1 
2 /*
3  * Copyright (C) 2024 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.nfc;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.when;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.BroadcastReceiver;
27 import android.content.ComponentName;
28 import android.content.Context;
29 import android.content.ContextWrapper;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageManager;
34 import android.content.pm.ResolveInfo;
35 import android.content.pm.ServiceInfo;
36 import android.content.res.Resources;
37 import android.content.res.TypedArray;
38 import android.content.res.XmlResourceParser;
39 import android.nfc.cardemulation.HostNfcFService;
40 import android.nfc.cardemulation.NfcFServiceInfo;
41 import android.os.Build;
42 import android.os.Handler;
43 import android.os.Looper;
44 import android.os.Message;
45 import android.os.UserHandle;
46 import android.os.test.TestLooper;
47 import android.util.AttributeSet;
48 import android.util.Log;
49 import android.util.Xml;
50 
51 import androidx.test.ext.junit.runners.AndroidJUnit4;
52 import androidx.test.platform.app.InstrumentationRegistry;
53 
54 import com.android.dx.mockito.inline.extended.ExtendedMockito;
55 import com.android.nfc.cardemulation.HostNfcFEmulationManager;
56 import com.android.nfc.cardemulation.RegisteredNfcFServicesCache;
57 
58 import org.junit.After;
59 import org.junit.Assert;
60 import org.junit.Before;
61 import org.junit.Test;
62 import org.junit.runner.RunWith;
63 import org.mockito.MockitoSession;
64 import org.mockito.quality.Strictness;
65 import org.xmlpull.v1.XmlPullParser;
66 import org.xmlpull.v1.XmlPullParserException;
67 
68 import java.io.IOException;
69 import java.util.ArrayList;
70 import java.util.List;
71 
72 @RunWith(AndroidJUnit4.class)
73 public class RegisteredNfcFServicesCacheTest {
74 
75     private static String TAG = RegisteredNfcFServicesCacheTest.class.getSimpleName();
76     private boolean mNfcSupported;
77     private MockitoSession mStaticMockSession;
78     private RegisteredNfcFServicesCache mNfcFServicesCache;
79     private int mUserId = -1;
80     private RegisteredNfcFServicesCache.Callback mCallback;
81 
82     @Before
setUp()83     public void setUp() throws Exception {
84 
85         mStaticMockSession = ExtendedMockito.mockitoSession()
86                 .mockStatic(Xml.class)
87                 .mockStatic(NfcStatsLog.class)
88                 .strictness(Strictness.LENIENT)
89                 .startMocking();
90 
91         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
92         PackageManager pm = context.getPackageManager();
93         if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
94             mNfcSupported = false;
95             return;
96         }
97         mNfcSupported = true;
98 
99         Context mockContext = new ContextWrapper(context) {
100             public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
101                     @NonNull IntentFilter filter, @Nullable String broadcastPermission,
102                     @Nullable Handler scheduler) {
103                 return mock(Intent.class);
104             }
105 
106             public Context createPackageContextAsUser(
107                     @NonNull String packageName, @CreatePackageOptions int flags,
108                     @NonNull UserHandle user)
109                     throws PackageManager.NameNotFoundException {
110                 Log.d(TAG, "createPackageContextAsUser called");
111                 return this;
112             }
113 
114             public PackageManager getPackageManager() {
115                 PackageManager packageManager = mock(PackageManager.class);
116                 ResolveInfo resolveInfo = mock(ResolveInfo.class);
117                 ServiceInfo serviceInfo = mock(ServiceInfo.class);
118                 serviceInfo.name = "nfc";
119                 serviceInfo.packageName = "com.android.nfc";
120                 serviceInfo.permission = android.Manifest.permission.BIND_NFC_SERVICE;
121                 ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
122                 serviceInfo.applicationInfo = applicationInfo;
123                 Resources resources = mock(Resources.class);
124 
125                 try {
126                     when(packageManager.getResourcesForApplication(applicationInfo)).thenReturn(
127                             resources);
128                 } catch (PackageManager.NameNotFoundException e) {
129                     throw new RuntimeException(e);
130                 }
131                 XmlResourceParser parser = mock(XmlResourceParser.class);
132                 AttributeSet attributeSet = mock(AttributeSet.class);
133                 TypedArray typedArray = mock(TypedArray.class);
134                 when(typedArray.getString(
135                         com.android.internal.R.styleable.HostNfcFService_description)).thenReturn(
136                         "nfc");
137                 when(resources.obtainAttributes(attributeSet,
138                         com.android.internal.R.styleable.HostNfcFService)).thenReturn(typedArray);
139                 when(Xml.asAttributeSet(parser)).thenReturn(attributeSet);
140                 try {
141                     when(parser.getEventType()).thenReturn(XmlPullParser.START_TAG,
142                             XmlPullParser.END_TAG);
143                     when(parser.next()).thenReturn(1, 0);
144                     when(parser.getName()).thenReturn("host-nfcf-service");
145                 } catch (XmlPullParserException e) {
146                 } catch (IOException e) {
147                     throw new RuntimeException(e);
148                 }
149                 when(serviceInfo.loadXmlMetaData(packageManager,
150                         HostNfcFService.SERVICE_META_DATA)).thenReturn(parser);
151                 serviceInfo.loadXmlMetaData(packageManager, HostNfcFService.SERVICE_META_DATA);
152                 resolveInfo.serviceInfo = serviceInfo;
153                 List<ResolveInfo> list = new ArrayList<>();
154                 list.add(resolveInfo);
155                 when(packageManager.queryIntentServicesAsUser(any(),
156                         any(),
157                         any())).thenReturn(list);
158                 return packageManager;
159             }
160 
161         };
162 
163         mCallback = mock(RegisteredNfcFServicesCache.Callback.class);
164 
165         InstrumentationRegistry.getInstrumentation().runOnMainSync(
166                 () -> mNfcFServicesCache =
167                         new RegisteredNfcFServicesCache(mockContext, mCallback));
168         Assert.assertNotNull(mNfcFServicesCache);
169 
170     }
171 
172     @After
tearDown()173     public void tearDown() throws Exception {
174         mStaticMockSession.finishMocking();
175     }
176 
177     @Test
testOnHostEmulationActivated()178     public void testOnHostEmulationActivated() {
179         if (!mNfcSupported) return;
180 
181         boolean isActive = mNfcFServicesCache.isActivated();
182         Assert.assertFalse(isActive);
183         mNfcFServicesCache.onHostEmulationActivated();
184         isActive = mNfcFServicesCache.isActivated();
185         Assert.assertTrue(isActive);
186     }
187 
188     @Test
testOnHostEmulationDeactivated()189     public void testOnHostEmulationDeactivated() {
190         if (!mNfcSupported) return;
191 
192         mNfcFServicesCache.onHostEmulationActivated();
193         boolean isActive = mNfcFServicesCache.isActivated();
194         Assert.assertTrue(isActive);
195         mNfcFServicesCache.onHostEmulationDeactivated();
196         isActive = mNfcFServicesCache.isActivated();
197         Assert.assertFalse(isActive);
198     }
199 
200     @Test
testOnNfcDisabled()201     public void testOnNfcDisabled() {
202         if (!mNfcSupported) return;
203 
204         mNfcFServicesCache.onHostEmulationActivated();
205         boolean isActive = mNfcFServicesCache.isActivated();
206         Assert.assertTrue(isActive);
207         mNfcFServicesCache.onNfcDisabled();
208         isActive = mNfcFServicesCache.isActivated();
209         Assert.assertFalse(isActive);
210     }
211 
212     @Test
testInvalidateCache()213     public void testInvalidateCache() {
214         if (!mNfcSupported) return;
215 
216         Assert.assertEquals(-1, mUserId);
217         mNfcFServicesCache.invalidateCache(1);
218         List<NfcFServiceInfo> services = mNfcFServicesCache.getServices(1);
219         Assert.assertNotNull(services);
220         Assert.assertTrue(services.size() > 0);
221         ExtendedMockito.verify(mCallback).onNfcFServicesUpdated(1, services);
222         NfcFServiceInfo nfcFServiceInfo = services.get(0);
223         ComponentName cName = nfcFServiceInfo.getComponent();
224         Assert.assertNotNull(cName);
225         Assert.assertEquals("com.android.nfc", cName.getPackageName());
226     }
227 
228     @Test
testGetServices()229     public void testGetServices() {
230         if (!mNfcSupported) return;
231 
232         mNfcFServicesCache.invalidateCache(1);
233         List<NfcFServiceInfo> services = mNfcFServicesCache.getServices(1);
234         Assert.assertNotNull(services);
235         Assert.assertTrue(services.size() > 0);
236         NfcFServiceInfo nfcFServiceInfo = services.get(0);
237         ComponentName cName = nfcFServiceInfo.getComponent();
238         Assert.assertNotNull(cName);
239         Assert.assertEquals("com.android.nfc", cName.getPackageName());
240     }
241 }