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 package com.android.nfc;
17 
18 import static com.android.nfc.NfcService.PREF_NFC_ON;
19 
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyBoolean;
22 import static org.mockito.ArgumentMatchers.anyInt;
23 import static org.mockito.ArgumentMatchers.anyLong;
24 import static org.mockito.ArgumentMatchers.anyString;
25 import static org.mockito.ArgumentMatchers.argThat;
26 import static org.mockito.ArgumentMatchers.eq;
27 import static org.mockito.Mockito.atLeastOnce;
28 import static org.mockito.Mockito.clearInvocations;
29 import static org.mockito.Mockito.doNothing;
30 import static org.mockito.Mockito.mock;
31 import static org.mockito.Mockito.times;
32 import static org.mockito.Mockito.verify;
33 import static org.mockito.Mockito.verifyNoMoreInteractions;
34 import static org.mockito.Mockito.when;
35 
36 import android.app.ActivityManager;
37 import android.app.AlarmManager;
38 import android.app.Application;
39 import android.app.KeyguardManager;
40 import android.app.backup.BackupManager;
41 import android.content.BroadcastReceiver;
42 import android.content.ContentResolver;
43 import android.content.Intent;
44 import android.content.SharedPreferences;
45 import android.content.pm.PackageManager;
46 import android.content.res.Resources;
47 import android.nfc.NfcAdapter;
48 import android.nfc.NfcServiceManager;
49 import android.nfc.tech.Ndef;
50 import android.os.AsyncTask;
51 import android.os.Bundle;
52 import android.os.Handler;
53 import android.os.HandlerExecutor;
54 import android.os.PowerManager;
55 import android.os.UserManager;
56 import android.os.test.TestLooper;
57 
58 import androidx.test.ext.junit.runners.AndroidJUnit4;
59 
60 
61 import com.android.dx.mockito.inline.extended.ExtendedMockito;
62 
63 import org.junit.After;
64 import org.junit.Assert;
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.ArgumentCaptor;
69 import org.mockito.Captor;
70 import org.mockito.Mock;
71 import org.mockito.MockitoAnnotations;
72 import org.mockito.MockitoSession;
73 import org.mockito.quality.Strictness;
74 
75 import java.util.Optional;
76 
77 @RunWith(AndroidJUnit4.class)
78 public final class NfcServiceTest {
79     private static final String PKG_NAME = "com.test";
80     @Mock Application mApplication;
81     @Mock NfcInjector mNfcInjector;
82     @Mock DeviceHost mDeviceHost;
83     @Mock NfcEventLog mNfcEventLog;
84     @Mock NfcDispatcher mNfcDispatcher;
85     @Mock NfcUnlockManager mNfcUnlockManager;
86     @Mock SharedPreferences mPreferences;
87     @Mock SharedPreferences.Editor mPreferencesEditor;
88     @Mock PowerManager mPowerManager;
89     @Mock PackageManager mPackageManager;
90     @Mock ScreenStateHelper mScreenStateHelper;
91     @Mock Resources mResources;
92     @Mock KeyguardManager mKeyguardManager;
93     @Mock UserManager mUserManager;
94     @Mock ActivityManager mActivityManager;
95     @Mock NfcServiceManager.ServiceRegisterer mNfcManagerRegisterer;
96     @Mock NfcDiagnostics mNfcDiagnostics;
97     @Mock DeviceConfigFacade mDeviceConfigFacade;
98     @Mock ContentResolver mContentResolver;
99     @Mock Bundle mUserRestrictions;
100     @Mock BackupManager mBackupManager;
101     @Mock AlarmManager mAlarmManager;
102     @Captor ArgumentCaptor<DeviceHost.DeviceHostListener> mDeviceHostListener;
103     @Captor ArgumentCaptor<BroadcastReceiver> mGlobalReceiver;
104     @Captor ArgumentCaptor<AlarmManager.OnAlarmListener> mAlarmListener;
105     TestLooper mLooper;
106     NfcService mNfcService;
107     private MockitoSession mStaticMockSession;
108 
109     @Before
setUp()110     public void setUp() {
111         mLooper = new TestLooper();
112         MockitoAnnotations.initMocks(this);
113         AsyncTask.setDefaultExecutor(new HandlerExecutor(new Handler(mLooper.getLooper())));
114 
115         when(mNfcInjector.getMainLooper()).thenReturn(mLooper.getLooper());
116         when(mNfcInjector.getNfcEventLog()).thenReturn(mNfcEventLog);
117         when(mNfcInjector.makeDeviceHost(any())).thenReturn(mDeviceHost);
118         when(mNfcInjector.getScreenStateHelper()).thenReturn(mScreenStateHelper);
119         when(mNfcInjector.getNfcDiagnostics()).thenReturn(mNfcDiagnostics);
120         when(mNfcInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
121         when(mNfcInjector.getNfcManagerRegisterer()).thenReturn(mNfcManagerRegisterer);
122         when(mNfcInjector.getBackupManager()).thenReturn(mBackupManager);
123         when(mNfcInjector.getNfcDispatcher()).thenReturn(mNfcDispatcher);
124         when(mNfcInjector.getNfcUnlockManager()).thenReturn(mNfcUnlockManager);
125         when(mApplication.getSharedPreferences(anyString(), anyInt())).thenReturn(mPreferences);
126         when(mApplication.getSystemService(PowerManager.class)).thenReturn(mPowerManager);
127         when(mApplication.getSystemService(UserManager.class)).thenReturn(mUserManager);
128         when(mApplication.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
129         when(mApplication.getSystemService(KeyguardManager.class)).thenReturn(mKeyguardManager);
130         when(mApplication.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
131         when(mApplication.getPackageManager()).thenReturn(mPackageManager);
132         when(mApplication.getResources()).thenReturn(mResources);
133         when(mApplication.createContextAsUser(any(), anyInt())).thenReturn(mApplication);
134         when(mApplication.getContentResolver()).thenReturn(mContentResolver);
135         when(mUserManager.getUserRestrictions()).thenReturn(mUserRestrictions);
136         when(mResources.getStringArray(R.array.nfc_allow_list)).thenReturn(new String[0]);
137         when(mPreferences.edit()).thenReturn(mPreferencesEditor);
138         when(mPowerManager.newWakeLock(anyInt(), anyString()))
139                 .thenReturn(mock(PowerManager.WakeLock.class));
140         createNfcService();
141     }
142 
143     @After
tearDown()144     public void tearDown() {
145     }
146 
createNfcService()147     private void createNfcService() {
148         mNfcService = new NfcService(mApplication, mNfcInjector);
149         mLooper.dispatchAll();
150         verify(mNfcInjector).makeDeviceHost(mDeviceHostListener.capture());
151         verify(mApplication).registerReceiverForAllUsers(
152                 mGlobalReceiver.capture(),
153                 argThat(intent -> intent.hasAction(Intent.ACTION_SCREEN_ON)), any(), any());
154         clearInvocations(mDeviceHost, mNfcInjector, mApplication);
155     }
156 
enableAndVerify()157     private void enableAndVerify() throws Exception {
158         when(mDeviceHost.initialize()).thenReturn(true);
159         when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(true);
160         mNfcService.mNfcAdapter.enable(PKG_NAME);
161         verify(mPreferencesEditor).putBoolean(PREF_NFC_ON, true);
162         mLooper.dispatchAll();
163         verify(mDeviceHost).initialize();
164         clearInvocations(mDeviceHost, mPreferencesEditor);
165     }
166 
disableAndVerify()167     private void disableAndVerify() throws Exception {
168         when(mDeviceHost.deinitialize()).thenReturn(true);
169         when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(false);
170         mNfcService.mNfcAdapter.disable(true, PKG_NAME);
171         verify(mPreferencesEditor).putBoolean(PREF_NFC_ON, false);
172         mLooper.dispatchAll();
173         verify(mDeviceHost).deinitialize();
174         verify(mNfcDispatcher).resetForegroundDispatch();
175         clearInvocations(mDeviceHost, mPreferencesEditor, mNfcDispatcher);
176     }
177 
178 
179     @Test
testEnable()180     public void testEnable() throws Exception {
181         enableAndVerify();
182     }
183 
184     @Test
testDisable()185     public void testDisable() throws Exception {
186         enableAndVerify();
187         disableAndVerify();
188     }
189 
190     @Test
testBootupWithNfcOn()191     public void testBootupWithNfcOn() throws Exception {
192         when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(true);
193         mNfcService = new NfcService(mApplication, mNfcInjector);
194         mLooper.dispatchAll();
195         verify(mNfcInjector).makeDeviceHost(mDeviceHostListener.capture());
196         verify(mApplication).registerReceiverForAllUsers(
197                 mGlobalReceiver.capture(),
198                 argThat(intent -> intent.hasAction(Intent.ACTION_SCREEN_ON)), any(), any());
199         verify(mDeviceHost).initialize();
200     }
201 
202     @Test
testBootupWithNfcOn_WhenOemExtensionEnabled()203     public void testBootupWithNfcOn_WhenOemExtensionEnabled() throws Exception {
204         when(mResources.getBoolean(R.bool.enable_oem_extension)).thenReturn(true);
205         createNfcService();
206 
207         verifyNoMoreInteractions(mDeviceHost);
208     }
209 
210     @Test
testBootupWithNfcOn_WhenOemExtensionEnabled_ThenAllowBoot()211     public void testBootupWithNfcOn_WhenOemExtensionEnabled_ThenAllowBoot() throws Exception {
212         when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(true);
213         when(mResources.getBoolean(R.bool.enable_oem_extension)).thenReturn(true);
214         createNfcService();
215 
216         mNfcService.mNfcAdapter.allowBoot();
217         mLooper.dispatchAll();
218         verify(mDeviceHost).initialize();
219     }
220 
221     @Test
testBootupWithNfcOn_WhenOemExtensionEnabled_ThenTimeout()222     public void testBootupWithNfcOn_WhenOemExtensionEnabled_ThenTimeout() throws Exception {
223         when(mPreferences.getBoolean(eq(PREF_NFC_ON), anyBoolean())).thenReturn(true);
224         when(mResources.getBoolean(R.bool.enable_oem_extension)).thenReturn(true);
225         createNfcService();
226         verify(mAlarmManager).setExact(
227                 anyInt(), anyLong(), anyString(), mAlarmListener.capture(), any());
228 
229         mAlarmListener.getValue().onAlarm();
230         mLooper.dispatchAll();
231         verify(mDeviceHost).initialize();
232     }
233 
234     @Test
testSetObserveMode_nfcDisabled()235     public void testSetObserveMode_nfcDisabled() throws Exception {
236         mNfcService.mNfcAdapter.disable(true, PKG_NAME);
237 
238         Assert.assertFalse(mNfcService.mNfcAdapter.setObserveMode(true, null));
239     }
240 
241     @Test
testIsObserveModeEnabled_nfcDisabled()242     public void testIsObserveModeEnabled_nfcDisabled() throws Exception {
243         mNfcService.mNfcAdapter.disable(true, PKG_NAME);
244 
245         Assert.assertFalse(mNfcService.mNfcAdapter.isObserveModeEnabled());
246     }
247 
248     @Test
testIsObserveModeSupported_nfcDisabled()249     public void testIsObserveModeSupported_nfcDisabled() throws Exception {
250         mNfcService.mNfcAdapter.disable(true, PKG_NAME);
251 
252         Assert.assertFalse(mNfcService.mNfcAdapter.isObserveModeSupported());
253     }
254 
255     @Test
testEnableNfc_changeStateRestricted()256     public void testEnableNfc_changeStateRestricted() throws Exception {
257         when(mUserRestrictions.getBoolean(
258                 UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO)).thenReturn(true);
259         mNfcService.mNfcAdapter.enable(PKG_NAME);
260         assert(mNfcService.mState == NfcAdapter.STATE_OFF);
261     }
262 
263     @Test
testDisableNfc_changeStateRestricted()264     public void testDisableNfc_changeStateRestricted() throws Exception {
265         enableAndVerify();
266         when(mUserRestrictions.getBoolean(
267                 UserManager.DISALLOW_CHANGE_NEAR_FIELD_COMMUNICATION_RADIO)).thenReturn(true);
268         mNfcService.mNfcAdapter.disable(true, PKG_NAME);
269         assert(mNfcService.mState == NfcAdapter.STATE_ON);
270     }
271 }
272