1 /*
2  * Copyright (C) 2022 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.google.common.truth.Truth.assertThat;
19 
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.PackageManager;
25 import android.nfc.NfcAdapter;
26 import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
27 import android.util.Log;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.ext.junit.runners.AndroidJUnit4;
31 
32 import org.junit.After;
33 import org.junit.Assert;
34 import org.junit.Before;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 
38 import java.util.concurrent.Executor;
39 
40 @RunWith(AndroidJUnit4.class)
41 public final class NfcStateTest {
42 
43     private static final String TAG = NfcStateTest.class.getSimpleName();
44     private static final int MAX_TIMEOUT_MS = 20000;
45     private Context mContext;
46     private NfcAdapter mNfcAdapter;
47     private BroadcastReceiver mAdapterStateChangedReceiver;
48     private int mState;
49     private boolean mIsAlwaysOnEnabled;
50     private boolean mNfcSupported;
51     private ControllerAlwaysOnListener mListener;
52 
53     class SynchronousExecutor implements Executor {
execute(Runnable r)54         public void execute(Runnable r) {
55             r.run();
56         }
57     }
58 
59     @Before
setUp()60     public void setUp() {
61         mContext = InstrumentationRegistry.getTargetContext();
62         IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED);
63         mAdapterStateChangedReceiver = new AdapterStateChangedReceiver();
64         mContext.registerReceiver(mAdapterStateChangedReceiver, filter);
65         PackageManager pm = mContext.getPackageManager();
66         if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC_ANY)) {
67             mNfcSupported = false;
68             return;
69         }
70         mNfcSupported = true;
71         mNfcAdapter = NfcAdapter.getDefaultAdapter(mContext);
72         Assert.assertNotNull(mNfcAdapter);
73         if (mNfcAdapter.isEnabled()) {
74             mState = NfcAdapter.STATE_ON;
75         } else {
76             mState = NfcAdapter.STATE_OFF;
77         }
78         if (mNfcAdapter.isControllerAlwaysOnSupported()) {
79             mListener = new AlwaysOnStateListener();
80             mNfcAdapter.registerControllerAlwaysOnListener(new SynchronousExecutor(),
81                     mListener);
82             mIsAlwaysOnEnabled = mNfcAdapter.isControllerAlwaysOn();
83         }
84     }
85 
86     @After
tearDown()87     public void tearDown() throws Exception {
88         mContext.unregisterReceiver(mAdapterStateChangedReceiver);
89         if (mNfcSupported && mNfcAdapter.isControllerAlwaysOnSupported()) {
90             mNfcAdapter.unregisterControllerAlwaysOnListener(mListener);
91         }
92     }
93 
94     @Test
testSetControllerAlwaysOnTrueFromFalseWhenDisabled()95     public void testSetControllerAlwaysOnTrueFromFalseWhenDisabled() {
96         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
97         if (mNfcAdapter.isControllerAlwaysOn()) {
98             mNfcAdapter.setControllerAlwaysOn(false);
99             wait_for_always_on(false);
100             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
101         }
102         if (mNfcAdapter.isEnabled()) {
103             mNfcAdapter.disable();
104             wait_for_state(NfcAdapter.STATE_OFF);
105         }
106         mNfcAdapter.setControllerAlwaysOn(true);
107         wait_for_always_on(true);
108         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
109     }
110 
111     @Test
testSetControllerAlwaysOnFalseFromTrueWhenDisabled()112     public void testSetControllerAlwaysOnFalseFromTrueWhenDisabled() {
113         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
114         if (!mNfcAdapter.isControllerAlwaysOn()) {
115             mNfcAdapter.setControllerAlwaysOn(true);
116             wait_for_always_on(true);
117             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
118         }
119         if (mNfcAdapter.isEnabled()) {
120             mNfcAdapter.disable();
121             wait_for_state(NfcAdapter.STATE_OFF);
122         }
123         mNfcAdapter.setControllerAlwaysOn(false);
124         wait_for_always_on(false);
125         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
126     }
127 
128     @Test
testSetControllerAlwaysOnFalseFromFalseWhenDisabled()129     public void testSetControllerAlwaysOnFalseFromFalseWhenDisabled() {
130         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
131         if (!mNfcAdapter.isControllerAlwaysOn()) {
132             mNfcAdapter.setControllerAlwaysOn(false);
133             wait_for_always_on(false);
134             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
135         }
136         if (mNfcAdapter.isEnabled()) {
137             mNfcAdapter.disable();
138             wait_for_state(NfcAdapter.STATE_OFF);
139         }
140         mNfcAdapter.setControllerAlwaysOn(false);
141         wait_for_always_on(false);
142         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
143     }
144 
145     @Test
testSetControllerAlwaysOnTrueFromTrueWhenDisabled()146     public void testSetControllerAlwaysOnTrueFromTrueWhenDisabled() {
147         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
148         if (!mNfcAdapter.isControllerAlwaysOn()) {
149             mNfcAdapter.setControllerAlwaysOn(true);
150             wait_for_always_on(true);
151             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
152         }
153         if (mNfcAdapter.isEnabled()) {
154             mNfcAdapter.disable();
155             wait_for_state(NfcAdapter.STATE_OFF);
156         }
157         mNfcAdapter.setControllerAlwaysOn(true);
158         wait_for_always_on(true);
159         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
160     }
161 
162     @Test
testSetControllerAlwaysOnTrueFromFalseWhenEnabled()163     public void testSetControllerAlwaysOnTrueFromFalseWhenEnabled() {
164         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
165         if (mNfcAdapter.isControllerAlwaysOn()) {
166             mNfcAdapter.setControllerAlwaysOn(false);
167             wait_for_always_on(false);
168             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
169         }
170         if (!mNfcAdapter.isEnabled()) {
171             mNfcAdapter.enable();
172             wait_for_state(NfcAdapter.STATE_ON);
173         }
174         mNfcAdapter.setControllerAlwaysOn(true);
175         wait_for_always_on(true);
176         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
177     }
178 
179     @Test
testSetAlwaysOnFalseFromTrueWhenEnabled()180     public void testSetAlwaysOnFalseFromTrueWhenEnabled() {
181         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
182         if (!mNfcAdapter.isControllerAlwaysOn()) {
183             mNfcAdapter.setControllerAlwaysOn(true);
184             wait_for_always_on(true);
185             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
186         }
187         if (!mNfcAdapter.isEnabled()) {
188             mNfcAdapter.enable();
189             wait_for_state(NfcAdapter.STATE_ON);
190         }
191         mNfcAdapter.setControllerAlwaysOn(false);
192         wait_for_always_on(false);
193         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
194     }
195 
196     @Test
testSetControllerAlwaysOnFalseFromFalseWhenEnabled()197     public void testSetControllerAlwaysOnFalseFromFalseWhenEnabled() {
198         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
199         if (!mNfcAdapter.isControllerAlwaysOn()) {
200             mNfcAdapter.setControllerAlwaysOn(false);
201             wait_for_always_on(false);
202             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
203         }
204         if (!mNfcAdapter.isEnabled()) {
205             mNfcAdapter.enable();
206             wait_for_state(NfcAdapter.STATE_ON);
207         }
208         mNfcAdapter.setControllerAlwaysOn(false);
209         wait_for_always_on(false);
210         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(false);
211     }
212 
213     @Test
testSetControllerAlwaysOnTrueFromTrueWhenEnabled()214     public void testSetControllerAlwaysOnTrueFromTrueWhenEnabled() {
215         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
216         if (!mNfcAdapter.isControllerAlwaysOn()) {
217             mNfcAdapter.setControllerAlwaysOn(true);
218             wait_for_always_on(true);
219             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
220         }
221         if (!mNfcAdapter.isEnabled()) {
222             mNfcAdapter.enable();
223             wait_for_state(NfcAdapter.STATE_ON);
224         }
225         mNfcAdapter.setControllerAlwaysOn(true);
226         wait_for_always_on(true);
227         assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
228     }
229 
230     @Test
testEnableWhenSetControllerAlwaysOnTrueAndDisabled()231     public void testEnableWhenSetControllerAlwaysOnTrueAndDisabled() {
232         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
233         if (!mNfcAdapter.isControllerAlwaysOn()) {
234             mNfcAdapter.setControllerAlwaysOn(true);
235             wait_for_always_on(true);
236             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
237         }
238         if (mNfcAdapter.isEnabled()) {
239             mNfcAdapter.disable();
240             wait_for_state(NfcAdapter.STATE_OFF);
241             assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
242         }
243         mNfcAdapter.enable();
244         wait_for_state(NfcAdapter.STATE_ON);
245         assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
246         assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
247     }
248 
249     @Test
testDisableWhenSetControllerAlwaysOnTrueAndEnabled()250     public void testDisableWhenSetControllerAlwaysOnTrueAndEnabled() {
251         if (!mNfcSupported || !mNfcAdapter.isControllerAlwaysOnSupported()) return;
252         if (!mNfcAdapter.isControllerAlwaysOn()) {
253             mNfcAdapter.setControllerAlwaysOn(true);
254             wait_for_always_on(true);
255             assertThat(mNfcAdapter.isControllerAlwaysOn()).isEqualTo(true);
256         }
257         if (!mNfcAdapter.isEnabled()) {
258             mNfcAdapter.enable();
259             wait_for_state(NfcAdapter.STATE_ON);
260             assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
261         }
262         mNfcAdapter.disable();
263         wait_for_state(NfcAdapter.STATE_OFF);
264         assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
265         assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
266     }
267 
268     @Test
testDisableWhenEnabled()269     public void testDisableWhenEnabled() {
270         if (!mNfcSupported) return;
271         if (!mNfcAdapter.isEnabled()) {
272             mNfcAdapter.enable();
273             wait_for_state(NfcAdapter.STATE_ON);
274         }
275         mNfcAdapter.disable();
276         wait_for_state(NfcAdapter.STATE_OFF);
277         assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
278         assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
279     }
280 
281     @Test
testEnableWhenDisabled()282     public void testEnableWhenDisabled() {
283         if (!mNfcSupported) return;
284         if (mNfcAdapter.isEnabled()) {
285             mNfcAdapter.disable();
286             wait_for_state(NfcAdapter.STATE_OFF);
287         }
288         mNfcAdapter.enable();
289         wait_for_state(NfcAdapter.STATE_ON);
290         assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
291         assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
292     }
293 
294     @Test
testDisableWhenDisabled()295     public void testDisableWhenDisabled() {
296         if (!mNfcSupported) return;
297         if (mNfcAdapter.isEnabled()) {
298             mNfcAdapter.disable();
299             wait_for_state(NfcAdapter.STATE_OFF);
300         }
301         mNfcAdapter.disable();
302         wait_for_state(NfcAdapter.STATE_OFF);
303         assertThat(mState).isEqualTo(NfcAdapter.STATE_OFF);
304         assertThat(mNfcAdapter.isEnabled()).isEqualTo(false);
305     }
306 
307     @Test
testEnableWhenEnabled()308     public void testEnableWhenEnabled() {
309         if (!mNfcSupported) return;
310         if (!mNfcAdapter.isEnabled()) {
311             mNfcAdapter.enable();
312             wait_for_state(NfcAdapter.STATE_ON);
313         }
314         mNfcAdapter.enable();
315         wait_for_state(NfcAdapter.STATE_ON);
316         assertThat(mState).isEqualTo(NfcAdapter.STATE_ON);
317         assertThat(mNfcAdapter.isEnabled()).isEqualTo(true);
318     }
319 
320     private class AdapterStateChangedReceiver extends BroadcastReceiver {
321         @Override
onReceive(Context context, Intent intent)322         public void onReceive(Context context, Intent intent) {
323             if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(intent.getAction())) {
324                 mState = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE,
325                         NfcAdapter.STATE_OFF);
326                 Log.i(TAG, "mState = " + mState);
327             }
328         }
329     }
330     private class AlwaysOnStateListener implements ControllerAlwaysOnListener {
331         @Override
onControllerAlwaysOnChanged(boolean isEnabled)332         public void onControllerAlwaysOnChanged(boolean isEnabled) {
333             Log.i(TAG, "onControllerAlwaysOnChanged, mIsAlwaysOnEnabled = " + isEnabled);
334             mIsAlwaysOnEnabled = isEnabled;
335         }
336     }
wait_for_state(int targetState)337     private void wait_for_state(int targetState) {
338         int duration = 100;
339         for (int i = 0; i < MAX_TIMEOUT_MS / duration; i++) {
340             msleep(duration);
341             if (mState == targetState) break;
342         }
343     }
wait_for_always_on(boolean isEnabled)344     private void wait_for_always_on(boolean isEnabled) {
345         int duration = 1000;
346         for (int i = 0; i < MAX_TIMEOUT_MS / duration; i++) {
347             msleep(duration);
348             if (isEnabled == mIsAlwaysOnEnabled) break;
349         }
350     }
351 
msleep(int millis)352     private void msleep(int millis) {
353         try {
354             Thread.sleep(millis);
355         } catch (InterruptedException e) {
356         }
357     }
358 }
359