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;
18 
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.times;
23 import static org.mockito.Mockito.when;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.annotation.RequiresPermission;
28 import android.app.ActivityManager;
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.ContentResolver;
32 import android.content.Context;
33 import android.content.ContextWrapper;
34 import android.content.Intent;
35 import android.content.IntentFilter;
36 import android.content.ServiceConnection;
37 import android.content.pm.PackageManager;
38 import android.nfc.Constants;
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.IBinder;
44 import android.os.Message;
45 import android.os.Messenger;
46 import android.os.UserHandle;
47 import android.provider.Settings;
48 import android.util.Log;
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.cardemulation.AidRoutingManager;
55 import com.android.nfc.cardemulation.CardEmulationManager;
56 import com.android.nfc.cardemulation.HostNfcFEmulationManager;
57 import com.android.nfc.cardemulation.RegisteredAidCache;
58 import com.android.nfc.cardemulation.RegisteredT3tIdentifiersCache;
59 import com.android.nfc.cardemulation.RoutingOptionManager;
60 
61 import org.junit.After;
62 import org.junit.Assert;
63 import org.junit.Before;
64 import org.junit.Test;
65 import org.junit.runner.RunWith;
66 import org.mockito.MockitoSession;
67 import org.mockito.quality.Strictness;
68 
69 import com.android.nfc.flags.Flags;
70 
71 @RunWith(AndroidJUnit4.class)
72 public class HostNfcFEmulationManagerTest {
73 
74     private static final String TAG = HostNfcFEmulationManagerTest.class.getSimpleName();
75     private boolean mNfcSupported;
76     private MockitoSession mStaticMockSession;
77     private HostNfcFEmulationManager mHostNfcFEmulationManager;
78     private ComponentName componentName;
79 
80     @Before
setUp()81     public void setUp() throws Exception {
82         mStaticMockSession = ExtendedMockito.mockitoSession()
83                 .mockStatic(Flags.class)
84                 .mockStatic(NfcStatsLog.class)
85                 .mockStatic(Message.class)
86                 .strictness(Strictness.LENIENT)
87                 .startMocking();
88 
89         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
90         PackageManager pm = context.getPackageManager();
91         if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
92             mNfcSupported = false;
93             return;
94         }
95         mNfcSupported = true;
96 
97         Context mockContext = new ContextWrapper(context) {
98 
99             public Context createContextAsUser(@NonNull UserHandle user,
100                     @CreatePackageOptions int flags) {
101                 if (Build.IS_ENG) {
102                     throw new IllegalStateException("createContextAsUser not overridden!");
103                 }
104                 return this;
105             }
106 
107             public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
108                     @NonNull IntentFilter filter, @Nullable String broadcastPermission,
109                     @Nullable Handler scheduler) {
110                 return mock(Intent.class);
111             }
112 
113             public ContentResolver getContentResolver() {
114                 return mock(ContentResolver.class);
115             }
116 
117             public boolean bindServiceAsUser(
118                     @NonNull @RequiresPermission Intent service, @NonNull ServiceConnection conn,
119                     int flags,
120                     @NonNull UserHandle user) {
121                 return true;
122             }
123 
124             public void unbindService(@NonNull ServiceConnection conn) {
125 
126             }
127 
128 
129         };
130 
131         when(Flags.statsdCeEventsFlag()).thenReturn(false);
132         RegisteredT3tIdentifiersCache t3tIdentifiersCache = mock(
133                 RegisteredT3tIdentifiersCache.class);
134         NfcFServiceInfo nfcFServiceInfo = mock(NfcFServiceInfo.class);
135         componentName = mock(ComponentName.class);
136         when(nfcFServiceInfo.getComponent()).thenReturn(componentName);
137         when(t3tIdentifiersCache.resolveNfcid2("6D2E616E64726F69")).thenReturn(nfcFServiceInfo);
138         Message message = mock(Message.class);
139         when(Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET)).thenReturn(message);
140         when(Message.obtain(null, HostNfcFService.MSG_DEACTIVATED)).thenReturn(message);
141         when(Message.obtain()).thenReturn(message);
142 
143         InstrumentationRegistry.getInstrumentation().runOnMainSync(
144                 () -> mHostNfcFEmulationManager =
145                         new HostNfcFEmulationManager(mockContext, t3tIdentifiersCache));
146         Assert.assertNotNull(mHostNfcFEmulationManager);
147     }
148 
149     @After
tearDown()150     public void tearDown() throws Exception {
151         mStaticMockSession.finishMocking();
152     }
153 
154     @Test
testOnEnabledForegroundNfcFServiceChanged()155     public void testOnEnabledForegroundNfcFServiceChanged() {
156         if (!mNfcSupported) return;
157 
158         String packageName = mHostNfcFEmulationManager.getEnabledFgServiceName();
159         Assert.assertNull(packageName);
160         when(componentName.getPackageName()).thenReturn("com.android.nfc");
161         mHostNfcFEmulationManager.onEnabledForegroundNfcFServiceChanged(0, componentName);
162         packageName = mHostNfcFEmulationManager.getEnabledFgServiceName();
163         Assert.assertNotNull(packageName);
164         Assert.assertEquals("com.android.nfc", packageName);
165 
166     }
167 
168     @Test
testOnHostEmulationData()169     public void testOnHostEmulationData() {
170         if (!mNfcSupported) return;
171 
172         testOnEnabledForegroundNfcFServiceChanged();
173         mHostNfcFEmulationManager.onHostEmulationData("com.android.nfc".getBytes());
174         ExtendedMockito.verify(() -> NfcStatsLog.write(NfcStatsLog.NFC_CARDEMULATION_OCCURRED,
175                 NfcStatsLog.NFC_CARDEMULATION_OCCURRED__CATEGORY__HCE_PAYMENT,
176                 "HCEF",
177                 0));
178     }
179 
180     @Test
testOnNfcDisabled()181     public void testOnNfcDisabled() {
182         if (!mNfcSupported) return;
183 
184         testOnHostEmulationData();
185         ServiceConnection serviceConnection = mHostNfcFEmulationManager.getServiceConnection();
186         Message message = mock(Message.class);
187         when(Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET)).thenReturn(message);
188         serviceConnection.onServiceConnected(mock(ComponentName.class), mock(IBinder.class));
189         int userId = mHostNfcFEmulationManager.getServiceUserId();
190         Assert.assertEquals(0, userId);
191         mHostNfcFEmulationManager.onNfcDisabled();
192         userId = mHostNfcFEmulationManager.getServiceUserId();
193         Assert.assertEquals(-1, userId);
194         ExtendedMockito.verify(() -> Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET));
195     }
196 
197 
198     @Test
testOnUserSwitched()199     public void testOnUserSwitched() {
200         if (!mNfcSupported) return;
201 
202         testOnHostEmulationData();
203         ServiceConnection serviceConnection = mHostNfcFEmulationManager.getServiceConnection();
204         Message message = mock(Message.class);
205         when(Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET)).thenReturn(message);
206         serviceConnection.onServiceConnected(mock(ComponentName.class), mock(IBinder.class));
207         int userId = mHostNfcFEmulationManager.getServiceUserId();
208         Assert.assertEquals(0, userId);
209         mHostNfcFEmulationManager.onUserSwitched();
210         boolean isUserSwitched = mHostNfcFEmulationManager.isUserSwitched();
211         Assert.assertTrue(isUserSwitched);
212         userId = mHostNfcFEmulationManager.getServiceUserId();
213         Assert.assertEquals(-1, userId);
214         ExtendedMockito.verify(() -> Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET));
215     }
216 
217     @Test
testOnHostEmulationDeactivated()218     public void testOnHostEmulationDeactivated() {
219         if (!mNfcSupported) return;
220 
221         testOnHostEmulationData();
222         ServiceConnection serviceConnection = mHostNfcFEmulationManager.getServiceConnection();
223         Message message = mock(Message.class);
224         when(Message.obtain(null, HostNfcFService.MSG_COMMAND_PACKET)).thenReturn(message);
225         serviceConnection.onServiceConnected(mock(ComponentName.class), mock(IBinder.class));
226         ComponentName serviceName = mHostNfcFEmulationManager.getServiceName();
227         Assert.assertNotNull(serviceName);
228         mHostNfcFEmulationManager.onHostEmulationDeactivated();
229         serviceName = mHostNfcFEmulationManager.getServiceName();
230         Assert.assertNull(serviceName);
231     }
232 }
233 
234