1/*
2 * Copyright 2017 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
17package com.android.server.wifi;
18
19import static org.junit.Assert.assertFalse;
20import static org.junit.Assert.assertTrue;
21import static org.mockito.ArgumentMatchers.any;
22import static org.mockito.ArgumentMatchers.anyBoolean;
23import static org.mockito.ArgumentMatchers.anyInt;
24import static org.mockito.ArgumentMatchers.eq;
25import static org.mockito.Mockito.never;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.when;
28
29import android.content.Context;
30import android.database.ContentObserver;
31import android.net.wifi.ScanResult;
32import android.net.wifi.WifiConfiguration;
33import android.net.wifi.WifiScanner;
34import android.os.test.TestLooper;
35import android.provider.Settings;
36
37import com.android.server.wifi.util.ScanResultUtil;
38
39import org.junit.Before;
40import org.junit.Test;
41import org.mockito.ArgumentCaptor;
42import org.mockito.InOrder;
43import org.mockito.Mock;
44import org.mockito.Mockito;
45import org.mockito.MockitoAnnotations;
46import org.xmlpull.v1.XmlPullParserException;
47
48import java.io.ByteArrayOutputStream;
49import java.io.IOException;
50import java.io.PrintWriter;
51import java.util.Arrays;
52import java.util.Collections;
53import java.util.Set;
54
55/**
56 * Unit tests for {@link WakeupController}.
57 */
58public class WakeupControllerTest {
59
60    private static final String SAVED_SSID = "test scan ssid";
61    private static final int DFS_CHANNEL_FREQ = 5540;
62
63    @Mock private Context mContext;
64    @Mock private WakeupLock mWakeupLock;
65    @Mock private WakeupEvaluator mWakeupEvaluator;
66    @Mock private WakeupOnboarding mWakeupOnboarding;
67    @Mock private WifiConfigStore mWifiConfigStore;
68    @Mock private WifiInjector mWifiInjector;
69    @Mock private WifiScanner mWifiScanner;
70    @Mock private WifiConfigManager mWifiConfigManager;
71    @Mock private FrameworkFacade mFrameworkFacade;
72    @Mock private WifiSettingsStore mWifiSettingsStore;
73    @Mock private WifiWakeMetrics mWifiWakeMetrics;
74    @Mock private WifiController mWifiController;
75    @Mock private WifiNative mWifiNative;
76
77    private TestLooper mLooper;
78    private WakeupController mWakeupController;
79    private WakeupConfigStoreData mWakeupConfigStoreData;
80    private WifiScanner.ScanData[] mTestScanDatas;
81    private ScanResult mTestScanResult;
82
83    /** Initialize objects before each test run. */
84    @Before
85    public void setUp() {
86        MockitoAnnotations.initMocks(this);
87
88        when(mWifiInjector.getWifiScanner()).thenReturn(mWifiScanner);
89        when(mWifiInjector.getWifiSettingsStore()).thenReturn(mWifiSettingsStore);
90        when(mWifiInjector.getWifiController()).thenReturn(mWifiController);
91        when(mWifiInjector.getWifiNative()).thenReturn(mWifiNative);
92        when(mWifiNative.getChannelsForBand(WifiScanner.WIFI_BAND_5_GHZ_DFS_ONLY))
93                .thenReturn(new int[]{DFS_CHANNEL_FREQ});
94
95        when(mWifiSettingsStore.handleWifiToggled(anyBoolean())).thenReturn(true);
96
97        mLooper = new TestLooper();
98
99        // scanlistener input
100        mTestScanResult = new ScanResult();
101        mTestScanResult.SSID = SAVED_SSID;
102        mTestScanResult.capabilities = "";
103        mTestScanResult.frequency = 2412;
104        ScanResult[] scanResults = new ScanResult[1];
105        scanResults[0] = mTestScanResult;
106        mTestScanDatas = new WifiScanner.ScanData[1];
107        mTestScanDatas[0] = new WifiScanner.ScanData(0 /* id */, 0 /* flags */,
108                0 /* bucketsScanned */, true /* allChannelsScanned */, scanResults);
109    }
110
111    /** Initializes the wakeupcontroller in the given {@code enabled} state. */
112    private void initializeWakeupController(boolean enabled) {
113        initializeWakeupController(enabled, true /* isRead */);
114    }
115
116    private void initializeWakeupController(boolean enabled, boolean isRead) {
117        int settingsValue = enabled ? 1 : 0;
118        when(mFrameworkFacade.getIntegerSetting(mContext,
119                Settings.Global.WIFI_WAKEUP_ENABLED, 0)).thenReturn(settingsValue);
120        when(mWakeupOnboarding.isOnboarded()).thenReturn(true);
121        mWakeupController = new WakeupController(mContext,
122                mLooper.getLooper(),
123                mWakeupLock,
124                mWakeupEvaluator,
125                mWakeupOnboarding,
126                mWifiConfigManager,
127                mWifiConfigStore,
128                mWifiWakeMetrics,
129                mWifiInjector,
130                mFrameworkFacade);
131
132        ArgumentCaptor<WakeupConfigStoreData> captor =
133                ArgumentCaptor.forClass(WakeupConfigStoreData.class);
134        verify(mWifiConfigStore).registerStoreData(captor.capture());
135        mWakeupConfigStoreData = captor.getValue();
136        if (isRead) {
137            readUserStore();
138        }
139    }
140
141    private void readUserStore() {
142        try {
143            mWakeupConfigStoreData.deserializeData(null, 0, false);
144        } catch (XmlPullParserException | IOException e) {
145            // unreachable
146        }
147    }
148
149    private ScanResult createOpenScanResult(String ssid, int frequency) {
150        ScanResult scanResult = new ScanResult();
151        scanResult.SSID = ssid;
152        scanResult.capabilities = "";
153        scanResult.frequency = frequency;
154        return scanResult;
155    }
156
157    private void verifyDoesNotEnableWifi() {
158        verify(mWifiSettingsStore, never()).handleWifiToggled(true /* wifiEnabled */);
159    }
160
161    /**
162     * Verify WakeupController is enabled when the settings toggle is true.
163     */
164    @Test
165    public void verifyEnabledWhenToggledOn() {
166        initializeWakeupController(true /* enabled */);
167
168        assertTrue(mWakeupController.isEnabled());
169    }
170
171    /**
172     * Verify WakeupController is disabled when the settings toggle is false.
173     */
174    @Test
175    public void verifyDisabledWhenToggledOff() {
176        initializeWakeupController(false /* enabled */);
177
178        assertFalse(mWakeupController.isEnabled());
179    }
180
181    /**
182     * Verify WakeupController registers its store data with the WifiConfigStore on construction.
183     */
184    @Test
185    public void registersWakeupConfigStoreData() {
186        initializeWakeupController(true /* enabled */);
187        verify(mWifiConfigStore).registerStoreData(any(WakeupConfigStoreData.class));
188    }
189
190    /**
191     * Verify that dump calls also dump the state of the WakeupLock.
192     */
193    @Test
194    public void dumpIncludesWakeupLock() {
195        initializeWakeupController(true /* enabled */);
196        ByteArrayOutputStream stream = new ByteArrayOutputStream();
197        PrintWriter writer = new PrintWriter(stream);
198        mWakeupController.dump(null, writer, null);
199
200        verify(mWakeupLock).dump(null, writer, null);
201    }
202
203    /**
204     * Verify that start sets the wakeup lock.
205     */
206    @Test
207    public void startSetsWakeupLock() {
208        initializeWakeupController(true /* enabled */);
209        mWakeupController.start();
210        verify(mWakeupLock).setLock(any());
211        verify(mWifiWakeMetrics).recordStartEvent(anyInt());
212    }
213
214    /**
215     * Verify that start does not record an ignored start call if the controller is not yet active.
216     */
217    @Test
218    public void startDoesNotRecordIgnoredStart() {
219        initializeWakeupController(true /* enabled */);
220        mWakeupController.start();
221        verify(mWifiWakeMetrics, never()).recordIgnoredStart();
222    }
223
224    /**
225     * Verify that start does not set the wakeup lock when feature is disabled.
226     */
227    @Test
228    public void startDoesNotSetWakeupLockWhenDisabled() {
229        initializeWakeupController(false /* enabled */);
230        mWakeupController.start();
231        verify(mWakeupLock, never()).setLock(any());
232        verify(mWifiWakeMetrics, never()).recordStartEvent(anyInt());
233    }
234
235    /**
236     * If the controller is already active, verify that start() is ignored and no setup is done.
237     */
238    @Test
239    public void startIsIgnoredIfAlreadyActive() {
240        initializeWakeupController(true /* enabled */);
241        InOrder lockInOrder = Mockito.inOrder(mWakeupLock);
242        InOrder metricsInOrder = Mockito.inOrder(mWifiWakeMetrics);
243
244        mWakeupController.start();
245        lockInOrder.verify(mWakeupLock).setLock(any());
246        metricsInOrder.verify(mWifiWakeMetrics).recordStartEvent(anyInt());
247
248        mWakeupController.stop();
249        mWakeupController.start();
250        metricsInOrder.verify(mWifiWakeMetrics).recordIgnoredStart();
251        metricsInOrder.verify(mWifiWakeMetrics, never()).recordStartEvent(anyInt());
252        lockInOrder.verify(mWakeupLock, never()).setLock(any());
253    }
254
255    /**
256     * Verify that start registers the scan listener on the wifi scanner.
257     */
258    @Test
259    public void startRegistersScanListener() {
260        initializeWakeupController(true /* enabled */);
261        mWakeupController.start();
262        verify(mWifiScanner).registerScanListener(any());
263    }
264
265    /**
266     * Verify that stop deregisters the scan listener from the wifi scanner.
267     */
268    @Test
269    public void stopDeresgistersScanListener() {
270        initializeWakeupController(true /* enabled */);
271        mWakeupController.start();
272        mWakeupController.stop();
273        verify(mWifiScanner).deregisterScanListener(any());
274    }
275
276    /**
277     * Verify that reset sets active to false.
278     *
279     * <p>This is accomplished by initiating another call to start and verifying that the wakeup
280     * lock is re-set.
281     */
282    @Test
283    public void resetSetsActiveToFalse() {
284        initializeWakeupController(true /* enabled */);
285        InOrder lockInOrder = Mockito.inOrder(mWakeupLock);
286        InOrder metricsInOrder = Mockito.inOrder(mWifiWakeMetrics);
287
288        mWakeupController.start();
289        lockInOrder.verify(mWakeupLock).setLock(any());
290        metricsInOrder.verify(mWifiWakeMetrics).recordStartEvent(anyInt());
291
292        mWakeupController.stop();
293        mWakeupController.reset();
294        metricsInOrder.verify(mWifiWakeMetrics).recordResetEvent(0 /* numScans */);
295
296        mWakeupController.start();
297        lockInOrder.verify(mWakeupLock).setLock(any());
298        metricsInOrder.verify(mWifiWakeMetrics).recordStartEvent(anyInt());
299    }
300
301    /**
302     * Verify that the wakeup lock is initialized with the intersection of ScanResults and saved
303     * networks.
304     */
305    @Test
306    public void startInitializesWakeupLockWithSavedScanResults() {
307        String ssid1 = "ssid 1";
308        String ssid2 = "ssid 2";
309        String quotedSsid = ScanResultUtil.createQuotedSSID(ssid1);
310
311        // saved configs
312        WifiConfiguration openNetwork = WifiConfigurationTestUtil.createOpenNetwork(quotedSsid);
313        openNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
314        WifiConfiguration wepNetwork = WifiConfigurationTestUtil.createWepNetwork();
315        wepNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
316        when(mWifiConfigManager.getSavedNetworks())
317                .thenReturn(Arrays.asList(openNetwork, wepNetwork));
318
319        // scan results from most recent scan
320        ScanResult savedScanResult = createOpenScanResult(ssid1, 2412 /* frequency */);
321        ScanResult unsavedScanResult = createOpenScanResult(ssid2, 2412 /* frequency */);
322
323        when(mWifiScanner.getSingleScanResults())
324                .thenReturn(Arrays.asList(savedScanResult, unsavedScanResult));
325
326        // intersection of most recent scan + saved configs
327        Set<ScanResultMatchInfo> expectedMatchInfos =
328                Collections.singleton(ScanResultMatchInfo.fromScanResult(savedScanResult));
329
330        initializeWakeupController(true /* enabled */);
331        mWakeupController.start();
332        verify(mWakeupLock).setLock(eq(expectedMatchInfos));
333        verify(mWifiWakeMetrics).recordStartEvent(expectedMatchInfos.size());
334    }
335
336    /**
337     * Verify that start filters out DFS channels.
338     */
339    @Test
340    public void startFiltersOutDfsScanResults() {
341        String ssidDfs = "DFS scan";
342        String ssid24 = "2.4 scan";
343
344        // saved configs
345        WifiConfiguration openNetworkDfs = WifiConfigurationTestUtil
346                .createOpenNetwork(ScanResultUtil.createQuotedSSID(ssidDfs));
347        openNetworkDfs.getNetworkSelectionStatus().setHasEverConnected(true);
348        WifiConfiguration openNetwork24 = WifiConfigurationTestUtil
349                .createOpenNetwork(ScanResultUtil.createQuotedSSID(ssid24));
350        openNetwork24.getNetworkSelectionStatus().setHasEverConnected(true);
351
352        when(mWifiConfigManager.getSavedNetworks())
353                .thenReturn(Arrays.asList(openNetworkDfs, openNetwork24));
354
355        // scan results from most recent scan
356        ScanResult scanResultDfs = createOpenScanResult(ssidDfs, DFS_CHANNEL_FREQ);
357        ScanResult scanResult24 = createOpenScanResult(ssid24, 2412 /* frequency */);
358
359        when(mWifiScanner.getSingleScanResults())
360                .thenReturn(Arrays.asList(scanResultDfs, scanResult24));
361
362        // should filter out scanResultDfs
363        Set<ScanResultMatchInfo> expectedMatchInfos =
364                Collections.singleton(ScanResultMatchInfo.fromScanResult(scanResult24));
365
366        initializeWakeupController(true /* enabled */);
367        mWakeupController.start();
368        verify(mWakeupLock).setLock(eq(expectedMatchInfos));
369        verify(mWifiWakeMetrics).recordStartEvent(expectedMatchInfos.size());
370    }
371
372    /**
373     * Verify that onResults updates the WakeupLock.
374     */
375    @Test
376    public void onResultsUpdatesWakeupLock() {
377        // saved config
378        WifiConfiguration openNetwork = WifiConfigurationTestUtil
379                .createOpenNetwork(ScanResultUtil.createQuotedSSID(SAVED_SSID));
380        openNetwork.getNetworkSelectionStatus().setHasEverConnected(true);
381        when(mWifiConfigManager.getSavedNetworks())
382                .thenReturn(Collections.singletonList(openNetwork));
383
384        initializeWakeupController(true /* enabled */);
385        mWakeupController.start();
386
387        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
388                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
389
390        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
391        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
392
393        // incoming scan results
394        scanListener.onResults(mTestScanDatas);
395
396        ScanResultMatchInfo expectedMatchInfo = ScanResultMatchInfo.fromScanResult(mTestScanResult);
397        verify(mWakeupLock).update(eq(Collections.singleton(expectedMatchInfo)));
398    }
399
400
401    /**
402     * Verify that onResults filters out unsaved networks when updating the WakeupLock.
403     */
404    @Test
405    public void onResultsUpdatesWakeupLockWithOnlySavedNetworks() {
406        // no saved configs
407        when(mWifiConfigManager.getSavedNetworks()).thenReturn(Collections.emptyList());
408
409        initializeWakeupController(true /* enabled */);
410        mWakeupController.start();
411
412        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
413                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
414
415        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
416        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
417
418        // incoming scan results
419        scanListener.onResults(mTestScanDatas);
420
421        verify(mWakeupLock).update(eq(Collections.emptySet()));
422    }
423
424    /**
425     * Verify that onResults filters out DFS channels.
426     */
427    @Test
428    public void onResultsFiltersOutDfsScanResults() {
429        initializeWakeupController(true /* enabled */);
430        mWakeupController.start();
431
432        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
433                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
434
435        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
436        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
437
438        // incoming scan results
439        mTestScanResult.frequency = DFS_CHANNEL_FREQ;
440        scanListener.onResults(mTestScanDatas);
441
442        verify(mWakeupLock).update(eq(Collections.emptySet()));
443    }
444
445    /**
446     * Verify that the controller searches for viable networks during onResults when WakeupLock is
447     * unlocked.
448     */
449    @Test
450    public void onResultsSearchesForViableNetworkWhenWakeupLockIsUnlocked() {
451        // unlock wakeup lock
452        when(mWakeupLock.isUnlocked()).thenReturn(true);
453        // do not find viable network
454        when(mWakeupEvaluator.findViableNetwork(any(), any())).thenReturn(null);
455
456        initializeWakeupController(true /* enabled */);
457        mWakeupController.start();
458
459        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
460                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
461
462        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
463        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
464
465        // incoming scan results
466        scanListener.onResults(mTestScanDatas);
467
468        verify(mWakeupEvaluator).findViableNetwork(any(), any());
469        verifyDoesNotEnableWifi();
470    }
471
472    /**
473     * Verify that the controller updates the WakeupLock even if the user is not onboarded.
474     */
475    @Test
476    public void onResultsUpdatesIfNotOnboarded() {
477        initializeWakeupController(true /* enabled */);
478        when(mWakeupOnboarding.isOnboarded()).thenReturn(false);
479        when(mWakeupLock.isUnlocked()).thenReturn(false);
480        mWakeupController.start();
481
482        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
483                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
484
485        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
486        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
487
488        // incoming scan results
489        scanListener.onResults(mTestScanDatas);
490
491        verify(mWakeupLock).update(any());
492        verify(mWakeupLock).isUnlocked();
493        verifyDoesNotEnableWifi();
494    }
495
496    /**
497     * Verify that the controller enables wifi and notifies user when all criteria are met.
498     */
499    @Test
500    public void onResultsEnablesWifi() {
501        // unlock wakeup lock
502        when(mWakeupLock.isUnlocked()).thenReturn(true);
503        // find viable network
504        when(mWakeupEvaluator.findViableNetwork(any(), any())).thenReturn(mTestScanResult);
505
506        initializeWakeupController(true /* enabled */);
507        mWakeupController.start();
508
509        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
510                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
511
512        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
513        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
514
515        // incoming scan results
516        scanListener.onResults(mTestScanDatas);
517
518        verify(mWakeupEvaluator).findViableNetwork(any(), any());
519        verify(mWifiSettingsStore).handleWifiToggled(true /* wifiEnabled */);
520        verify(mWifiWakeMetrics).recordWakeupEvent(1 /* numScans */);
521    }
522
523    /**
524     * Verify that the controller will not do any work if the user store has not been read.
525     */
526    @Test
527    public void controllerDoesNoWorkIfUserStoreIsNotRead() {
528        initializeWakeupController(true /* enabled */, false /* isRead */);
529        mWakeupController.start();
530
531        ArgumentCaptor<WifiScanner.ScanListener> scanListenerArgumentCaptor =
532                ArgumentCaptor.forClass(WifiScanner.ScanListener.class);
533
534        verify(mWifiScanner).registerScanListener(scanListenerArgumentCaptor.capture());
535        WifiScanner.ScanListener scanListener = scanListenerArgumentCaptor.getValue();
536
537        // incoming scan results
538        scanListener.onResults(mTestScanDatas);
539
540        verify(mWakeupLock, never()).setLock(any());
541        verify(mWakeupLock, never()).update(any());
542        verify(mWakeupLock, never()).isUnlocked();
543        verify(mWakeupOnboarding, never()).maybeShowNotification();
544        verify(mWakeupEvaluator, never()).findViableNetwork(any(), any());
545    }
546
547    @Test
548    public void userIsNotOnboardedByInitialization() {
549        initializeWakeupController(true /* enabled */);
550        verify(mWakeupOnboarding, never()).setOnboarded();
551    }
552
553    @Test
554    public void userIsOnboardedBySettingChange() {
555        initializeWakeupController(true /* enabled */);
556        ArgumentCaptor<ContentObserver> argumentCaptor =
557                ArgumentCaptor.forClass(ContentObserver.class);
558        verify(mFrameworkFacade).registerContentObserver(any(), any(), eq(true),
559                argumentCaptor.capture());
560        ContentObserver contentObserver = argumentCaptor.getValue();
561        contentObserver.onChange(false /* selfChange */);
562        verify(mWakeupOnboarding).setOnboarded();
563    }
564}
565