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.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertNull;
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.anyBoolean;
23 import static org.mockito.ArgumentMatchers.anyInt;
24 import static org.mockito.ArgumentMatchers.argThat;
25 import static org.mockito.ArgumentMatchers.eq;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyNoMoreInteractions;
29 import static org.mockito.Mockito.when;
30 
31 import android.app.ActivityManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.content.pm.PackageManager;
38 import android.content.pm.ResolveInfo;
39 import android.content.pm.ServiceInfo;
40 import android.nfc.NfcAdapter;
41 import android.nfc.cardemulation.AidGroup;
42 import android.nfc.cardemulation.ApduServiceInfo;
43 import android.nfc.cardemulation.CardEmulation;
44 import android.nfc.cardemulation.HostApduService;
45 import android.nfc.cardemulation.OffHostApduService;
46 import android.os.UserHandle;
47 import android.os.UserManager;
48 import android.util.Pair;
49 
50 import androidx.test.ext.junit.runners.AndroidJUnit4;
51 import androidx.test.platform.app.InstrumentationRegistry;
52 
53 import com.android.dx.mockito.inline.extended.ExtendedMockito;
54 import com.android.nfc.NfcService;
55 import com.android.nfc.NfcStatsLog;
56 
57 import org.junit.After;
58 import org.junit.Assert;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 import org.mockito.Captor;
64 import org.mockito.Mock;
65 import org.mockito.Mockito;
66 import org.mockito.MockitoAnnotations;
67 import org.mockito.MockitoSession;
68 import org.mockito.quality.Strictness;
69 import org.xmlpull.v1.XmlPullParserException;
70 
71 import java.io.ByteArrayInputStream;
72 import java.io.ByteArrayOutputStream;
73 import java.io.File;
74 import java.io.FileNotFoundException;
75 import java.io.FileOutputStream;
76 import java.io.IOException;
77 import java.io.InputStream;
78 import java.util.ArrayList;
79 import java.util.HashMap;
80 import java.util.List;
81 import java.util.Map;
82 
83 @RunWith(AndroidJUnit4.class)
84 public class RegisteredServicesCacheTest {
85 
86     private static final int USER_ID = 1;
87     private static final int SERVICE_UID = 1;
88     private static final int SYSTEM_ID = 0;
89     private static final UserHandle USER_HANDLE = UserHandle.of(USER_ID);
90     private static final UserHandle SYSTEM_USER_HANDLE = UserHandle.of(SYSTEM_ID);
91     private static final UserHandle USER_HANDLE_QUITE_MODE = UserHandle.of(2);
92     private static final File DIR = new File("/");
93     private static final String MOCK_FILE_PATH = "mockfile";
94     private static final String MOCK_FILE_EXT = ".xml";
95     private static final String ANOTHER_PACKAGE_NAME = "com.android.test.another";
96     private static final String NON_PAYMENT_NFC_PACKAGE_NAME = "com.android.test.nonpaymentnfc";
97     private static final String WALLET_HOLDER_PACKAGE_NAME = "com.android.test.walletroleholder";
98     private static final String ON_HOST_SERVICE_NAME
99             = "com.android.test.walletroleholder.OnHostApduService";
100     private static final String OFF_HOST_SERVICE_NAME
101             = "com.android.test.another.OffHostApduService";
102     private static final String NON_PAYMENT_SERVICE_NAME
103             = "com.android.test.nonpaymentnfc.NonPaymentApduService";
104     private static final ComponentName WALLET_HOLDER_SERVICE_COMPONENT =
105             new ComponentName(WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME);
106     private static final ComponentName NON_PAYMENT_SERVICE_COMPONENT =
107             new ComponentName(NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME);
108     private static final ComponentName ANOTHER_SERVICE_COMPONENT =
109             new ComponentName(ANOTHER_PACKAGE_NAME, OFF_HOST_SERVICE_NAME);
110     private static final String OFFHOST_SE_STRING = "offhostse";
111     private static final String TRUE_STRING = "true";
112     private static final String FALSE_STRING = "false";
113     private static final String ANDROID_STRING = "android";
114     private static final List<String> PAYMENT_AIDS = List.of("A000000004101011",
115             "A000000004101012", "A000000004101013");
116     private static final List<String> NON_PAYMENT_AID = List.of("F053414950454D");
117 
118     @Mock
119     private Context mContext;
120     @Mock
121     private RegisteredServicesCache.Callback mCallback;
122     @Mock
123     private UserManager mUserManager;
124     @Mock
125     private PackageManager mPackageManager;
126     @Mock
127     private RegisteredServicesCache.SettingsFile mDynamicSettingsFile;
128     @Mock
129     private RegisteredServicesCache.SettingsFile mOtherSettingsFile;
130     @Mock
131     private RegisteredServicesCache.ServiceParser mServiceParser;
132     @Mock
133     private RoutingOptionManager mRoutingOptionManager;
134     @Captor
135     private ArgumentCaptor<BroadcastReceiver> mReceiverArgumentCaptor;
136     @Captor
137     private ArgumentCaptor<IntentFilter> mIntentFilterArgumentCaptor;
138     @Captor
139     private ArgumentCaptor<PackageManager.ResolveInfoFlags> mFlagArgumentCaptor;
140     @Captor
141     private ArgumentCaptor<Intent> mIntentArgumentCaptor;
142     @Captor
143     private ArgumentCaptor<List<ApduServiceInfo>> mApduServiceListCaptor;
144     @Captor
145     private ArgumentCaptor<FileOutputStream> fileOutputStreamArgumentCaptor;
146 
147     private MockitoSession mStaticMockSession;
148     private RegisteredServicesCache mRegisteredServicesCache;
149     private Map<String, ApduServiceInfo> mMappedServices;
150     private File mMockFile = new File(MOCK_FILE_PATH + MOCK_FILE_EXT);
151 
152     @Before
setUp()153     public void setUp() throws PackageManager.NameNotFoundException, XmlPullParserException,
154             IOException {
155         mStaticMockSession = ExtendedMockito.mockitoSession()
156                 .mockStatic(com.android.nfc.flags.Flags.class)
157                 .mockStatic(ActivityManager.class)
158                 .mockStatic(NfcStatsLog.class)
159                 .mockStatic(UserHandle.class)
160                 .mockStatic(NfcAdapter.class)
161                 .mockStatic(NfcService.class)
162                 .strictness(Strictness.LENIENT)
163                 .startMocking();
164         MockitoAnnotations.initMocks(this);
165         if (!mMockFile.exists()) {
166             mMockFile = File.createTempFile(MOCK_FILE_PATH, MOCK_FILE_EXT);
167         }
168         mMappedServices = new HashMap<>();
169         when(UserHandle.getUserHandleForUid(eq(USER_ID))).thenReturn(USER_HANDLE);
170         when(UserHandle.getUserHandleForUid(eq(SYSTEM_ID))).thenReturn(SYSTEM_USER_HANDLE);
171         when(UserHandle.of(eq(SYSTEM_ID))).thenReturn(SYSTEM_USER_HANDLE);
172         when(UserHandle.of(eq(USER_ID))).thenReturn(USER_HANDLE);
173         when(ActivityManager.getCurrentUser()).thenReturn(USER_ID);
174         when(mContext.getSystemService(eq(UserManager.class))).thenReturn(mUserManager);
175         when(mContext.getFilesDir()).thenReturn(DIR);
176         when(mContext.createContextAsUser(
177                 any(), anyInt())).thenReturn(mContext);
178         when(mContext.createPackageContextAsUser(
179                 any(), anyInt(), any())).thenReturn(mContext);
180         when(mContext.getPackageManager()).thenReturn(mPackageManager);
181         List<UserHandle> enabledProfiles = new ArrayList<>();
182         enabledProfiles.add(USER_HANDLE);
183         enabledProfiles.add(USER_HANDLE_QUITE_MODE);
184         when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
185         when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE))).thenReturn(false);
186         when(mUserManager.isQuietModeEnabled(eq(USER_HANDLE_QUITE_MODE))).thenReturn(true);
187         when(mUserManager.getProfileParent(eq(USER_HANDLE))).thenReturn(USER_HANDLE);
188         List<ResolveInfo> onHostServicesList = new ArrayList<>();
189         onHostServicesList.add(createServiceResolveInfo(true,
190                 WALLET_HOLDER_PACKAGE_NAME, ON_HOST_SERVICE_NAME,
191                 List.of(CardEmulation.CATEGORY_PAYMENT)));
192         onHostServicesList.add(createServiceResolveInfo(false,
193                 NON_PAYMENT_NFC_PACKAGE_NAME, NON_PAYMENT_SERVICE_NAME,
194                 List.of(CardEmulation.CATEGORY_OTHER)));
195         List<ResolveInfo> offHostServicesList = new ArrayList<>();
196         offHostServicesList.add(createServiceResolveInfo(true, ANOTHER_PACKAGE_NAME,
197                 OFF_HOST_SERVICE_NAME, List.of(CardEmulation.CATEGORY_OTHER)));
198         when(mPackageManager.queryIntentServicesAsUser(
199                 any(), any(), any())).thenAnswer(invocation -> {
200                     Intent intent = invocation.getArgument(0);
201                     if(intent.getAction().equals(OffHostApduService.SERVICE_INTERFACE)) {
202                         return offHostServicesList;
203                     }
204                     if(intent.getAction().equals(HostApduService.SERVICE_INTERFACE)) {
205                         return onHostServicesList;
206                     }
207                     return List.of();
208                 });
209         when(mServiceParser.parseApduService(any(), any(), anyBoolean()))
210                 .thenAnswer(invocation -> {
211                     ResolveInfo resolveInfo = invocation.getArgument(1);
212                     if(mMappedServices.containsKey(resolveInfo.serviceInfo.name)) {
213                         return mMappedServices.get(resolveInfo.serviceInfo.name);
214                     }
215                     return null;
216                 });
217     }
218 
219     @After
tearDown()220     public void tearDown() {
221         mStaticMockSession.finishMocking();
222         if (mMockFile.exists()) {
223             mMockFile.delete();
224         }
225     }
226 
227     // Intent filter registration is actually not happening. It's just a mock verification.
228     @SuppressWarnings({"UnspecifiedRegisterReceiverFlag", "GuardedBy"})
229     @Test
testConstructor()230     public void testConstructor() {
231         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
232                 mRoutingOptionManager);
233 
234         // Verify that the users handles are populated correctly
235         Assert.assertEquals(1, mRegisteredServicesCache.mUserHandles.size());
236         Assert.assertEquals(mRegisteredServicesCache.mUserHandles.get(0), USER_HANDLE);
237         // Verify that broadcast receivers for apk changes are created and registered properly
238         assertNotNull(mRegisteredServicesCache.mReceiver.get());
239         verify(mContext).createContextAsUser(eq(USER_HANDLE), eq(0));
240         verify(mContext, times(2)).registerReceiverForAllUsers(
241                 mReceiverArgumentCaptor.capture(), mIntentFilterArgumentCaptor.capture(),
242                 eq(null), eq(null));
243         IntentFilter packageInstallTrackerIntent = mIntentFilterArgumentCaptor
244                 .getAllValues().get(0);
245         Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_ADDED));
246         Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_CHANGED));
247         Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_REMOVED));
248         Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_REPLACED));
249         Assert.assertTrue(packageInstallTrackerIntent
250                 .hasAction(Intent.ACTION_PACKAGE_FIRST_LAUNCH));
251         Assert.assertTrue(packageInstallTrackerIntent.hasAction(Intent.ACTION_PACKAGE_RESTARTED));
252         Assert.assertTrue(packageInstallTrackerIntent
253                 .hasDataScheme(RegisteredServicesCache.PACKAGE_DATA));
254         IntentFilter sdCardIntentFilter = mIntentFilterArgumentCaptor.getAllValues().get(1);
255         Assert.assertTrue(sdCardIntentFilter
256                 .hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE));
257         Assert.assertTrue(sdCardIntentFilter
258                 .hasAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE));
259         Assert.assertEquals(mRegisteredServicesCache.mReceiver.get(),
260                 mReceiverArgumentCaptor.getAllValues().get(0));
261         Assert.assertEquals(mRegisteredServicesCache.mReceiver.get(),
262                 mReceiverArgumentCaptor.getAllValues().get(1));
263         verify(mContext, times(2)).getFilesDir();
264         // Verify that correct file setting directories are set
265         Assert.assertEquals(mRegisteredServicesCache.mDynamicSettingsFile.getBaseFile()
266                         .getParentFile(), DIR);
267         Assert.assertEquals(mRegisteredServicesCache.mDynamicSettingsFile.getBaseFile()
268                         .getAbsolutePath(), DIR + RegisteredServicesCache.AID_XML_PATH);
269         Assert.assertEquals(mRegisteredServicesCache.mOthersFile.getBaseFile().getParentFile(),
270                 DIR);
271         Assert.assertEquals(mRegisteredServicesCache.mOthersFile.getBaseFile()
272                 .getAbsolutePath(), DIR + RegisteredServicesCache.OTHER_STATUS_PATH);
273     }
274 
275     @Test
testInitialize_filesExist()276     public void testInitialize_filesExist() throws IOException,
277             PackageManager.NameNotFoundException {
278         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
279                 .getContext().getResources().getAssets()
280                 .open(RegisteredServicesCache.AID_XML_PATH);
281         InputStream otherSettingsIs = InstrumentationRegistry.getInstrumentation()
282                 .getContext().getResources().getAssets()
283                 .open(RegisteredServicesCache.OTHER_STATUS_PATH);
284         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
285         when(mOtherSettingsFile.openRead()).thenReturn(otherSettingsIs);
286         when(mDynamicSettingsFile.exists()).thenReturn(true);
287         when(mOtherSettingsFile.exists()).thenReturn(true);
288         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
289                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
290 
291         mRegisteredServicesCache.initialize();
292 
293         // Verify that file operations are called
294         verify(mDynamicSettingsFile).exists();
295         verify(mOtherSettingsFile).exists();
296         verify(mDynamicSettingsFile).openRead();
297         verify(mOtherSettingsFile).openRead();
298         verifyNoMoreInteractions(mDynamicSettingsFile);
299         verifyNoMoreInteractions(mOtherSettingsFile);
300         // Verify that user services are read properly
301         Assert.assertEquals(1, mRegisteredServicesCache.mUserServices.size());
302         RegisteredServicesCache.UserServices userServices
303                 = mRegisteredServicesCache.mUserServices.get(USER_ID);
304         Assert.assertEquals(2, userServices.services.size());
305         Assert.assertTrue(userServices.services.containsKey(WALLET_HOLDER_SERVICE_COMPONENT));
306         Assert.assertTrue(userServices.services.containsKey(ANOTHER_SERVICE_COMPONENT));
307         Assert.assertEquals(3, userServices.dynamicSettings.size());
308         // Verify that dynamic settings are read properly
309         Assert.assertTrue(userServices.dynamicSettings
310                 .containsKey(WALLET_HOLDER_SERVICE_COMPONENT));
311         Assert.assertTrue(userServices.dynamicSettings.containsKey(NON_PAYMENT_SERVICE_COMPONENT));
312         // Verify that dynamic settings are properly populated for each service in the xml
313         // Verify the details of service 1
314         RegisteredServicesCache.DynamicSettings walletHolderSettings =
315                 userServices.dynamicSettings.get(WALLET_HOLDER_SERVICE_COMPONENT);
316         Assert.assertEquals(OFFHOST_SE_STRING+"1", walletHolderSettings.offHostSE);
317         Assert.assertEquals(1, walletHolderSettings.uid);
318         Assert.assertEquals(TRUE_STRING, walletHolderSettings.shouldDefaultToObserveModeStr);
319         Assert.assertTrue(walletHolderSettings.aidGroups
320                 .containsKey(CardEmulation.CATEGORY_PAYMENT));
321         Assert.assertTrue(walletHolderSettings.aidGroups.get(CardEmulation.CATEGORY_PAYMENT)
322                         .getAids().containsAll(PAYMENT_AIDS));
323         Assert.assertFalse(walletHolderSettings.aidGroups
324                 .containsKey(CardEmulation.CATEGORY_OTHER));
325         // Verify the details of service 2
326         RegisteredServicesCache.DynamicSettings nonPaymentSettings =
327                 userServices.dynamicSettings.get(NON_PAYMENT_SERVICE_COMPONENT);
328         Assert.assertEquals(OFFHOST_SE_STRING+"2", nonPaymentSettings.offHostSE);
329         Assert.assertEquals(1, nonPaymentSettings.uid);
330         Assert.assertEquals(FALSE_STRING, nonPaymentSettings.shouldDefaultToObserveModeStr);
331         Assert.assertTrue(nonPaymentSettings.aidGroups
332                 .containsKey(CardEmulation.CATEGORY_OTHER));
333         Assert.assertTrue(nonPaymentSettings.aidGroups.get(CardEmulation.CATEGORY_OTHER)
334                 .getAids().containsAll(NON_PAYMENT_AID));
335         // Verify that other settings are read properly
336         Assert.assertEquals(1, userServices.others.size());
337         Assert.assertTrue(userServices.others.containsKey(ANOTHER_SERVICE_COMPONENT));
338         RegisteredServicesCache.OtherServiceStatus otherServiceStatus
339                 = userServices.others.get(ANOTHER_SERVICE_COMPONENT);
340         Assert.assertTrue(otherServiceStatus.checked);
341         Assert.assertEquals(1, otherServiceStatus.uid);
342         // Verify that the installed services are populated properly
343         verify(mContext)
344                 .createPackageContextAsUser(eq(ANDROID_STRING), eq(0), eq(USER_HANDLE));
345         verify(mContext).getPackageManager();
346         verify(mPackageManager, times(2))
347                 .queryIntentServicesAsUser(mIntentArgumentCaptor.capture(),
348                         mFlagArgumentCaptor.capture(), eq(USER_HANDLE));
349         Intent onHostIntent = mIntentArgumentCaptor.getAllValues().get(0);
350         Assert.assertEquals(HostApduService.SERVICE_INTERFACE, onHostIntent.getAction());
351         Intent offHostIntent = mIntentArgumentCaptor.getAllValues().get(1);
352         Assert.assertEquals(OffHostApduService.SERVICE_INTERFACE, offHostIntent.getAction());
353         PackageManager.ResolveInfoFlags onHostFlag = mFlagArgumentCaptor.getAllValues().get(0);
354         Assert.assertEquals(PackageManager.GET_META_DATA, onHostFlag.getValue());
355         PackageManager.ResolveInfoFlags offHostFlag = mFlagArgumentCaptor.getAllValues().get(1);
356         Assert.assertEquals(PackageManager.GET_META_DATA, offHostFlag.getValue());
357         // Verify that the installed services are filtered properly
358         verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
359                 eq(WALLET_HOLDER_PACKAGE_NAME));
360         verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
361                 eq(NON_PAYMENT_NFC_PACKAGE_NAME));
362         verify(mPackageManager).checkPermission(eq(android.Manifest.permission.NFC),
363                 eq(ANOTHER_PACKAGE_NAME));
364         // Verify that the callback is called with properly installed and filtered services.
365         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
366                 eq(false));
367         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
368         Assert.assertEquals(2, apduServiceInfos.size());
369         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(0)
370                 .getComponent());
371         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(1).getComponent());
372     }
373 
374     @Test
testInitialize_filesDontExist()375     public void testInitialize_filesDontExist() throws IOException {
376         MockFileOutputStream mockFileOutputStream = new MockFileOutputStream();
377         when(mDynamicSettingsFile.exists()).thenReturn(false);
378         when(mOtherSettingsFile.exists()).thenReturn(false);
379         when(mOtherSettingsFile.startWrite()).thenReturn(mockFileOutputStream);
380 
381         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
382                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
383         mRegisteredServicesCache.initialize();
384 
385         // Verify that files are NOT read
386         verify(mDynamicSettingsFile).exists();
387         verifyNoMoreInteractions(mDynamicSettingsFile);
388         verify(mOtherSettingsFile).exists();
389         verify(mOtherSettingsFile).startWrite();
390         verify(mOtherSettingsFile).finishWrite(fileOutputStreamArgumentCaptor.capture());
391         verifyNoMoreInteractions(mOtherSettingsFile);
392         // Validate that no dynamic settings are read
393         Assert.assertEquals(1, mRegisteredServicesCache.mUserServices.size());
394         RegisteredServicesCache.UserServices userServices
395                 = mRegisteredServicesCache.mUserServices.get(USER_ID);
396         Assert.assertTrue(userServices.dynamicSettings.isEmpty());
397         // Verify that other settings are only read from system services
398         Assert.assertEquals(1, userServices.others.size());
399         Assert.assertTrue(userServices.others.containsKey(ANOTHER_SERVICE_COMPONENT));
400         RegisteredServicesCache.OtherServiceStatus otherServiceStatus
401                 = userServices.others.get(ANOTHER_SERVICE_COMPONENT);
402         Assert.assertTrue(otherServiceStatus.checked);
403         Assert.assertEquals(USER_ID, otherServiceStatus.uid);
404         // Verify that the callback is called with properly installed and filtered services.
405         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
406                 eq(false));
407         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
408         Assert.assertEquals(2, apduServiceInfos.size());
409         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(0)
410                 .getComponent());
411         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(1).getComponent());
412         // Validate that other setting file is written properly with a setting
413         // that previously did not exists.
414         InputStream otherSettingsIs = mockFileOutputStream.toInputStream();
415         RegisteredServicesCache.SettingsFile otherSettingsFile
416                 = Mockito.mock(RegisteredServicesCache.SettingsFile.class);
417         when(otherSettingsFile.openRead()).thenReturn(otherSettingsIs);
418         when(otherSettingsFile.exists()).thenReturn(true);
419         Map<Integer, List<Pair<ComponentName, RegisteredServicesCache.OtherServiceStatus>>>
420                 readOtherSettingsFromFile = RegisteredServicesCache
421                 .readOtherFromFile(otherSettingsFile);
422         Assert.assertEquals(mockFileOutputStream, fileOutputStreamArgumentCaptor.getValue());
423         Assert.assertTrue(readOtherSettingsFromFile.containsKey(USER_ID));
424         Assert.assertFalse(readOtherSettingsFromFile.get(USER_ID).isEmpty());
425         Assert.assertEquals(readOtherSettingsFromFile.get(USER_ID).get(0).first,
426                 ANOTHER_SERVICE_COMPONENT);
427         Assert.assertTrue(readOtherSettingsFromFile.get(USER_ID).get(0).second.checked);
428     }
429 
430     @SuppressWarnings("GuardedBy")
431     @Test
testOnUserSwitched()432     public void testOnUserSwitched() {
433         List<UserHandle> enabledProfiles = new ArrayList<>();
434         // Do not trigger user switch in constructor. Send empty list.
435         when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
436         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
437                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
438         enabledProfiles.add(USER_HANDLE);
439         enabledProfiles.add(USER_HANDLE_QUITE_MODE);
440         when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
441 
442         mRegisteredServicesCache.onUserSwitched();
443 
444         // Validate that quite mode profiles get filtered out.
445         Assert.assertEquals(1, mRegisteredServicesCache.mUserHandles.size());
446         Assert.assertEquals(mRegisteredServicesCache.mUserHandles.get(0), USER_HANDLE);
447     }
448 
449     @SuppressWarnings("GuardedBy")
450     @Test
testOnManagedProfileChanged()451     public void testOnManagedProfileChanged() {
452         List<UserHandle> enabledProfiles = new ArrayList<>();
453         // Do not trigger user switch in constructor. Send empty list.
454         when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
455         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
456                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
457         enabledProfiles.add(USER_HANDLE);
458         enabledProfiles.add(USER_HANDLE_QUITE_MODE);
459         when(mUserManager.getEnabledProfiles()).thenReturn(enabledProfiles);
460 
461         mRegisteredServicesCache.onManagedProfileChanged();
462 
463         // Validate that quite mode profiles get filtered out.
464         Assert.assertEquals(1, mRegisteredServicesCache.mUserHandles.size());
465         Assert.assertEquals(mRegisteredServicesCache.mUserHandles.get(0), USER_HANDLE);
466     }
467 
468     @Test
testHasService()469     public void testHasService() {
470         when(mDynamicSettingsFile.exists()).thenReturn(false);
471         when(mOtherSettingsFile.exists()).thenReturn(false);
472         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
473                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
474         mRegisteredServicesCache.initialize();
475 
476         Assert.assertTrue(mRegisteredServicesCache.hasService(USER_ID, ANOTHER_SERVICE_COMPONENT));
477         Assert.assertTrue(mRegisteredServicesCache.hasService(USER_ID,
478                 WALLET_HOLDER_SERVICE_COMPONENT));
479     }
480 
481     @Test
testGetService()482     public void testGetService() {
483         when(mDynamicSettingsFile.exists()).thenReturn(false);
484         when(mOtherSettingsFile.exists()).thenReturn(false);
485         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
486                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
487         mRegisteredServicesCache.initialize();
488 
489         ApduServiceInfo serviceInfo = mRegisteredServicesCache.getService(USER_ID,
490                 WALLET_HOLDER_SERVICE_COMPONENT);
491         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, serviceInfo.getComponent());
492         Assert.assertEquals(SERVICE_UID, serviceInfo.getUid());
493     }
494 
495     @Test
testGetServices()496     public void testGetServices() {
497         when(mDynamicSettingsFile.exists()).thenReturn(false);
498         when(mOtherSettingsFile.exists()).thenReturn(false);
499         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
500                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
501         mRegisteredServicesCache.initialize();
502 
503         List<ApduServiceInfo> serviceInfos = mRegisteredServicesCache.getServices(USER_ID);
504         Assert.assertFalse(serviceInfos.isEmpty());
505         Assert.assertEquals(2, serviceInfos.size());
506         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, serviceInfos.get(0).getComponent());
507         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, serviceInfos.get(1).getComponent());
508     }
509 
510     @Test
testGetServicesForCategory_paymentCategory()511     public void testGetServicesForCategory_paymentCategory() {
512         when(mDynamicSettingsFile.exists()).thenReturn(false);
513         when(mOtherSettingsFile.exists()).thenReturn(false);
514         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
515                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
516         mRegisteredServicesCache.initialize();
517 
518         List<ApduServiceInfo> serviceInfos = mRegisteredServicesCache
519                 .getServicesForCategory(USER_ID, CardEmulation.CATEGORY_PAYMENT);
520         Assert.assertFalse(serviceInfos.isEmpty());
521         Assert.assertEquals(1, serviceInfos.size());
522         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, serviceInfos.get(0).getComponent());
523     }
524 
525     @Test
testGetServicesForCategory_otherCategory()526     public void testGetServicesForCategory_otherCategory() {
527         when(mDynamicSettingsFile.exists()).thenReturn(false);
528         when(mOtherSettingsFile.exists()).thenReturn(false);
529         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
530                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
531         mRegisteredServicesCache.initialize();
532 
533         List<ApduServiceInfo> serviceInfos = mRegisteredServicesCache
534                 .getServicesForCategory(USER_ID, CardEmulation.CATEGORY_OTHER);
535         Assert.assertFalse(serviceInfos.isEmpty());
536         Assert.assertEquals(1, serviceInfos.size());
537         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, serviceInfos.get(0).getComponent());
538     }
539 
540     @Test
testSetOffhostSecureElement_nonExistingService()541     public void testSetOffhostSecureElement_nonExistingService() {
542         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
543                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
544         mRegisteredServicesCache.initialize();
545         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
546 
547         Assert.assertFalse(mRegisteredServicesCache.setOffHostSecureElement(USER_ID,
548                 SERVICE_UID, wrongComponentName, "offhostse1"));
549     }
550 
551     @Test
testSetOffhostSecureElement_wrongServiceUid()552     public void testSetOffhostSecureElement_wrongServiceUid() {
553         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
554                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
555         mRegisteredServicesCache.initialize();
556 
557         Assert.assertFalse(mRegisteredServicesCache.setOffHostSecureElement(USER_ID,
558                 3, WALLET_HOLDER_SERVICE_COMPONENT, "offhostse1"));
559     }
560 
561     @Test
testSetOffhostSecureElement_nullOffHostSet()562     public void testSetOffhostSecureElement_nullOffHostSet() {
563         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
564                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
565         mRegisteredServicesCache.initialize();
566 
567         Assert.assertFalse(mRegisteredServicesCache.setOffHostSecureElement(USER_ID,
568                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, null));
569     }
570 
571     @Test
testSetOffhostSecureElement_existingService_correctUid_nonNullSE()572     public void testSetOffhostSecureElement_existingService_correctUid_nonNullSE()
573             throws IOException {
574         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
575                 .getContext().getResources().getAssets()
576                 .open(RegisteredServicesCache.AID_XML_PATH);
577         MockFileOutputStream mockFileOutputStream = new MockFileOutputStream();
578         when(mDynamicSettingsFile.exists()).thenReturn(true);
579         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
580         when(mOtherSettingsFile.exists()).thenReturn(false);
581         when(mDynamicSettingsFile.startWrite()).thenReturn(mockFileOutputStream);
582         when(mOtherSettingsFile.startWrite()).thenReturn(new MockFileOutputStream());
583         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
584                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
585         mRegisteredServicesCache.initialize();
586         String newOffHostValue = "newOffhostValue";
587 
588         Assert.assertTrue(mRegisteredServicesCache.setOffHostSecureElement(USER_ID,
589                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, newOffHostValue));
590         verify(mDynamicSettingsFile).exists();
591         verify(mDynamicSettingsFile).openRead();
592         verify(mDynamicSettingsFile).startWrite();
593         verify(mDynamicSettingsFile).finishWrite(fileOutputStreamArgumentCaptor.capture());
594         verifyNoMoreInteractions(mDynamicSettingsFile);
595         Assert.assertEquals(mockFileOutputStream, fileOutputStreamArgumentCaptor.getValue());
596         // Verify that the callback is called with properly installed and filtered services.
597         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
598                 eq(true));
599         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
600         Assert.assertEquals(2, apduServiceInfos.size());
601         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
602                 .getComponent());
603         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
604                 .getComponent());
605         verify(apduServiceInfos.get(1)).setOffHostSecureElement(eq(newOffHostValue));
606         // Verify that dynamic settings file is updated
607         InputStream dynamicSettingsMockIs = mockFileOutputStream.toInputStream();
608         RegisteredServicesCache.SettingsFile dynamicSettingsFile
609                 = Mockito.mock(RegisteredServicesCache.SettingsFile.class);
610         when(dynamicSettingsFile.openRead()).thenReturn(dynamicSettingsMockIs);
611         when(dynamicSettingsFile.exists()).thenReturn(true);
612         Map<Integer, List<Pair<ComponentName, RegisteredServicesCache.DynamicSettings>>>
613                 readDynamicSettingsFromFile = RegisteredServicesCache
614                 .readDynamicSettingsFromFile(dynamicSettingsFile);
615         Assert.assertFalse(readDynamicSettingsFromFile.isEmpty());
616         Assert.assertTrue(readDynamicSettingsFromFile.containsKey(USER_ID));
617         RegisteredServicesCache.DynamicSettings dynamicSettings
618                 = readDynamicSettingsFromFile.get(USER_ID).get(1).second;
619         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT,
620                 readDynamicSettingsFromFile.get(USER_ID).get(1).first);
621         Assert.assertEquals(newOffHostValue, dynamicSettings.offHostSE);
622     }
623 
624     @Test
testResetOffhostSecureElement_nonExistingService()625     public void testResetOffhostSecureElement_nonExistingService() {
626         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
627                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
628         mRegisteredServicesCache.initialize();
629         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
630 
631         Assert.assertFalse(mRegisteredServicesCache.resetOffHostSecureElement(USER_ID,
632                 SERVICE_UID, wrongComponentName));
633     }
634 
635     @Test
testResetOffhostSecureElement_wrongServiceUid()636     public void testResetOffhostSecureElement_wrongServiceUid() {
637         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
638                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
639         mRegisteredServicesCache.initialize();
640 
641         Assert.assertFalse(mRegisteredServicesCache.resetOffHostSecureElement(USER_ID,
642                 3, WALLET_HOLDER_SERVICE_COMPONENT));
643     }
644 
645     @Test
testResetOffhostSecureElement_nullOffHostSet()646     public void testResetOffhostSecureElement_nullOffHostSet() {
647         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
648                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
649         mRegisteredServicesCache.initialize();
650 
651         Assert.assertFalse(mRegisteredServicesCache.resetOffHostSecureElement(USER_ID,
652                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT));
653     }
654 
655     @Test
testResetOffhostSecureElement_existingService_correctUid()656     public void testResetOffhostSecureElement_existingService_correctUid()
657             throws IOException {
658         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
659                 .getContext().getResources().getAssets()
660                 .open(RegisteredServicesCache.AID_XML_PATH);
661         MockFileOutputStream mockFileOutputStream = new MockFileOutputStream();
662         when(mDynamicSettingsFile.exists()).thenReturn(true);
663         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
664         when(mOtherSettingsFile.exists()).thenReturn(false);
665         when(mDynamicSettingsFile.startWrite()).thenReturn(mockFileOutputStream);
666         when(mOtherSettingsFile.startWrite()).thenReturn(new MockFileOutputStream());
667         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
668                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
669         mRegisteredServicesCache.initialize();
670         when(mRegisteredServicesCache.getService(USER_ID, ANOTHER_SERVICE_COMPONENT)
671                 .getOffHostSecureElement()).thenReturn("offhost");
672 
673         Assert.assertTrue(mRegisteredServicesCache.resetOffHostSecureElement(USER_ID,
674                 SERVICE_UID, ANOTHER_SERVICE_COMPONENT));
675         verify(mDynamicSettingsFile).exists();
676         verify(mDynamicSettingsFile).openRead();
677         verify(mDynamicSettingsFile).startWrite();
678         verify(mDynamicSettingsFile).finishWrite(fileOutputStreamArgumentCaptor.capture());
679         verifyNoMoreInteractions(mDynamicSettingsFile);
680         Assert.assertEquals(mockFileOutputStream, fileOutputStreamArgumentCaptor.getValue());
681         // Verify that the callback is called with properly installed and filtered services.
682         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
683                 eq(true));
684         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
685         Assert.assertEquals(2, apduServiceInfos.size());
686         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
687                 .getComponent());
688         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
689                 .getComponent());
690         verify(apduServiceInfos.get(0)).resetOffHostSecureElement();
691         // Verify that dynamic settings file is updated
692         InputStream dynamicSettingsMockIs = mockFileOutputStream.toInputStream();
693         RegisteredServicesCache.SettingsFile dynamicSettingsFile
694                 = Mockito.mock(RegisteredServicesCache.SettingsFile.class);
695         when(dynamicSettingsFile.openRead()).thenReturn(dynamicSettingsMockIs);
696         when(dynamicSettingsFile.exists()).thenReturn(true);
697         Map<Integer, List<Pair<ComponentName, RegisteredServicesCache.DynamicSettings>>>
698                 readDynamicSettingsFromFile = RegisteredServicesCache
699                 .readDynamicSettingsFromFile(dynamicSettingsFile);
700         Assert.assertFalse(readDynamicSettingsFromFile.isEmpty());
701         Assert.assertTrue(readDynamicSettingsFromFile.containsKey(USER_ID));
702         RegisteredServicesCache.DynamicSettings dynamicSettings
703                 = readDynamicSettingsFromFile.get(USER_ID).get(0).second;
704         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT,
705                 readDynamicSettingsFromFile.get(USER_ID).get(0).first);
706         assertNull(dynamicSettings.offHostSE);
707     }
708 
709     @Test
testSetShouldDefaultToObserveModeForService_nonExistingService()710     public void testSetShouldDefaultToObserveModeForService_nonExistingService() {
711         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
712                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
713         mRegisteredServicesCache.initialize();
714         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
715 
716         Assert.assertFalse(mRegisteredServicesCache.setShouldDefaultToObserveModeForService(USER_ID,
717                 SERVICE_UID, wrongComponentName, true));
718     }
719 
720     @Test
testSetShouldDefaultToObserveModeForService_wrongServiceUid()721     public void testSetShouldDefaultToObserveModeForService_wrongServiceUid() {
722         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
723                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
724         mRegisteredServicesCache.initialize();
725 
726         Assert.assertFalse(mRegisteredServicesCache.setShouldDefaultToObserveModeForService(USER_ID,
727                 3, WALLET_HOLDER_SERVICE_COMPONENT, true));
728     }
729 
730     @Test
testSetShouldDefaultToObserveModeForService_existingService_correctUid()731     public void testSetShouldDefaultToObserveModeForService_existingService_correctUid()
732             throws IOException {
733         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
734                 .getContext().getResources().getAssets()
735                 .open(RegisteredServicesCache.AID_XML_PATH);
736         when(mDynamicSettingsFile.exists()).thenReturn(true);
737         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
738         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
739                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
740         mRegisteredServicesCache.initialize();
741 
742         Assert.assertTrue(mRegisteredServicesCache.setShouldDefaultToObserveModeForService(USER_ID,
743                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, true));
744         Assert.assertEquals("true", mRegisteredServicesCache.mUserServices.get(USER_ID)
745                 .dynamicSettings.get(WALLET_HOLDER_SERVICE_COMPONENT)
746                 .shouldDefaultToObserveModeStr);
747         verify(mRegisteredServicesCache.getService(USER_ID, WALLET_HOLDER_SERVICE_COMPONENT),
748                 times(2)).setShouldDefaultToObserveMode(eq(true));
749     }
750 
751     @Test
testRegisterPollingLoopFilterForService_nonExistingService()752     public void testRegisterPollingLoopFilterForService_nonExistingService() {
753         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
754                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
755         mRegisteredServicesCache.initialize();
756         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
757 
758         Assert.assertFalse(mRegisteredServicesCache.registerPollingLoopFilterForService(USER_ID,
759                 SERVICE_UID, wrongComponentName, "empty", true));
760     }
761 
762     @Test
testRegisterPollingLoopFilterForService_wrongUid()763     public void testRegisterPollingLoopFilterForService_wrongUid() {
764         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
765                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
766         mRegisteredServicesCache.initialize();
767 
768         Assert.assertFalse(mRegisteredServicesCache.registerPollingLoopFilterForService(USER_ID,
769                 3, WALLET_HOLDER_SERVICE_COMPONENT, "empty", true));
770     }
771 
772     @Test
testRegisterPollingLoopFilterForService_existingService_correctUid()773     public void testRegisterPollingLoopFilterForService_existingService_correctUid() {
774         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
775                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
776         mRegisteredServicesCache.initialize();
777         String plFilter = "afilter";
778 
779         Assert.assertTrue(mRegisteredServicesCache.registerPollingLoopFilterForService(USER_ID,
780                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, plFilter, true));
781         // Verify that the callback is called with properly installed and filtered services.
782         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
783                 eq(true));
784         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
785         Assert.assertEquals(2, apduServiceInfos.size());
786         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
787                 .getComponent());
788         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
789                 .getComponent());
790         verify(apduServiceInfos.get(1)).addPollingLoopFilter(eq(plFilter), eq(true));
791     }
792 
793     @Test
testRemovePollingLoopFilterForService_nonExistingService()794     public void testRemovePollingLoopFilterForService_nonExistingService() {
795         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
796                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
797         mRegisteredServicesCache.initialize();
798         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
799 
800         Assert.assertFalse(mRegisteredServicesCache.removePollingLoopFilterForService(USER_ID,
801                 SERVICE_UID, wrongComponentName, "empty"));
802     }
803 
804     @Test
testRemovePollingLoopFilterForService_wrongUid()805     public void testRemovePollingLoopFilterForService_wrongUid() {
806         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
807                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
808         mRegisteredServicesCache.initialize();
809 
810         Assert.assertFalse(mRegisteredServicesCache.removePollingLoopFilterForService(USER_ID,
811                 3, WALLET_HOLDER_SERVICE_COMPONENT, "empty"));
812     }
813 
814     @Test
testRemovePollingLoopFilterForService_existingService_correctUid()815     public void testRemovePollingLoopFilterForService_existingService_correctUid() {
816         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
817                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
818         mRegisteredServicesCache.initialize();
819         String plFilter = "afilter";
820 
821         Assert.assertTrue(mRegisteredServicesCache.removePollingLoopFilterForService(USER_ID,
822                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, plFilter));
823         // Verify that the callback is called with properly installed and filtered services.
824         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
825                 eq(true));
826         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
827         Assert.assertEquals(2, apduServiceInfos.size());
828         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
829                 .getComponent());
830         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
831                 .getComponent());
832         verify(apduServiceInfos.get(1)).removePollingLoopFilter(eq(plFilter));
833     }
834 
835     @Test
testRegisterPollingLoopPatternFilterForService_nonExistingService()836     public void testRegisterPollingLoopPatternFilterForService_nonExistingService() {
837         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
838                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
839         mRegisteredServicesCache.initialize();
840         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
841 
842         Assert.assertFalse(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(
843                 USER_ID, SERVICE_UID, wrongComponentName, "empty",
844                 true));
845     }
846 
847     @Test
testRegisterPollingLoopPatterFilterForService_wrongUid()848     public void testRegisterPollingLoopPatterFilterForService_wrongUid() {
849         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
850                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
851         mRegisteredServicesCache.initialize();
852 
853         Assert.assertFalse(mRegisteredServicesCache
854                 .registerPollingLoopPatternFilterForService(USER_ID, 3,
855                         WALLET_HOLDER_SERVICE_COMPONENT, "empty", true));
856     }
857 
858     @Test
testRegisterPollingLoopPatternFilterForService_existingService_correctUid()859     public void testRegisterPollingLoopPatternFilterForService_existingService_correctUid() {
860         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
861                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
862         mRegisteredServicesCache.initialize();
863         String plFilter = "afilter";
864 
865         Assert.assertTrue(mRegisteredServicesCache.registerPollingLoopPatternFilterForService(
866                 USER_ID, SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, plFilter, true));
867         // Verify that the callback is called with properly installed and filtered services.
868         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
869                 eq(true));
870         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
871         Assert.assertEquals(2, apduServiceInfos.size());
872         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
873                 .getComponent());
874         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
875                 .getComponent());
876         verify(apduServiceInfos.get(1)).addPollingLoopPatternFilter(eq(plFilter), eq(true));
877     }
878 
879     @Test
testRemovePollingLoopPatternFilterForService_nonExistingService()880     public void testRemovePollingLoopPatternFilterForService_nonExistingService() {
881         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
882                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
883         mRegisteredServicesCache.initialize();
884         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
885 
886         Assert.assertFalse(mRegisteredServicesCache.removePollingLoopPatternFilterForService(
887                 USER_ID, SERVICE_UID, wrongComponentName, "empty"));
888     }
889 
890     @Test
testRemovePollingLoopPatternFilterForService_wrongUid()891     public void testRemovePollingLoopPatternFilterForService_wrongUid() {
892         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
893                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
894         mRegisteredServicesCache.initialize();
895 
896         Assert.assertFalse(mRegisteredServicesCache.removePollingLoopFilterForService(USER_ID,
897                 3, WALLET_HOLDER_SERVICE_COMPONENT, "empty"));
898     }
899 
900     @Test
testRemovePollingLoopPatternFilterForService_existingService_correctUid()901     public void testRemovePollingLoopPatternFilterForService_existingService_correctUid() {
902         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
903                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
904         mRegisteredServicesCache.initialize();
905         String plFilter = "afilter";
906 
907         Assert.assertTrue(mRegisteredServicesCache.removePollingLoopPatternFilterForService(USER_ID,
908                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, plFilter));
909         // Verify that the callback is called with properly installed and filtered services.
910         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
911                 eq(true));
912         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
913         Assert.assertEquals(2, apduServiceInfos.size());
914         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
915                 .getComponent());
916         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
917                 .getComponent());
918         verify(apduServiceInfos.get(1)).removePollingLoopPatternFilter(eq(plFilter));
919     }
920 
921     @Test
testRegisterAidGroupForService_nonExistingService()922     public void testRegisterAidGroupForService_nonExistingService() {
923         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
924                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
925         mRegisteredServicesCache.initialize();
926         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
927         AidGroup aidGroup = createAidGroup(CardEmulation.CATEGORY_PAYMENT);
928 
929         Assert.assertFalse(mRegisteredServicesCache.registerAidGroupForService(
930                 USER_ID, SERVICE_UID, wrongComponentName, aidGroup));
931     }
932 
933     @Test
testRegisterAidGroupForService_wrongUid()934     public void testRegisterAidGroupForService_wrongUid() {
935         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
936                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
937         mRegisteredServicesCache.initialize();
938         AidGroup aidGroup = createAidGroup(CardEmulation.CATEGORY_PAYMENT);
939 
940         Assert.assertFalse(mRegisteredServicesCache.registerAidGroupForService(USER_ID,
941                 3, WALLET_HOLDER_SERVICE_COMPONENT, aidGroup));
942     }
943 
944     @Test
testRegisterAidGroupForService_existingService_correctUid()945     public void testRegisterAidGroupForService_existingService_correctUid() throws IOException {
946         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
947                 .getContext().getResources().getAssets()
948                 .open(RegisteredServicesCache.AID_XML_PATH);
949         MockFileOutputStream mockFileOutputStream = new MockFileOutputStream();
950         when(mDynamicSettingsFile.exists()).thenReturn(true);
951         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
952         when(mOtherSettingsFile.exists()).thenReturn(false);
953         when(mDynamicSettingsFile.startWrite()).thenReturn(mockFileOutputStream);
954         when(mOtherSettingsFile.startWrite()).thenReturn(new MockFileOutputStream());
955         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
956                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
957         mRegisteredServicesCache.initialize();
958         AidGroup aidGroup = createAidGroup(CardEmulation.CATEGORY_OTHER);
959 
960         Assert.assertTrue(mRegisteredServicesCache.registerAidGroupForService(USER_ID,
961                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, aidGroup));
962 
963         ApduServiceInfo serviceInfo = mRegisteredServicesCache.getService(USER_ID,
964                 WALLET_HOLDER_SERVICE_COMPONENT);
965         verify(serviceInfo).setDynamicAidGroup(eq(aidGroup));
966         Assert.assertEquals(aidGroup, mRegisteredServicesCache.mUserServices.get(USER_ID)
967                 .dynamicSettings.get(WALLET_HOLDER_SERVICE_COMPONENT)
968                 .aidGroups.get(CardEmulation.CATEGORY_OTHER));
969         // Verify that the callback is called with properly installed and filtered services.
970         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
971                 eq(true));
972         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
973         Assert.assertEquals(2, apduServiceInfos.size());
974         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
975                 .getComponent());
976         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
977                 .getComponent());
978         // Verify that dynamic settings file is updated
979         InputStream dynamicSettingsMockIs = mockFileOutputStream.toInputStream();
980         RegisteredServicesCache.SettingsFile dynamicSettingsFile
981                 = Mockito.mock(RegisteredServicesCache.SettingsFile.class);
982         when(dynamicSettingsFile.openRead()).thenReturn(dynamicSettingsMockIs);
983         when(dynamicSettingsFile.exists()).thenReturn(true);
984         Map<Integer, List<Pair<ComponentName, RegisteredServicesCache.DynamicSettings>>>
985                 readDynamicSettingsFromFile = RegisteredServicesCache
986                 .readDynamicSettingsFromFile(dynamicSettingsFile);
987         Assert.assertFalse(readDynamicSettingsFromFile.isEmpty());
988         Assert.assertTrue(readDynamicSettingsFromFile.containsKey(USER_ID));
989         RegisteredServicesCache.DynamicSettings dynamicSettings
990                 = readDynamicSettingsFromFile.get(USER_ID).get(0).second;
991         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT,
992                 readDynamicSettingsFromFile.get(USER_ID).get(0).first);
993         Assert.assertTrue(dynamicSettings.aidGroups.containsKey(CardEmulation.CATEGORY_OTHER));
994         Assert.assertEquals(aidGroup.getAids(),
995                 dynamicSettings.aidGroups.get(CardEmulation.CATEGORY_OTHER).getAids());
996     }
997 
998     @Test
testGetAidGroupForService_nonExistingService()999     public void testGetAidGroupForService_nonExistingService() {
1000         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1001                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1002         mRegisteredServicesCache.initialize();
1003         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
1004 
1005         assertNull(mRegisteredServicesCache.getAidGroupForService(
1006                 USER_ID, SERVICE_UID, wrongComponentName, CardEmulation.CATEGORY_PAYMENT));
1007     }
1008 
1009     @Test
testGetAidGroupForService_wrongUid()1010     public void testGetAidGroupForService_wrongUid() {
1011         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1012                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1013         mRegisteredServicesCache.initialize();
1014 
1015         assertNull(mRegisteredServicesCache.getAidGroupForService(USER_ID,
1016                 3, WALLET_HOLDER_SERVICE_COMPONENT, CardEmulation.CATEGORY_PAYMENT));
1017     }
1018 
1019     @Test
testGetAidGroupForService_existingService_correctUid()1020     public void testGetAidGroupForService_existingService_correctUid() {
1021         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1022                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1023         mRegisteredServicesCache.initialize();
1024         ApduServiceInfo serviceInfo = mRegisteredServicesCache.getService(USER_ID,
1025                 WALLET_HOLDER_SERVICE_COMPONENT);
1026         AidGroup aidGroup = createAidGroup(CardEmulation.CATEGORY_OTHER);
1027         when(serviceInfo.getDynamicAidGroupForCategory(eq(CardEmulation.CATEGORY_OTHER)))
1028                 .thenReturn(aidGroup);
1029 
1030         AidGroup aidGroupReceived = mRegisteredServicesCache.getAidGroupForService(USER_ID,
1031                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, CardEmulation.CATEGORY_OTHER);
1032         Assert.assertEquals(aidGroup, aidGroupReceived);
1033     }
1034 
1035     @Test
testRemoveAidGroupForService_nonExistingService()1036     public void testRemoveAidGroupForService_nonExistingService() {
1037         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1038                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1039         mRegisteredServicesCache.initialize();
1040         ComponentName wrongComponentName = new ComponentName("test","com.wrong.class");
1041 
1042         Assert.assertFalse(mRegisteredServicesCache.removeAidGroupForService(
1043                 USER_ID, SERVICE_UID, wrongComponentName, CardEmulation.CATEGORY_PAYMENT));
1044     }
1045 
1046     @Test
testRemoveAidGroupForService_wrongUid()1047     public void testRemoveAidGroupForService_wrongUid() {
1048         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1049                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1050         mRegisteredServicesCache.initialize();
1051 
1052         Assert.assertFalse(mRegisteredServicesCache.removeAidGroupForService(USER_ID,
1053                 3, WALLET_HOLDER_SERVICE_COMPONENT, CardEmulation.CATEGORY_PAYMENT));
1054     }
1055 
1056     @Test
testRemoveAidGroupForService_existingService_correctUid()1057     public void testRemoveAidGroupForService_existingService_correctUid() throws IOException {
1058         InputStream dynamicSettingsIs = InstrumentationRegistry.getInstrumentation()
1059                 .getContext().getResources().getAssets()
1060                 .open(RegisteredServicesCache.AID_XML_PATH);
1061         MockFileOutputStream mockFileOutputStream = new MockFileOutputStream();
1062         when(mDynamicSettingsFile.exists()).thenReturn(true);
1063         when(mDynamicSettingsFile.openRead()).thenReturn(dynamicSettingsIs);
1064         when(mOtherSettingsFile.exists()).thenReturn(false);
1065         when(mDynamicSettingsFile.startWrite()).thenReturn(mockFileOutputStream);
1066         when(mOtherSettingsFile.startWrite()).thenReturn(new MockFileOutputStream());
1067         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1068                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1069         mRegisteredServicesCache.initialize();
1070         ApduServiceInfo serviceInfo = mRegisteredServicesCache.getService(USER_ID,
1071                 WALLET_HOLDER_SERVICE_COMPONENT);
1072         when(serviceInfo.removeDynamicAidGroupForCategory(eq(CardEmulation.CATEGORY_PAYMENT)))
1073                 .thenReturn(true);
1074 
1075         Assert.assertTrue(mRegisteredServicesCache.removeAidGroupForService(USER_ID,
1076                 SERVICE_UID, WALLET_HOLDER_SERVICE_COMPONENT, CardEmulation.CATEGORY_PAYMENT));
1077 
1078         verify(serviceInfo).removeDynamicAidGroupForCategory(eq(CardEmulation.CATEGORY_PAYMENT));
1079         Assert.assertFalse(mRegisteredServicesCache.mUserServices.get(USER_ID)
1080                 .dynamicSettings.get(WALLET_HOLDER_SERVICE_COMPONENT)
1081                 .aidGroups.containsKey(CardEmulation.CATEGORY_PAYMENT));
1082         // Verify that the callback is called with properly installed and filtered services.
1083         verify(mCallback).onServicesUpdated(eq(USER_ID), mApduServiceListCaptor.capture(),
1084                 eq(true));
1085         List<ApduServiceInfo> apduServiceInfos = mApduServiceListCaptor.getValue();
1086         Assert.assertEquals(2, apduServiceInfos.size());
1087         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT, apduServiceInfos.get(0)
1088                 .getComponent());
1089         Assert.assertEquals(WALLET_HOLDER_SERVICE_COMPONENT, apduServiceInfos.get(1)
1090                 .getComponent());
1091         // Verify that dynamic settings file is updated
1092         InputStream dynamicSettingsMockIs = mockFileOutputStream.toInputStream();
1093         RegisteredServicesCache.SettingsFile dynamicSettingsFile
1094                 = Mockito.mock(RegisteredServicesCache.SettingsFile.class);
1095         when(dynamicSettingsFile.openRead()).thenReturn(dynamicSettingsMockIs);
1096         when(dynamicSettingsFile.exists()).thenReturn(true);
1097         Map<Integer, List<Pair<ComponentName, RegisteredServicesCache.DynamicSettings>>>
1098                 readDynamicSettingsFromFile = RegisteredServicesCache
1099                 .readDynamicSettingsFromFile(dynamicSettingsFile);
1100         Assert.assertFalse(readDynamicSettingsFromFile.isEmpty());
1101         Assert.assertTrue(readDynamicSettingsFromFile.containsKey(USER_ID));
1102         RegisteredServicesCache.DynamicSettings dynamicSettings
1103                 = readDynamicSettingsFromFile.get(USER_ID).get(0).second;
1104         Assert.assertEquals(ANOTHER_SERVICE_COMPONENT,
1105                 readDynamicSettingsFromFile.get(USER_ID).get(0).first);
1106         Assert.assertFalse(dynamicSettings.aidGroups.containsKey(CardEmulation.CATEGORY_PAYMENT));
1107     }
1108     @Test
testHandlePackageRemoved()1109     public void testHandlePackageRemoved() {
1110         mRegisteredServicesCache = new RegisteredServicesCache(mContext, mCallback,
1111                 mDynamicSettingsFile, mOtherSettingsFile, mServiceParser, mRoutingOptionManager);
1112         verify(mContext).registerReceiverForAllUsers(
1113                 mReceiverArgumentCaptor.capture(),
1114                 argThat(intent -> intent.hasAction(Intent.ACTION_PACKAGE_ADDED)),
1115                 eq(null), eq(null));
1116         mRegisteredServicesCache.initialize();
1117         assertNotNull(mRegisteredServicesCache.getService(USER_ID, WALLET_HOLDER_SERVICE_COMPONENT));
1118 
1119         Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
1120         intent.putExtra(Intent.EXTRA_UID, USER_ID);
1121         // return empty list
1122         Mockito.reset(mPackageManager);
1123         when(mPackageManager.queryIntentServicesAsUser(any(), any(), any())).thenReturn(List.of());
1124         mReceiverArgumentCaptor.getValue().onReceive(mContext, intent);
1125         assertNull(mRegisteredServicesCache.getService(USER_ID, WALLET_HOLDER_SERVICE_COMPONENT));
1126     }
1127 
1128     private class MockFileOutputStream extends FileOutputStream {
1129 
1130         ByteArrayOutputStream mByteOutputStream;
1131 
MockFileOutputStream()1132         public MockFileOutputStream() throws FileNotFoundException {
1133             // Does not actually matter what the path is since we won't be writing to it.
1134             super(mMockFile);
1135             mByteOutputStream = new ByteArrayOutputStream();
1136         }
1137 
1138         @Override
write(byte[] b, int off, int len)1139         public void write(byte[] b, int off, int len) throws IOException {
1140             mByteOutputStream.write(b, off, len);
1141         }
1142 
1143         @Override
write(byte[] b)1144         public void write(byte[] b) throws IOException {
1145             mByteOutputStream.write(b);
1146         }
1147 
1148         @Override
write(int b)1149         public void write(int b) throws IOException {
1150             mByteOutputStream.write(b);
1151         }
1152 
1153         @Override
close()1154         public void close() throws IOException {
1155             mByteOutputStream.close();
1156         }
1157 
1158         @Override
flush()1159         public void flush() throws IOException {
1160             mByteOutputStream.flush();
1161         }
1162 
toInputStream()1163         InputStream toInputStream() {
1164             return new ByteArrayInputStream(mByteOutputStream.toByteArray());
1165         }
1166     }
1167 
createServiceResolveInfo(boolean hasPermission, String packageName, String className, List<String> categories)1168     private ResolveInfo createServiceResolveInfo(boolean hasPermission,
1169                                                  String packageName, String className,
1170                                                  List<String> categories) {
1171         when(mPackageManager.checkPermission(any(), eq(packageName)))
1172                 .thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
1173                         : PackageManager.PERMISSION_DENIED);
1174         ApduServiceInfo apduServiceInfo = Mockito.mock(ApduServiceInfo.class);
1175         when(apduServiceInfo.getComponent()).thenReturn(new ComponentName(packageName, className));
1176         when(apduServiceInfo.getUid()).thenReturn(SERVICE_UID);
1177         if (!categories.isEmpty()) {
1178             for(String category : categories) {
1179                when(apduServiceInfo.hasCategory(category)).thenReturn(true);
1180                when(apduServiceInfo.getDynamicAidGroupForCategory(eq(category)))
1181                        .thenReturn(createAidGroup(category));
1182             }
1183         }
1184         mMappedServices.put(className, apduServiceInfo);
1185 
1186         ResolveInfo resolveInfo = new ResolveInfo();
1187         resolveInfo.serviceInfo = new ServiceInfo();
1188         resolveInfo.serviceInfo.permission = android.Manifest.permission.BIND_NFC_SERVICE;
1189         resolveInfo.serviceInfo.exported = true;
1190         resolveInfo.serviceInfo.packageName = packageName;
1191         resolveInfo.serviceInfo.name = className;
1192         return resolveInfo;
1193     }
1194 
createAidGroup(String category)1195     private static AidGroup createAidGroup(String category) {
1196         return new AidGroup(category.equals(
1197                 CardEmulation.CATEGORY_PAYMENT) ? PAYMENT_AIDS : NON_PAYMENT_AID, category);
1198     }
1199 }
1200