1 /*
2  * Copyright (C) 2023 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 android.net.wifi.nl80211;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 import static org.mockito.ArgumentMatchers.argThat;
22 import static org.mockito.ArgumentMatchers.eq;
23 import static org.mockito.Mockito.any;
24 import static org.mockito.Mockito.anyInt;
25 import static org.mockito.Mockito.anyLong;
26 import static org.mockito.Mockito.doNothing;
27 import static org.mockito.Mockito.doReturn;
28 import static org.mockito.Mockito.mock;
29 import static org.mockito.Mockito.never;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.when;
32 
33 import android.app.AlarmManager;
34 import android.app.test.TestAlarmManager;
35 import android.content.BroadcastReceiver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.net.ConnectivityManager;
40 import android.net.ConnectivityManager.NetworkCallback;
41 import android.net.Network;
42 import android.net.NetworkCapabilities;
43 import android.net.wifi.WifiConfiguration;
44 import android.net.wifi.WifiInfo;
45 import android.net.wifi.WifiManager;
46 import android.os.Handler;
47 import android.os.IPowerManager;
48 import android.os.IThermalService;
49 import android.os.PowerManager;
50 import android.os.test.TestLooper;
51 
52 import androidx.test.filters.SmallTest;
53 
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.mockito.ArgumentCaptor;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 
60 import java.util.HashSet;
61 import java.util.Set;
62 
63 /**
64  * Unit tests for {@link android.net.wifi.nl80211.InstantWifi}.
65  */
66 @SmallTest
67 public class InstantWifiTest {
68     @Mock private Context mContext;
69     @Mock private ConnectivityManager mMockConnectivityManager;
70     @Mock private WifiManager mMockWifiManager;
71     @Mock private Network mMockWifiNetwork;
72     @Mock private WifiInfo mMockWifiInfo;
73     @Mock private WifiConfiguration mMockWifiConfiguration;
74     @Mock private IPowerManager mPowerManagerService;
75     private InstantWifi mInstantWifi;
76     private TestLooper mLooper;
77     private Handler mHandler;
78     private TestAlarmManager mTestAlarmManager;
79     private AlarmManager mAlarmManager;
80     private PowerManager mMockPowerManager;
81 
82     private final ArgumentCaptor<NetworkCallback> mWifiNetworkCallbackCaptor =
83             ArgumentCaptor.forClass(NetworkCallback.class);
84     private final ArgumentCaptor<BroadcastReceiver> mScreenBroadcastReceiverCaptor =
85             ArgumentCaptor.forClass(BroadcastReceiver.class);
86     private final ArgumentCaptor<BroadcastReceiver> mWifiStateBroadcastReceiverCaptor =
87             ArgumentCaptor.forClass(BroadcastReceiver.class);
88 
89     private static final int TEST_NETWORK_ID = 1;
90     private static final int TEST_24G_FREQUENCY = 2412;
91     private static final int TEST_5G_FREQUENCY = 5745;
92     private long mTimeOffsetMs = 0;
93 
94     private class InstantWifiSpy extends InstantWifi {
InstantWifiSpy(Context context, AlarmManager alarmManager, Handler handler)95         InstantWifiSpy(Context context, AlarmManager alarmManager, Handler handler) {
96             super(context, alarmManager, handler);
97         }
98 
99         @Override
getMockableElapsedRealtime()100         protected long getMockableElapsedRealtime() {
101             return mTimeOffsetMs;
102         }
103     }
104 
105     @Before
setUp()106     public void setUp() throws Exception {
107         MockitoAnnotations.initMocks(this);
108 
109         mLooper = new TestLooper();
110         mHandler = new Handler(mLooper.getLooper());
111 
112         mTestAlarmManager = new TestAlarmManager();
113         mAlarmManager = mTestAlarmManager.getAlarmManager();
114         when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn(Context.ALARM_SERVICE);
115         when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
116         when(mContext.getSystemServiceName(WifiManager.class)).thenReturn(Context.WIFI_SERVICE);
117         when(mContext.getSystemService(WifiManager.class)).thenReturn(mMockWifiManager);
118         when(mContext.getSystemServiceName(ConnectivityManager.class))
119                 .thenReturn(Context.CONNECTIVITY_SERVICE);
120         when(mContext.getSystemService(ConnectivityManager.class))
121                 .thenReturn(mMockConnectivityManager);
122         mMockPowerManager = new PowerManager(mContext, mPowerManagerService,
123                 mock(IThermalService.class), mHandler);
124         when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE);
125         when(mContext.getSystemService(PowerManager.class)).thenReturn(mMockPowerManager);
126         when(mPowerManagerService.isInteractive()).thenReturn(true);
127 
128         doReturn(mMockWifiInfo).when(mMockWifiInfo).makeCopy(anyLong());
129         mTimeOffsetMs = 0;
130         mInstantWifi = new InstantWifiSpy(mContext, mAlarmManager, mHandler);
131         verifyInstantWifiInitialization();
132     }
133 
verifyInstantWifiInitialization()134     private void verifyInstantWifiInitialization() {
135         verify(mMockConnectivityManager).registerNetworkCallback(any(),
136                 mWifiNetworkCallbackCaptor.capture());
137         verify(mContext).registerReceiver(mScreenBroadcastReceiverCaptor.capture(),
138                 argThat((IntentFilter filter) ->
139                         filter.hasAction(Intent.ACTION_SCREEN_ON)
140                                 && filter.hasAction(Intent.ACTION_SCREEN_OFF)), eq(null), any());
141 
142         verify(mContext).registerReceiver(mWifiStateBroadcastReceiverCaptor.capture(),
143                 argThat((IntentFilter filter) ->
144                         filter.hasAction(WifiManager.WIFI_STATE_CHANGED_ACTION)), eq(null), any());
145     }
146 
mockWifiConnectedEvent(int networkId, int connectedFrequency)147     private void mockWifiConnectedEvent(int networkId, int connectedFrequency) {
148         // Send wifi connected event
149         NetworkCapabilities mockWifiNetworkCapabilities =
150                 new NetworkCapabilities.Builder().setTransportInfo(mMockWifiInfo).build();
151         mMockWifiConfiguration.networkId = networkId;
152         when(mMockWifiManager.getPrivilegedConnectedNetwork()).thenReturn(mMockWifiConfiguration);
153         when(mMockWifiInfo.getFrequency()).thenReturn(connectedFrequency);
154         mWifiNetworkCallbackCaptor.getValue().onCapabilitiesChanged(mMockWifiNetwork,
155                 mockWifiNetworkCapabilities);
156         mLooper.dispatchAll();
157     }
158 
mockWifiOnScreenOnBroadcast(boolean isWifiOn, boolean isScreenOn)159     private void mockWifiOnScreenOnBroadcast(boolean isWifiOn, boolean isScreenOn)
160             throws Exception {
161         // Send Wifi On broadcast
162         Intent wifiOnIntent = new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION);
163         wifiOnIntent.putExtra(WifiManager.EXTRA_WIFI_STATE,
164                 isWifiOn ? WifiManager.WIFI_STATE_ENABLED : WifiManager.WIFI_STATE_DISABLED);
165         mWifiStateBroadcastReceiverCaptor.getValue().onReceive(mContext, wifiOnIntent);
166         mLooper.dispatchAll();
167         // Send Screen On broadcast
168         Intent screenOnIntent =
169                 new Intent(isScreenOn ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF);
170         mScreenBroadcastReceiverCaptor.getValue().onReceive(mContext, screenOnIntent);
171         mLooper.dispatchAll();
172         when(mMockWifiManager.isWifiEnabled()).thenReturn(isWifiOn);
173         when(mPowerManagerService.isInteractive()).thenReturn(isScreenOn);
174     }
175 
176     @Test
testisUsePredictedScanningChannels()177     public void testisUsePredictedScanningChannels() throws Exception {
178         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
179         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, false /* isScreenOn */);
180         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
181         mockWifiOnScreenOnBroadcast(false /* isWifiOn */, true /* isScreenOn */);
182         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
183         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */);
184         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
185         // Send wifi connected event
186         mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY);
187         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
188         // Send wifi disconnect
189         mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork);
190         assertTrue(mInstantWifi.isUsePredictedScanningChannels());
191         // Shift time to make it expired
192         mTimeOffsetMs = 1100;
193         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
194     }
195 
196     @Test
testGetPredictedScanningChannels()197     public void testGetPredictedScanningChannels() throws Exception {
198         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */);
199         // Send wifi connected event on T0
200         mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY);
201         // Send wifi disconnect
202         mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork);
203         assertTrue(mInstantWifi.isUsePredictedScanningChannels());
204         assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_24G_FREQUENCY));
205         mTimeOffsetMs += 1000; // T1 = 1000 ms
206         // Send wifi connected event
207         mockWifiConnectedEvent(TEST_NETWORK_ID + 1, TEST_5G_FREQUENCY);
208         // Send wifi disconnect
209         mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork);
210         // isUsePredictedScanningChannels is false since wifi on & screen on is expired
211         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
212         // Override the Wifi On & Screen on time
213         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */);
214         assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_5G_FREQUENCY));
215         mTimeOffsetMs += 7 * 24 * 60 * 60 * 1000; // Make T0 expired
216         // Override the Wifi On & Screen on time
217         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */);
218         assertFalse(mInstantWifi.getPredictedScanningChannels().contains(TEST_24G_FREQUENCY));
219         assertTrue(mInstantWifi.getPredictedScanningChannels().contains(TEST_5G_FREQUENCY));
220     }
221 
222     @Test
testOverrideFreqsForSingleScanSettings()223     public void testOverrideFreqsForSingleScanSettings() throws Exception {
224         mockWifiOnScreenOnBroadcast(true /* isWifiOn */, true /* isScreenOn */);
225         // Send wifi connected event
226         mockWifiConnectedEvent(TEST_NETWORK_ID, TEST_24G_FREQUENCY);
227         assertFalse(mInstantWifi.isUsePredictedScanningChannels());
228         // Send wifi disconnect
229         mWifiNetworkCallbackCaptor.getValue().onLost(mMockWifiNetwork);
230         assertTrue(mInstantWifi.isUsePredictedScanningChannels());
231 
232         final ArgumentCaptor<AlarmManager.OnAlarmListener> alarmListenerCaptor =
233                 ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
234         doNothing().when(mAlarmManager).set(anyInt(), anyLong(), any(),
235                 alarmListenerCaptor.capture(), any());
236         Set<Integer> testFreqs = Set.of(
237                 TEST_24G_FREQUENCY, TEST_5G_FREQUENCY);
238         SingleScanSettings testSingleScanSettings = new SingleScanSettings();
239         mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(
240                 testSingleScanSettings, new HashSet<Integer>());
241         mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(
242                 testSingleScanSettings, null);
243         mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(null, null);
244         verify(mAlarmManager, never()).set(anyInt(), anyLong(), any(), any(), any());
245         mInstantWifi.overrideFreqsForSingleScanSettingsIfNecessary(testSingleScanSettings,
246                 testFreqs);
247         verify(mAlarmManager).set(anyInt(), anyLong(), any(), any(), any());
248         Set<Integer> overridedFreqs = new HashSet<Integer>();
249         for (ChannelSettings channel : testSingleScanSettings.channelSettings) {
250             overridedFreqs.add(channel.frequency);
251         }
252         assertEquals(testFreqs, overridedFreqs);
253         alarmListenerCaptor.getValue().onAlarm();
254         verify(mMockWifiManager).startScan();
255     }
256 }
257