WifiConnectivityManagerTest.java revision ee0ab818341d44614ffe56ae73ecc08b974c2cbb
1/*
2 * Copyright (C) 2016 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 com.android.server.wifi.WifiConfigurationTestUtil.generateWifiConfig;
20
21import static org.junit.Assert.assertEquals;
22import static org.mockito.Mockito.*;
23
24import android.content.Context;
25import android.content.res.Resources;
26import android.net.wifi.ScanResult;
27import android.net.wifi.ScanResult.InformationElement;
28import android.net.wifi.SupplicantState;
29import android.net.wifi.WifiConfiguration;
30import android.net.wifi.WifiInfo;
31import android.net.wifi.WifiScanner;
32import android.net.wifi.WifiScanner.PnoScanListener;
33import android.net.wifi.WifiScanner.PnoSettings;
34import android.net.wifi.WifiScanner.ScanListener;
35import android.net.wifi.WifiScanner.ScanSettings;
36import android.net.wifi.WifiSsid;
37import android.os.SystemClock;
38import android.os.WorkSource;
39import android.test.suitebuilder.annotation.SmallTest;
40
41import com.android.internal.R;
42import com.android.server.wifi.MockAnswerUtil.AnswerWithArguments;
43
44import org.junit.After;
45import org.junit.Before;
46import org.junit.Test;
47
48import java.nio.charset.StandardCharsets;
49import java.util.ArrayList;
50import java.util.concurrent.atomic.AtomicInteger;
51
52/**
53 * Unit tests for {@link com.android.server.wifi.WifiConnectivityManager}.
54 */
55@SmallTest
56public class WifiConnectivityManagerTest {
57
58    /**
59     * Called before each test
60     */
61    @Before
62    public void setUp() throws Exception {
63        mWifiInjector = mockWifiInjector();
64        mResource = mockResource();
65        mAlarmManager = new MockAlarmManager();
66        mContext = mockContext();
67        mWifiStateMachine = mockWifiStateMachine();
68        mWifiConfigManager = mockWifiConfigManager();
69        mWifiInfo = mockWifiInfo();
70        mWifiScanner = mockWifiScanner();
71        mWifiQNS = mockWifiQualifiedNetworkSelector();
72        mWifiConnectivityManager = new WifiConnectivityManager(mContext, mWifiStateMachine,
73                mWifiScanner, mWifiConfigManager, mWifiInfo, mWifiQNS, mWifiInjector,
74                mLooper.getLooper());
75        mWifiConnectivityManager.setWifiEnabled(true);
76        when(mClock.elapsedRealtime()).thenReturn(SystemClock.elapsedRealtime());
77    }
78
79    /**
80     * Called after each test
81     */
82    @After
83    public void cleanup() {
84        validateMockitoUsage();
85    }
86
87    private Resources mResource;
88    private Context mContext;
89    private MockAlarmManager mAlarmManager;
90    private MockLooper mLooper = new MockLooper();
91    private WifiConnectivityManager mWifiConnectivityManager;
92    private WifiQualifiedNetworkSelector mWifiQNS;
93    private WifiStateMachine mWifiStateMachine;
94    private WifiScanner mWifiScanner;
95    private WifiConfigManager mWifiConfigManager;
96    private WifiInfo mWifiInfo;
97    private Clock mClock = mock(Clock.class);
98    private WifiLastResortWatchdog mWifiLastResortWatchdog;
99    private WifiMetrics mWifiMetrics;
100    private WifiInjector mWifiInjector;
101
102    private static final int CANDIDATE_NETWORK_ID = 0;
103    private static final String CANDIDATE_SSID = "\"AnSsid\"";
104    private static final String CANDIDATE_BSSID = "6c:f3:7f:ae:8c:f3";
105    private static final String TAG = "WifiConnectivityManager Unit Test";
106    private static final long CURRENT_SYSTEM_TIME_MS = 1000;
107
108    Resources mockResource() {
109        Resources resource = mock(Resources.class);
110
111        when(resource.getInteger(R.integer.config_wifi_framework_SECURITY_AWARD)).thenReturn(80);
112        when(resource.getInteger(R.integer.config_wifi_framework_SAME_BSSID_AWARD)).thenReturn(24);
113
114        return resource;
115    }
116
117    Context mockContext() {
118        Context context = mock(Context.class);
119
120        when(context.getResources()).thenReturn(mResource);
121        when(context.getSystemService(Context.ALARM_SERVICE)).thenReturn(
122                mAlarmManager.getAlarmManager());
123
124        return context;
125    }
126
127    WifiScanner mockWifiScanner() {
128        WifiScanner scanner = mock(WifiScanner.class);
129
130        // dummy scan results. QNS PeriodicScanListener bulids scanDetails from
131        // the fullScanResult and doesn't really use results
132        final WifiScanner.ScanData[] scanDatas = new WifiScanner.ScanData[1];
133
134        // do a synchronous answer for the ScanListener callbacks
135        doAnswer(new AnswerWithArguments() {
136                public void answer(ScanSettings settings, ScanListener listener,
137                        WorkSource workSource) throws Exception {
138                    listener.onResults(scanDatas);
139                }}).when(scanner).startBackgroundScan(anyObject(), anyObject(), anyObject());
140
141        doAnswer(new AnswerWithArguments() {
142                public void answer(ScanSettings settings, ScanListener listener,
143                        WorkSource workSource) throws Exception {
144                    listener.onResults(scanDatas);
145                }}).when(scanner).startScan(anyObject(), anyObject(), anyObject());
146
147        // This unfortunately needs to be a somewhat valid scan result, otherwise
148        // |ScanDetailUtil.toScanDetail| raises exceptions.
149        final ScanResult[] scanResults = new ScanResult[1];
150        scanResults[0] = new ScanResult(WifiSsid.createFromAsciiEncoded(CANDIDATE_SSID),
151                CANDIDATE_SSID, CANDIDATE_BSSID, 1245, 0, "some caps",
152                -78, 2450, 1025, 22, 33, 20, 0, 0, true);
153        scanResults[0].informationElements = new InformationElement[1];
154        scanResults[0].informationElements[0] = new InformationElement();
155        scanResults[0].informationElements[0].id = InformationElement.EID_SSID;
156        scanResults[0].informationElements[0].bytes =
157                CANDIDATE_SSID.getBytes(StandardCharsets.UTF_8);
158
159        doAnswer(new AnswerWithArguments() {
160            public void answer(ScanSettings settings, PnoSettings pnoSettings,
161                    PnoScanListener listener) throws Exception {
162                listener.onPnoNetworkFound(scanResults);
163            }}).when(scanner).startDisconnectedPnoScan(anyObject(), anyObject(), anyObject());
164
165        doAnswer(new AnswerWithArguments() {
166            public void answer(ScanSettings settings, PnoSettings pnoSettings,
167                    PnoScanListener listener) throws Exception {
168                listener.onPnoNetworkFound(scanResults);
169            }}).when(scanner).startConnectedPnoScan(anyObject(), anyObject(), anyObject());
170
171        return scanner;
172    }
173
174    WifiStateMachine mockWifiStateMachine() {
175        WifiStateMachine stateMachine = mock(WifiStateMachine.class);
176
177        when(stateMachine.getFrequencyBand()).thenReturn(1);
178        when(stateMachine.isLinkDebouncing()).thenReturn(false);
179        when(stateMachine.isConnected()).thenReturn(false);
180        when(stateMachine.isDisconnected()).thenReturn(true);
181        when(stateMachine.isSupplicantTransientState()).thenReturn(false);
182
183        return stateMachine;
184    }
185
186    WifiQualifiedNetworkSelector mockWifiQualifiedNetworkSelector() {
187        WifiQualifiedNetworkSelector qns = mock(WifiQualifiedNetworkSelector.class);
188
189        WifiConfiguration candidate = generateWifiConfig(
190                0, CANDIDATE_NETWORK_ID, CANDIDATE_SSID, false, true, null, null);
191        candidate.BSSID = CANDIDATE_BSSID;
192        ScanResult candidateScanResult = new ScanResult();
193        candidateScanResult.SSID = CANDIDATE_SSID;
194        candidateScanResult.BSSID = CANDIDATE_BSSID;
195        candidate.getNetworkSelectionStatus().setCandidate(candidateScanResult);
196
197        when(qns.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
198              anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(candidate);
199        return qns;
200    }
201
202    WifiInfo mockWifiInfo() {
203        WifiInfo wifiInfo = mock(WifiInfo.class);
204
205        when(wifiInfo.getNetworkId()).thenReturn(WifiConfiguration.INVALID_NETWORK_ID);
206        when(wifiInfo.getBSSID()).thenReturn(null);
207        when(wifiInfo.getSupplicantState()).thenReturn(SupplicantState.DISCONNECTED);
208
209        return wifiInfo;
210    }
211
212    WifiConfigManager mockWifiConfigManager() {
213        WifiConfigManager wifiConfigManager = mock(WifiConfigManager.class);
214
215        when(wifiConfigManager.getWifiConfiguration(anyInt())).thenReturn(null);
216        wifiConfigManager.mThresholdSaturatedRssi24 = new AtomicInteger(
217                WifiQualifiedNetworkSelector.RSSI_SATURATION_2G_BAND);
218        wifiConfigManager.mCurrentNetworkBoost = new AtomicInteger(
219                WifiQualifiedNetworkSelector.SAME_NETWORK_AWARD);
220
221        // Pass dummy pno network list, otherwise Pno scan requests will not be triggered.
222        PnoSettings.PnoNetwork pnoNetwork = new PnoSettings.PnoNetwork(CANDIDATE_SSID);
223        ArrayList<PnoSettings.PnoNetwork> pnoNetworkList = new ArrayList<>();
224        pnoNetworkList.add(pnoNetwork);
225        when(wifiConfigManager.retrieveDisconnectedPnoNetworkList()).thenReturn(pnoNetworkList);
226        when(wifiConfigManager.retrieveConnectedPnoNetworkList()).thenReturn(pnoNetworkList);
227
228        return wifiConfigManager;
229    }
230
231    WifiInjector mockWifiInjector() {
232        WifiInjector wifiInjector = mock(WifiInjector.class);
233        mWifiLastResortWatchdog = mock(WifiLastResortWatchdog.class);
234        mWifiMetrics = mock(WifiMetrics.class);
235        when(wifiInjector.getWifiLastResortWatchdog()).thenReturn(mWifiLastResortWatchdog);
236        when(wifiInjector.getWifiMetrics()).thenReturn(mWifiMetrics);
237        when(wifiInjector.getClock()).thenReturn(mClock);
238        return wifiInjector;
239    }
240
241    /**
242     *  Wifi enters disconnected state while screen is on.
243     *
244     * Expected behavior: WifiConnectivityManager calls
245     * WifiStateMachine.autoConnectToNetwork() with the
246     * expected candidate network ID and BSSID.
247     */
248    @Test
249    public void enterWifiDisconnectedStateWhenScreenOn() {
250        // Set screen to on
251        mWifiConnectivityManager.handleScreenStateChanged(true);
252
253        // Set WiFi to disconnected state
254        mWifiConnectivityManager.handleConnectionStateChanged(
255                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
256
257        verify(mWifiStateMachine).autoConnectToNetwork(
258                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
259    }
260
261    /**
262     *  Wifi enters connected state while screen is on.
263     *
264     * Expected behavior: WifiConnectivityManager calls
265     * WifiStateMachine.autoConnectToNetwork() with the
266     * expected candidate network ID and BSSID.
267     */
268    @Test
269    public void enterWifiConnectedStateWhenScreenOn() {
270        // Set screen to on
271        mWifiConnectivityManager.handleScreenStateChanged(true);
272
273        // Set WiFi to connected state
274        mWifiConnectivityManager.handleConnectionStateChanged(
275                WifiConnectivityManager.WIFI_STATE_CONNECTED);
276
277        verify(mWifiStateMachine).autoConnectToNetwork(
278                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
279    }
280
281    /**
282     *  Screen turned on while WiFi in disconnected state.
283     *
284     * Expected behavior: WifiConnectivityManager calls
285     * WifiStateMachine.autoConnectToNetwork() with the
286     * expected candidate network ID and BSSID.
287     */
288    @Test
289    public void turnScreenOnWhenWifiInDisconnectedState() {
290        // Set WiFi to disconnected state
291        mWifiConnectivityManager.handleConnectionStateChanged(
292                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
293
294        // Set screen to on
295        mWifiConnectivityManager.handleScreenStateChanged(true);
296
297        verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
298                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
299    }
300
301    /**
302     *  Screen turned on while WiFi in connected state.
303     *
304     * Expected behavior: WifiConnectivityManager calls
305     * WifiStateMachine.autoConnectToNetwork() with the
306     * expected candidate network ID and BSSID.
307     */
308    @Test
309    public void turnScreenOnWhenWifiInConnectedState() {
310        // Set WiFi to connected state
311        mWifiConnectivityManager.handleConnectionStateChanged(
312                WifiConnectivityManager.WIFI_STATE_CONNECTED);
313
314        // Set screen to on
315        mWifiConnectivityManager.handleScreenStateChanged(true);
316
317        verify(mWifiStateMachine, atLeastOnce()).autoConnectToNetwork(
318                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
319    }
320
321    /**
322     * Multiple back to back connection attempts within the rate interval should be rate limited.
323     *
324     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
325     * with the expected candidate network ID and BSSID for only the expected number of times within
326     * the given interval.
327     */
328    @Test
329    public void connectionAttemptRateLimitedWhenScreenOff() {
330        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
331        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
332        int numAttempts = 0;
333        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
334
335        mWifiConnectivityManager.handleScreenStateChanged(false);
336
337        // First attempt the max rate number of connections within the rate interval.
338        long currentTimeStamp = 0;
339        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
340            currentTimeStamp += connectionAttemptIntervals;
341            when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
342            // Set WiFi to disconnected state to trigger PNO scan
343            mWifiConnectivityManager.handleConnectionStateChanged(
344                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
345            numAttempts++;
346        }
347        // Now trigger another connection attempt before the rate interval, this should be
348        // skipped because we've crossed rate limit.
349        when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
350        // Set WiFi to disconnected state to trigger PNO scan
351        mWifiConnectivityManager.handleConnectionStateChanged(
352                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
353
354        // Verify that we attempt to connect upto the rate.
355        verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
356                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
357    }
358
359    /**
360     * Multiple back to back connection attempts outside the rate interval should not be rate
361     * limited.
362     *
363     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
364     * with the expected candidate network ID and BSSID for only the expected number of times within
365     * the given interval.
366     */
367    @Test
368    public void connectionAttemptNotRateLimitedWhenScreenOff() {
369        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
370        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
371        int numAttempts = 0;
372        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
373
374        mWifiConnectivityManager.handleScreenStateChanged(false);
375
376        // First attempt the max rate number of connections within the rate interval.
377        long currentTimeStamp = 0;
378        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
379            currentTimeStamp += connectionAttemptIntervals;
380            when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
381            // Set WiFi to disconnected state to trigger PNO scan
382            mWifiConnectivityManager.handleConnectionStateChanged(
383                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
384            numAttempts++;
385        }
386        // Now trigger another connection attempt after the rate interval, this should not be
387        // skipped because we should've evicted the older attempt.
388        when(mClock.elapsedRealtime()).thenReturn(
389                currentTimeStamp + connectionAttemptIntervals * 2);
390        // Set WiFi to disconnected state to trigger PNO scan
391        mWifiConnectivityManager.handleConnectionStateChanged(
392                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
393        numAttempts++;
394
395        // Verify that all the connection attempts went through
396        verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
397                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
398    }
399
400    /**
401     * Multiple back to back connection attempts after a user selection should not be rate limited.
402     *
403     * Expected behavior: WifiConnectivityManager calls WifiStateMachine.autoConnectToNetwork()
404     * with the expected candidate network ID and BSSID for only the expected number of times within
405     * the given interval.
406     */
407    @Test
408    public void connectionAttemptNotRateLimitedWhenScreenOffAfterUserSelection() {
409        int maxAttemptRate = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_RATE;
410        int timeInterval = WifiConnectivityManager.MAX_CONNECTION_ATTEMPTS_TIME_INTERVAL_MS;
411        int numAttempts = 0;
412        int connectionAttemptIntervals = timeInterval / maxAttemptRate;
413
414        mWifiConnectivityManager.handleScreenStateChanged(false);
415
416        // First attempt the max rate number of connections within the rate interval.
417        long currentTimeStamp = 0;
418        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
419            currentTimeStamp += connectionAttemptIntervals;
420            when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
421            // Set WiFi to disconnected state to trigger PNO scan
422            mWifiConnectivityManager.handleConnectionStateChanged(
423                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
424            numAttempts++;
425        }
426
427        mWifiConnectivityManager.connectToUserSelectNetwork(CANDIDATE_NETWORK_ID, false);
428
429        for (int attempt = 0; attempt < maxAttemptRate; attempt++) {
430            currentTimeStamp += connectionAttemptIntervals;
431            when(mClock.elapsedRealtime()).thenReturn(currentTimeStamp);
432            // Set WiFi to disconnected state to trigger PNO scan
433            mWifiConnectivityManager.handleConnectionStateChanged(
434                    WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
435            numAttempts++;
436        }
437
438        // Verify that all the connection attempts went through
439        verify(mWifiStateMachine, times(numAttempts)).autoConnectToNetwork(
440                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
441    }
442
443    /**
444     *  PNO retry for low RSSI networks.
445     *
446     * Expected behavior: WifiConnectivityManager doubles the low RSSI
447     * network retry delay value after QNS skips the PNO scan results
448     * because of their low RSSI values.
449     */
450    @Test
451    public void PnoRetryForLowRssiNetwork() {
452        when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
453              anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
454
455        // Set screen to off
456        mWifiConnectivityManager.handleScreenStateChanged(false);
457
458        // Get the current retry delay value
459        int lowRssiNetworkRetryDelayStartValue = mWifiConnectivityManager
460                .getLowRssiNetworkRetryDelay();
461
462        // Set WiFi to disconnected state to trigger PNO scan
463        mWifiConnectivityManager.handleConnectionStateChanged(
464                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
465
466        // Get the retry delay value after QNS didn't select a
467        // network candicate from the PNO scan results.
468        int lowRssiNetworkRetryDelayAfterPnoValue = mWifiConnectivityManager
469                .getLowRssiNetworkRetryDelay();
470
471        assertEquals(lowRssiNetworkRetryDelayStartValue * 2,
472            lowRssiNetworkRetryDelayAfterPnoValue);
473    }
474
475    /**
476     * Ensure that the watchdog bite increments the "Pno bad" metric.
477     *
478     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
479     * a candidate while watchdog single scan did.
480     */
481    @Test
482    public void watchdogBitePnoBadIncrementsMetrics() {
483        // Set screen to off
484        mWifiConnectivityManager.handleScreenStateChanged(false);
485
486        // Set WiFi to disconnected state to trigger PNO scan
487        mWifiConnectivityManager.handleConnectionStateChanged(
488                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
489
490        // Now fire the watchdog alarm and verify the metrics were incremented.
491        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
492        mLooper.dispatchAll();
493
494        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoBad();
495        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoGood();
496    }
497
498    /**
499     * Ensure that the watchdog bite increments the "Pno good" metric.
500     *
501     * Expected behavior: WifiConnectivityManager detects that the PNO scan failed to find
502     * a candidate which was the same with watchdog single scan.
503     */
504    @Test
505    public void watchdogBitePnoGoodIncrementsMetrics() {
506        // Qns returns no candidate after watchdog single scan.
507        when(mWifiQNS.selectQualifiedNetwork(anyBoolean(), anyBoolean(), anyObject(),
508                anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean())).thenReturn(null);
509
510        // Set screen to off
511        mWifiConnectivityManager.handleScreenStateChanged(false);
512
513        // Set WiFi to disconnected state to trigger PNO scan
514        mWifiConnectivityManager.handleConnectionStateChanged(
515                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
516
517        // Now fire the watchdog alarm and verify the metrics were incremented.
518        mAlarmManager.dispatch(WifiConnectivityManager.WATCHDOG_TIMER_TAG);
519        mLooper.dispatchAll();
520
521        verify(mWifiMetrics).incrementNumConnectivityWatchdogPnoGood();
522        verify(mWifiMetrics, never()).incrementNumConnectivityWatchdogPnoBad();
523    }
524
525    /**
526     *  Verify that scan interval for screen on and wifi disconnected scenario
527     *  is in the exponential backoff fashion.
528     *
529     * Expected behavior: WifiConnectivityManager doubles periodic
530     * scan interval.
531     */
532    @Test
533    public void checkPeriodicScanIntervalWhenDisconnected() {
534        when(mClock.elapsedRealtime()).thenReturn(CURRENT_SYSTEM_TIME_MS);
535
536        // Set screen to ON
537        mWifiConnectivityManager.handleScreenStateChanged(true);
538
539        // Set WiFi to disconnected state to trigger periodic scan
540        mWifiConnectivityManager.handleConnectionStateChanged(
541                WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
542
543        // Get the first periodic scan interval
544        long firstIntervalMs = mAlarmManager
545                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
546                    - CURRENT_SYSTEM_TIME_MS;
547        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
548
549        // Now fire the first periodic scan alarm timer
550        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
551        mLooper.dispatchAll();
552
553        // Get the second periodic scan interval
554        long secondIntervalMs = mAlarmManager
555                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
556                    - CURRENT_SYSTEM_TIME_MS;
557
558        // Verify the intervals are exponential back off
559        assertEquals(firstIntervalMs * 2, secondIntervalMs);
560
561        // Make sure we eventually stay at the maximum scan interval.
562        long intervalMs = 0;
563        for (int i = 0; i < 5; i++) {
564            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
565            mLooper.dispatchAll();
566            intervalMs = mAlarmManager
567                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
568                    - CURRENT_SYSTEM_TIME_MS;
569        }
570
571        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
572    }
573
574    /**
575     *  Verify that scan interval for screen on and wifi connected scenario
576     *  is in the exponential backoff fashion.
577     *
578     * Expected behavior: WifiConnectivityManager doubles periodic
579     * scan interval.
580     */
581    @Test
582    public void checkPeriodicScanIntervalWhenConnected() {
583        when(mClock.elapsedRealtime()).thenReturn(CURRENT_SYSTEM_TIME_MS);
584
585        // Set screen to ON
586        mWifiConnectivityManager.handleScreenStateChanged(true);
587
588        // Set WiFi to connected state to trigger periodic scan
589        mWifiConnectivityManager.handleConnectionStateChanged(
590                WifiConnectivityManager.WIFI_STATE_CONNECTED);
591
592        // Get the first periodic scan interval
593        long firstIntervalMs = mAlarmManager
594                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
595                    - CURRENT_SYSTEM_TIME_MS;
596        assertEquals(firstIntervalMs, WifiConnectivityManager.PERIODIC_SCAN_INTERVAL_MS);
597
598        // Now fire the first periodic scan alarm timer
599        mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
600        mLooper.dispatchAll();
601
602        // Get the second periodic scan interval
603        long secondIntervalMs = mAlarmManager
604                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
605                    - CURRENT_SYSTEM_TIME_MS;
606
607        // Verify the intervals are exponential back off
608        assertEquals(firstIntervalMs * 2, secondIntervalMs);
609
610        // Make sure we eventually stay at the maximum scan interval.
611        long intervalMs = 0;
612        for (int i = 0; i < 5; i++) {
613            mAlarmManager.dispatch(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG);
614            mLooper.dispatchAll();
615            intervalMs = mAlarmManager
616                    .getTriggerTimeMillis(WifiConnectivityManager.PERIODIC_SCAN_TIMER_TAG)
617                    - CURRENT_SYSTEM_TIME_MS;
618        }
619
620        assertEquals(intervalMs, WifiConnectivityManager.MAX_PERIODIC_SCAN_INTERVAL_MS);
621    }
622}
623