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 org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertTrue;
21import static org.mockito.Mockito.*;
22import static org.mockito.MockitoAnnotations.*;
23
24import android.net.wifi.WifiConfiguration;
25import android.net.wifi.WifiSsid;
26import android.os.test.TestLooper;
27import android.support.test.filters.SmallTest;
28import android.util.Pair;
29
30import org.junit.Before;
31import org.junit.Test;
32import org.mockito.Mock;
33
34import java.util.ArrayList;
35import java.util.Arrays;
36import java.util.List;
37
38/**
39 * Unit tests for {@link com.android.server.wifi.WifiLastResortWatchdog}.
40 */
41@SmallTest
42public class WifiLastResortWatchdogTest {
43    WifiLastResortWatchdog mLastResortWatchdog;
44    @Mock WifiMetrics mWifiMetrics;
45    @Mock SelfRecovery mSelfRecovery;
46    @Mock WifiStateMachine mWifiStateMachine;
47    @Mock Clock mClock;
48
49    private String[] mSsids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
50    private String[] mBssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
51            "c0:ff:ee:ee:e3:ee"};
52    private int[] mFrequencies = {2437, 5180, 5180, 2437};
53    private String[] mCaps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
54            "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
55    private int[] mLevels = {-60, -86, -50, -62};
56    private boolean[] mIsEphemeral = {false, false, false, false};
57    private boolean[] mHasEverConnected = {false, false, false, false};
58    private TestLooper mLooper;
59
60    @Before
61    public void setUp() throws Exception {
62        initMocks(this);
63        mLooper = new TestLooper();
64        mLastResortWatchdog = new WifiLastResortWatchdog(mSelfRecovery, mClock, mWifiMetrics,
65                mWifiStateMachine, mLooper.getLooper());
66        mLastResortWatchdog.setBugReportProbability(1);
67    }
68
69    private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids,
70            String[] bssids, int[] frequencies, String[] caps, int[] levels,
71            boolean[] isEphemeral) {
72        List<Pair<ScanDetail, WifiConfiguration>> candidates = new ArrayList<>();
73        long timeStamp = System.currentTimeMillis();
74        for (int index = 0; index < ssids.length; index++) {
75            String ssid = ssids[index].replaceAll("^\"+", "").replaceAll("\"+$", "");
76            ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssid),
77                    bssids[index], caps[index], levels[index], frequencies[index], timeStamp,
78                    0);
79            WifiConfiguration config = null;
80            if (!isEphemeral[index]) {
81                config = mock(WifiConfiguration.class);
82                WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
83                        mock(WifiConfiguration.NetworkSelectionStatus.class);
84                when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus);
85                when(networkSelectionStatus.getHasEverConnected()).thenReturn(true);
86            }
87            candidates.add(Pair.create(scanDetail, config));
88        }
89        return candidates;
90    }
91
92    private List<Pair<ScanDetail, WifiConfiguration>> createFilteredQnsCandidates(String[] ssids,
93            String[] bssids, int[] frequencies, String[] caps, int[] levels,
94            boolean[] isEphemeral, boolean[] hasEverConnected) {
95        List<Pair<ScanDetail, WifiConfiguration>> candidates =
96                new ArrayList<Pair<ScanDetail, WifiConfiguration>>();
97        long timeStamp = System.currentTimeMillis();
98        for (int index = 0; index < ssids.length; index++) {
99            String ssid = ssids[index].replaceAll("^\"+", "").replaceAll("\"+$", "");
100            ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssid),
101                    bssids[index], caps[index], levels[index], frequencies[index], timeStamp,
102                    0);
103            WifiConfiguration config = null;
104            if (!isEphemeral[index]) {
105                config = mock(WifiConfiguration.class);
106                WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
107                        mock(WifiConfiguration.NetworkSelectionStatus.class);
108                when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus);
109                when(networkSelectionStatus.getHasEverConnected())
110                        .thenReturn(hasEverConnected[index]);
111            }
112            candidates.add(Pair.create(scanDetail, config));
113        }
114        return candidates;
115    }
116
117    private void assertFailureCountEquals(
118            String bssid, int associationRejections, int authenticationFailures, int dhcpFailures) {
119        assertEquals(associationRejections, mLastResortWatchdog.getFailureCount(bssid,
120                WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION));
121        assertEquals(authenticationFailures, mLastResortWatchdog.getFailureCount(bssid,
122                WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION));
123        assertEquals(dhcpFailures, mLastResortWatchdog.getFailureCount(bssid,
124                WifiLastResortWatchdog.FAILURE_CODE_DHCP));
125    }
126
127    /**
128     * Case #1: Test aging works in available network buffering
129     * This test simulates 4 networks appearing in a scan result, and then only the first 2
130     * appearing in successive scans results.
131     * Expected Behavior:
132     * 4 networks appear in recentAvailalbeNetworks, after N=MAX_BSSID_AGE scans, only 2 remain
133     */
134    @Test
135    public void testAvailableNetworkBuffering_ageCullingWorks() throws Exception {
136        // Buffer potential candidates 1,2,3 & 4
137        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
138                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral);
139        mLastResortWatchdog.updateAvailableNetworks(candidates);
140        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 4);
141
142        // Repeatedly buffer candidates 1 & 2, MAX_BSSID_AGE - 1 times
143        candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, 2),
144                Arrays.copyOfRange(mBssids, 0, 2),
145                Arrays.copyOfRange(mFrequencies, 0, 2),
146                Arrays.copyOfRange(mCaps, 0, 2),
147                Arrays.copyOfRange(mLevels, 0, 2),
148                Arrays.copyOfRange(mIsEphemeral, 0, 2));
149        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE - 1; i++) {
150            mLastResortWatchdog.updateAvailableNetworks(candidates);
151            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[0]).age, 0);
152            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[1]).age, 0);
153            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[2]).age,
154                    i + 1);
155            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[3]).age,
156                    i + 1);
157        }
158        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 4);
159
160        // One more buffering should age and cull candidates 2 & 3
161        mLastResortWatchdog.updateAvailableNetworks(candidates);
162        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 2);
163    };
164
165    /**
166     * Case #2: Culling of old networks
167     * Part 1:
168     * This test starts with 4 networks seen, it then buffers N=MAX_BSSID_AGE empty scans
169     * Expected behaviour: All networks are culled from recentAvailableNetworks
170     *
171     * Part 2:
172     * Buffer some more empty scans just to make sure nothing breaks
173     */
174    @Test
175    public void testAvailableNetworkBuffering_emptyBufferWithEmptyScanResults() throws Exception {
176        // Buffer potential candidates 1,2,3 & 4
177        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
178                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral);
179        mLastResortWatchdog.updateAvailableNetworks(candidates);
180        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 4);
181
182        // Repeatedly buffer with no candidates
183        candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, 0),
184                Arrays.copyOfRange(mBssids, 0, 0),
185                Arrays.copyOfRange(mFrequencies, 0, 0),
186                Arrays.copyOfRange(mCaps, 0, 0),
187                Arrays.copyOfRange(mLevels, 0, 0),
188                Arrays.copyOfRange(mIsEphemeral, 0, 0));
189        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) {
190            mLastResortWatchdog.updateAvailableNetworks(candidates);
191        }
192        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 0);
193        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) {
194            mLastResortWatchdog.updateAvailableNetworks(candidates);
195        }
196        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 0);
197    };
198
199    /**
200     * Case 3: Adding more networks over time
201     * In this test, each successive (4 total) scan result buffers one more network.
202     * Expected behavior: recentAvailableNetworks grows with number of scan results
203     */
204    @Test
205    public void testAvailableNetworkBuffering_addNewNetworksOverTime() throws Exception {
206        List<Pair<ScanDetail, WifiConfiguration>> candidates;
207        // Buffer (i) scan results with each successive scan result
208        for (int i = 1; i <= mSsids.length; i++) {
209            candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, i),
210                    Arrays.copyOfRange(mBssids, 0, i),
211                    Arrays.copyOfRange(mFrequencies, 0, i),
212                    Arrays.copyOfRange(mCaps, 0, i),
213                    Arrays.copyOfRange(mLevels, 0, i),
214                    Arrays.copyOfRange(mIsEphemeral, 0, i));
215            mLastResortWatchdog.updateAvailableNetworks(candidates);
216            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), i);
217            for (int j = 0; j < i; j++) {
218                assertEquals(
219                        mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[j]).age, 0);
220            }
221        }
222    };
223
224    /**
225     *  Case 4: Test buffering with ephemeral networks & toString()
226     *  This test is the same as Case 1, but it also includes ephemeral networks. toString is also
227     *  smoke tested at various places in this test
228     *  Expected behaviour: 4 networks added initially (2 ephemeral). After MAX_BSSID_AGE more
229     *  bufferings, 2 are culled (leaving 1 ephemeral, one normal). toString method should execute
230     *  without breaking anything.
231     */
232    @Test
233    public void testAvailableNetworkBuffering_multipleNetworksSomeEphemeral() throws Exception {
234        boolean[] isEphemeral = {true, false, true, false};
235
236        // Buffer potential candidates 1,2,3 & 4
237        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
238                mBssids, mFrequencies, mCaps, mLevels, isEphemeral);
239        mLastResortWatchdog.updateAvailableNetworks(candidates);
240        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 4);
241
242        // Repeatedly buffer candidates 1 & 2, MAX_BSSID_AGE - 1 times
243        candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, 2),
244                Arrays.copyOfRange(mBssids, 0, 2),
245                Arrays.copyOfRange(mFrequencies, 0, 2),
246                Arrays.copyOfRange(mCaps, 0, 2),
247                Arrays.copyOfRange(mLevels, 0, 2),
248                Arrays.copyOfRange(isEphemeral, 0, 2));
249        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE - 1; i++) {
250            mLastResortWatchdog.updateAvailableNetworks(candidates);
251            mLastResortWatchdog.toString();
252            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[0]).age, 0);
253            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[1]).age, 0);
254            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[2]).age,
255                    i + 1);
256            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().get(mBssids[3]).age,
257                    i + 1);
258        }
259        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 4);
260
261        // One more buffering should age and cull candidates 2 & 3
262        mLastResortWatchdog.updateAvailableNetworks(candidates);
263        assertEquals(mLastResortWatchdog.getRecentAvailableNetworks().size(), 2);
264        mLastResortWatchdog.toString();
265    };
266
267    /**
268     * Case 5: Test failure counting, incrementing a specific BSSID
269     * Test has 4 networks buffered, increment each different failure type on one of them
270     * Expected behaviour: See failure counts for the specific failures rise to the appropriate
271     * level for the specific network
272     */
273    @Test
274    public void testFailureCounting_countFailuresForSingleBssid() throws Exception {
275        int associationRejections = 5;
276        int authenticationFailures = 9;
277        int dhcpFailures = 11;
278        // Buffer potential candidates 1,2,3 & 4
279        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
280                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
281        mLastResortWatchdog.updateAvailableNetworks(candidates);
282
283        // Ensure new networks have zero'ed failure counts
284        for (int i = 0; i < mSsids.length; i++) {
285            assertFailureCountEquals(mBssids[i], 0, 0, 0);
286        }
287
288        //Increment failure count for each network and failure type
289        int net = 0;
290        for (int i = 0; i < associationRejections; i++) {
291            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
292                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
293            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
294                    .get(mBssids[net]).associationRejection);
295        }
296        net = 1;
297        for (int i = 0; i < authenticationFailures; i++) {
298            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
299                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
300            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
301                    .get(mBssids[net]).authenticationFailure);
302        }
303        net = 2;
304        for (int i = 0; i < dhcpFailures; i++) {
305            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
306                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
307            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
308                    .get(mBssids[net]).dhcpFailure);
309        }
310        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
311        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
312        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
313        assertFailureCountEquals(mBssids[3], 0, 0, 0);
314    }
315
316    /**
317     * Case 6: Test failure counting, incrementing a specific BSSID, with some ephemeral networks
318     * Almost identical to test case 5.
319     * Test has 4 networks buffered (two are ephemeral), increment each different failure type on
320     * one of them.
321     * Expected behavior: See failure counts for the specific failures rise to the appropriate
322     * level for the specific network
323     */
324    @Test
325    public void testFailureCounting_countFailuresForSingleBssidWithEphemeral() throws Exception {
326        int associationRejections = 5;
327        int authenticationFailures = 9;
328        int dhcpFailures = 11;
329        boolean[] mIsEphemeral = {false, true, false, true};
330        // Buffer potential candidates 1,2,3 & 4
331        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
332                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
333        mLastResortWatchdog.updateAvailableNetworks(candidates);
334
335        // Ensure new networks have zero'ed failure counts
336        for (int i = 0; i < mSsids.length; i++) {
337            assertFailureCountEquals(mBssids[i], 0, 0, 0);
338        }
339
340        //Increment failure count for each network and failure type
341        int net = 0;
342        for (int i = 0; i < associationRejections; i++) {
343            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
344                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
345            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks()
346                    .get(mBssids[net]).associationRejection, i + 1);
347        }
348        net = 1;
349        for (int i = 0; i < authenticationFailures; i++) {
350            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
351                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
352            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks()
353                    .get(mBssids[net]).authenticationFailure, i + 1);
354        }
355        net = 2;
356        for (int i = 0; i < dhcpFailures; i++) {
357            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
358                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
359            assertEquals(mLastResortWatchdog.getRecentAvailableNetworks()
360                    .get(mBssids[net]).dhcpFailure, i + 1);
361        }
362        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
363        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
364        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
365        assertFailureCountEquals(mBssids[3], 0, 0, 0);
366    }
367
368    /**
369     * Case 7: Test failure counting, incrementing a specific BSSID but with the wrong SSID given
370     * Test has 4 networks buffered, increment each different failure type on one of them but using
371     * the wrong ssid.
372     * Expected behavior: Failure counts will remain at zero for all networks
373     */
374    @Test
375    public void testFailureCounting_countFailuresForSingleBssidWrongSsid() throws Exception {
376        String badSsid = "ItHertzWhenIP";
377        int associationRejections = 5;
378        int authenticationFailures = 9;
379        int dhcpFailures = 11;
380        // Buffer potential candidates 1,2,3 & 4
381        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
382                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
383        mLastResortWatchdog.updateAvailableNetworks(candidates);
384
385        // Ensure new networks have zero'ed failure counts
386        for (int i = 0; i < mSsids.length; i++) {
387            assertFailureCountEquals(mBssids[i], 0, 0, 0);
388        }
389
390        //Increment failure count for each network and failure type
391        int net = 0;
392        for (int i = 0; i < associationRejections; i++) {
393            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(badSsid, mBssids[net],
394                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
395        }
396        net = 1;
397        for (int i = 0; i < authenticationFailures; i++) {
398            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(badSsid, mBssids[net],
399                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
400        }
401        net = 2;
402        for (int i = 0; i < dhcpFailures; i++) {
403            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(badSsid, mBssids[net],
404                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
405        }
406
407        // Ensure all networks still have zero failure count
408        for (int i = 0; i < mSsids.length; i++) {
409            assertFailureCountEquals(mBssids[i], 0, 0, 0);
410        }
411    }
412
413    /**
414     * Case 8: Test failure counting, increment a bssid that does not exist
415     * Test has 4 networks buffered, increment each failure type, but using the wrong bssid
416     * Expected behavior: Failure counts will remain at zero for all networks
417     */
418    @Test
419    public void testFailureCounting_countFailuresForNonexistentBssid() throws Exception {
420        String badBssid = "de:ad:be:ee:e3:ef";
421        int associationRejections = 5;
422        int authenticationFailures = 9;
423        int dhcpFailures = 11;
424        // Buffer potential candidates 1,2,3 & 4
425        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
426                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
427        mLastResortWatchdog.updateAvailableNetworks(candidates);
428
429        // Ensure new networks have zero'ed failure counts
430        for (int i = 0; i < mSsids.length; i++) {
431            assertFailureCountEquals(mBssids[i], 0, 0, 0);
432        }
433
434        //Increment failure count for each network and failure type
435        int net = 0;
436        for (int i = 0; i < associationRejections; i++) {
437            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], badBssid,
438                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
439        }
440        net = 1;
441        for (int i = 0; i < authenticationFailures; i++) {
442            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], badBssid,
443                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
444        }
445        net = 2;
446        for (int i = 0; i < dhcpFailures; i++) {
447            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], badBssid,
448                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
449        }
450
451        // Ensure all networks still have zero failure count
452        for (int i = 0; i < mSsids.length; i++) {
453            assertFailureCountEquals(mBssids[i], 0, 0, 0);
454        }
455    }
456
457    /**
458     * Case 9: Test Failure Counting, using the "Any" BSSID
459     * Test has 4 buffered networks, two of which share the same SSID (different mBssids)
460     * Each failure type is incremented for the shared SSID, but with BSSID "any"
461     * Expected Behavior: Both networks increment their counts in tandem
462     */
463    @Test
464    public void testFailureCounting_countFailuresForAnyBssid() throws Exception {
465        String[] ssids = {"\"test1\"", "\"test2\"", "\"test1\"", "\"test4\""};
466        int associationRejections = 5;
467        int authenticationFailures = 9;
468        int dhcpFailures = 11;
469        // Buffer potential candidates 1,2,3 & 4
470        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
471                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
472        mLastResortWatchdog.updateAvailableNetworks(candidates);
473
474        // Ensure new networks have zero'ed failure counts
475        for (int i = 0; i < ssids.length; i++) {
476            assertFailureCountEquals(mBssids[i], 0, 0, 0);
477        }
478
479        //Increment failure count for each network and failure type
480        int net = 0;
481        for (int i = 0; i < associationRejections; i++) {
482            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
483                    ssids[0], WifiLastResortWatchdog.BSSID_ANY,
484                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
485        }
486        net = 1;
487        for (int i = 0; i < authenticationFailures; i++) {
488            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
489                    ssids[0], WifiLastResortWatchdog.BSSID_ANY,
490                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
491        }
492        net = 2;
493        for (int i = 0; i < dhcpFailures; i++) {
494            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
495                    ssids[0], WifiLastResortWatchdog.BSSID_ANY,
496                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
497        }
498        assertFailureCountEquals(mBssids[0], associationRejections, authenticationFailures,
499                dhcpFailures);
500        assertFailureCountEquals(mBssids[1], 0, 0, 0);
501        assertFailureCountEquals(mBssids[2], associationRejections, authenticationFailures,
502                dhcpFailures);
503        assertFailureCountEquals(mBssids[3], 0, 0, 0);
504    }
505
506    /**
507     * Case 10: Test Failure Counting, using the "Any" BSSID for nonexistent SSID
508     * Test has 4 buffered networks, two of which share the same SSID (different mBssids)
509     * Each failure type is incremented for a bad SSID (doesn't exist), but with BSSID "any"
510     * Expected Behavior: No Failures counted
511     */
512    @Test
513    public void testFailureCounting_countFailuresForAnyBssidNonexistentSsid() throws Exception {
514        int associationRejections = 5;
515        int authenticationFailures = 9;
516        int dhcpFailures = 11;
517        String badSsid = "DropItLikeIt'sHotSpot";
518        // Buffer potential candidates 1,2,3 & 4
519        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
520                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
521        mLastResortWatchdog.updateAvailableNetworks(candidates);
522
523        // Ensure new networks have zero'ed failure counts
524        for (int i = 0; i < mSsids.length; i++) {
525            assertFailureCountEquals(mBssids[i], 0, 0, 0);
526        }
527
528        //Increment failure count for each network and failure type
529        int net = 0;
530        for (int i = 0; i < associationRejections; i++) {
531            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
532                    badSsid, WifiLastResortWatchdog.BSSID_ANY,
533                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
534        }
535        net = 1;
536        for (int i = 0; i < authenticationFailures; i++) {
537            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
538                    badSsid, WifiLastResortWatchdog.BSSID_ANY,
539                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
540        }
541        net = 2;
542        for (int i = 0; i < dhcpFailures; i++) {
543            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
544                    badSsid, WifiLastResortWatchdog.BSSID_ANY,
545                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
546        }
547        // Check that all network failure counts are still zero
548        for (int i = 0; i < mSsids.length; i++) {
549            assertFailureCountEquals(mBssids[i], 0, 0, 0);
550        }
551    }
552
553    /**
554     * Case 11: Test Failure Counting, over failure Threshold check
555     * Test has 4 buffered networks, cause FAILURE_THRESHOLD failures for each failure type to one
556     * of each network (leaving one unfailed).
557     * Expected Behavior: 3 of the Available Networks report OverFailureThreshold
558     */
559    @Test
560    public void testFailureCounting_failureOverThresholdCheck() throws Exception {
561        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
562        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD;
563        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD;
564        // Buffer potential candidates 1,2,3 & 4
565        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
566                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
567        mLastResortWatchdog.updateAvailableNetworks(candidates);
568
569        // Ensure new networks have zero'ed failure counts
570        for (int i = 0; i < mSsids.length; i++) {
571            assertFailureCountEquals(mBssids[i], 0, 0, 0);
572        }
573
574        //Increment failure count for each network and failure type
575        int net = 0;
576        for (int i = 0; i < associationRejections; i++) {
577            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
578                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
579        }
580        net = 1;
581        for (int i = 0; i < authenticationFailures; i++) {
582            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
583                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
584        }
585        net = 2;
586        for (int i = 0; i < dhcpFailures; i++) {
587            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
588                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
589        }
590        assertEquals(true, mLastResortWatchdog.isOverFailureThreshold(mBssids[0]));
591        assertEquals(true, mLastResortWatchdog.isOverFailureThreshold(mBssids[1]));
592        assertEquals(true, mLastResortWatchdog.isOverFailureThreshold(mBssids[2]));
593        assertEquals(false, mLastResortWatchdog.isOverFailureThreshold(mBssids[3]));
594    }
595
596    /**
597     * Case 12: Test Failure Counting, under failure Threshold check
598     * Test has 4 buffered networks, cause FAILURE_THRESHOLD - 1 failures for each failure type to
599     * one of each network (leaving one unfailed).
600     * Expected Behavior: 0 of the Available Networks report OverFailureThreshold
601     */
602    @Test
603    public void testFailureCounting_failureUnderThresholdCheck() throws Exception {
604        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD - 1;
605        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD - 1;
606        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD - 1;
607        // Buffer potential candidates 1,2,3 & 4
608        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
609                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
610        mLastResortWatchdog.updateAvailableNetworks(candidates);
611
612        // Ensure new networks have zero'ed failure counts
613        for (int i = 0; i < mSsids.length; i++) {
614            assertFailureCountEquals(mBssids[i], 0, 0, 0);
615        }
616
617        //Increment failure count for each network and failure type
618        int net = 0;
619        for (int i = 0; i < associationRejections; i++) {
620            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
621                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
622        }
623        net = 1;
624        for (int i = 0; i < authenticationFailures; i++) {
625            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
626                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
627        }
628        net = 2;
629        for (int i = 0; i < dhcpFailures; i++) {
630            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
631                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
632        }
633        assertEquals(false, mLastResortWatchdog.isOverFailureThreshold(mBssids[0]));
634        assertEquals(false, mLastResortWatchdog.isOverFailureThreshold(mBssids[1]));
635        assertEquals(false, mLastResortWatchdog.isOverFailureThreshold(mBssids[2]));
636        assertEquals(false, mLastResortWatchdog.isOverFailureThreshold(mBssids[3]));
637    }
638
639    /**
640     * Case 13: Test Failure Counting, available network buffering does not affect counts
641     * In this test:
642     *   4 networks are buffered
643     *   Some number of failures are counted
644     *   networks are buffered again
645     * Expected Behavior: Failure counts are not modified by buffering
646     */
647    @Test
648    public void testAvailableNetworkBuffering_doesNotAffectFailureCounts() throws Exception {
649        int associationRejections = 5;
650        int authenticationFailures = 9;
651        int dhcpFailures = 11;
652        // Buffer potential candidates 1,2,3 & 4
653        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
654                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
655        mLastResortWatchdog.updateAvailableNetworks(candidates);
656
657        // Ensure new networks have zero'ed failure counts
658        for (int i = 0; i < mSsids.length; i++) {
659            assertFailureCountEquals(mBssids[i], 0, 0, 0);
660        }
661
662        //Increment failure count for each network and failure type
663        int net = 0;
664        for (int i = 0; i < associationRejections; i++) {
665            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
666                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
667            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
668                    .get(mBssids[net]).associationRejection);
669        }
670        net = 1;
671        for (int i = 0; i < authenticationFailures; i++) {
672            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
673                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
674            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
675                    .get(mBssids[net]).authenticationFailure);
676        }
677        net = 2;
678        for (int i = 0; i < dhcpFailures; i++) {
679            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
680                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
681            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
682                    .get(mBssids[net]).dhcpFailure);
683        }
684        // Check Each Network has appropriate failure count
685        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
686        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
687        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
688        assertFailureCountEquals(mBssids[3], 0, 0, 0);
689
690        // Re-buffer all networks
691        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) {
692            mLastResortWatchdog.updateAvailableNetworks(candidates);
693        }
694
695        // Check Each Network still has appropriate failure count
696        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
697        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
698        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
699        assertFailureCountEquals(mBssids[3], 0, 0, 0);
700    }
701
702    /**
703     * Case 14: Test Failure Counting, culling of an old network will remove its failure counts
704     * In this test:
705     *   4 networks are buffered
706     *   Some number of failures are counted for all networks
707     *   3 of the networks are buffered until the 4th dies of old age
708     *   The 4th network is re-buffered
709     * Expected Behavior: Failure counts for the 4th network are cleared after re-buffering
710     */
711    @Test
712    public void testAvailableNetworkBuffering_rebufferWipesCounts() throws Exception {
713        int associationRejections = 5;
714        int authenticationFailures = 9;
715        int dhcpFailures = 11;
716        // Buffer potential candidates 1,2,3 & 4
717        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
718                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
719        mLastResortWatchdog.updateAvailableNetworks(candidates);
720
721        // Ensure new networks have zero'ed failure counts
722        for (int i = 0; i < mSsids.length; i++) {
723            assertFailureCountEquals(mBssids[i], 0, 0, 0);
724        }
725
726        //Increment failure count for each network and failure type
727        int net = 0;
728        for (int i = 0; i < associationRejections; i++) {
729            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
730                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
731            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
732                    .get(mBssids[net]).associationRejection);
733        }
734        net = 1;
735        for (int i = 0; i < authenticationFailures; i++) {
736            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
737                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
738            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
739                    .get(mBssids[net]).authenticationFailure);
740        }
741        net = 2;
742        for (int i = 0; i < dhcpFailures; i++) {
743            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
744                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
745            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
746                    .get(mBssids[net]).dhcpFailure);
747        }
748        // Check Each Network has appropriate failure count
749        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
750        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
751        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
752        assertFailureCountEquals(mBssids[3], 0, 0, 0);
753
754        // Re-buffer all networks except 'test1' until it dies of old age
755        candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 1, 4),
756                Arrays.copyOfRange(mBssids, 1, 4),
757                Arrays.copyOfRange(mFrequencies, 1, 4),
758                Arrays.copyOfRange(mCaps, 1, 4),
759                Arrays.copyOfRange(mLevels, 1, 4),
760                Arrays.copyOfRange(mIsEphemeral, 1, 4));
761        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) {
762            mLastResortWatchdog.updateAvailableNetworks(candidates);
763        }
764        assertEquals(3, mLastResortWatchdog.getRecentAvailableNetworks().size());
765        // Re-buffer All networks, with 'test1' again
766        candidates = createFilteredQnsCandidates(mSsids,
767                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
768        mLastResortWatchdog.updateAvailableNetworks(candidates);
769
770        // Check Each Network has appropriate failure count (network 1 should be zero'd)
771        assertFailureCountEquals(mBssids[0], 0, 0, 0);
772        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
773        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
774        assertFailureCountEquals(mBssids[3], 0, 0, 0);
775    }
776
777    /**
778     * Case 26: Test Failure Counting, null failure incrementation
779     * In this test:
780     *   4 networks are buffered
781     *   Attempt to increment failures with null BSSID & SSID
782     * Expected behavior: Nothing breaks, no counts incremented
783     */
784    @Test
785    public void testFailureCounting_nullInputsNoBreaky() {
786        int associationRejections = 5;
787        int authenticationFailures = 9;
788        int dhcpFailures = 11;
789        // Buffer potential candidates 1,2,3 & 4
790        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
791                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
792        mLastResortWatchdog.updateAvailableNetworks(candidates);
793
794        // Ensure new networks have zero'ed failure counts
795        for (int i = 0; i < mSsids.length; i++) {
796            assertFailureCountEquals(mBssids[i], 0, 0, 0);
797        }
798
799        //Increment failure count for each network and failure type
800        int net = 0;
801        for (int i = 0; i < associationRejections; i++) {
802            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(null, mBssids[net],
803                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
804        }
805        net = 1;
806        for (int i = 0; i < authenticationFailures; i++) {
807            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], null,
808                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
809        }
810        net = 2;
811        for (int i = 0; i < dhcpFailures; i++) {
812            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(null, null,
813                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
814        }
815
816        // Ensure new networks have zero'ed failure counts
817        for (int i = 0; i < mSsids.length; i++) {
818            assertFailureCountEquals(mBssids[i], 0, 0, 0);
819        }
820    }
821
822    /**
823     * Case 27: Test Failure Counting, test all failures are counted across SSID
824     * In this test there are 8 networks,
825     * the first 4 networks have unique SSIDs amongst themselves,
826     * the last 4 networks share these SSIDs respectively, so there are 2 networks per SSID
827     * In this test we increment failure counts for the 'test1' ssid for a specific BSSID, and for
828     * the 'test2' ssid for BSSID_ANY.
829     * Expected behaviour: Failure counts for both networks on the same SSID are mirrored via both
830     * incrementation methods
831     */
832    @Test
833    public void testFailureCounting_countFailuresAcrossSsids() throws Exception {
834        String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\"",
835                "\"test1\"", "\"test2\"", "\"test3\"", "\"test4\""};
836        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
837                "c0:ff:ee:ee:e3:ee", "6c:f3:7f:ae:3c:f3", "6c:f3:7f:ae:3c:f4", "d3:ad:ba:b1:35:55",
838                "c0:ff:ee:ee:33:ee"};
839        int[] frequencies = {2437, 5180, 5180, 2437, 2437, 5180, 5180, 2437};
840        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
841                "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
842                "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
843        int[] levels = {-60, -86, -50, -62, -60, -86, -50, -62};
844        boolean[] isEphemeral = {false, false, false, false, false, false, false, false};
845        boolean[] hasEverConnected = {false, false, false, false, false, false, false,
846                false};
847        int firstNetFails = 13;
848        int secondNetFails = 8;
849        // Buffer potential candidates 1,2,3 & 4
850        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
851                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
852        mLastResortWatchdog.updateAvailableNetworks(candidates);
853
854        // Ensure new networks have zero'ed failure counts
855        for (int i = 0; i < ssids.length; i++) {
856            assertFailureCountEquals(bssids[i], 0, 0, 0);
857        }
858
859        //Increment failure count for the first test network ssid & bssid
860        for (int i = 0; i < firstNetFails; i++) {
861            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
862                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
863            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
864                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
865            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
866                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
867        }
868        //Increment failure count for the first test network ssid & BSSID_ANY
869        for (int i = 0; i < secondNetFails; i++) {
870            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
871                    ssids[1], WifiLastResortWatchdog.BSSID_ANY,
872                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
873            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
874                    ssids[1], WifiLastResortWatchdog.BSSID_ANY,
875                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
876            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
877                    ssids[1], WifiLastResortWatchdog.BSSID_ANY,
878                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
879        }
880        assertFailureCountEquals(bssids[0], firstNetFails, firstNetFails, firstNetFails);
881        assertFailureCountEquals(bssids[1], secondNetFails, secondNetFails, secondNetFails);
882        assertFailureCountEquals(bssids[2], 0, 0, 0);
883        assertFailureCountEquals(bssids[3], 0, 0, 0);
884        assertFailureCountEquals(bssids[4], firstNetFails, firstNetFails, firstNetFails);
885        assertFailureCountEquals(bssids[5], secondNetFails, secondNetFails, secondNetFails);
886        assertFailureCountEquals(bssids[6], 0, 0, 0);
887        assertFailureCountEquals(bssids[7], 0, 0, 0);
888    }
889
890    /**
891     * Case 15: Test failure counting, ensure failures still counted while connected
892     * Although failures should not occur while wifi is connected, race conditions are a thing, and
893     * I'd like the count to be incremented even while connected (Later test verifies that this
894     * can't cause a trigger though)
895     * Expected behavior: Failure counts increment like normal
896     */
897    @Test
898    public void testFailureCounting_wifiIsConnectedDoesNotAffectCounting() throws Exception {
899        int associationRejections = 5;
900        int authenticationFailures = 9;
901        int dhcpFailures = 11;
902
903        // Set Watchdogs internal wifi state tracking to 'connected'
904        mLastResortWatchdog.connectedStateTransition(true);
905
906        // Buffer potential candidates 1,2,3 & 4
907        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
908                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
909        mLastResortWatchdog.updateAvailableNetworks(candidates);
910
911        // Ensure new networks have zero'ed failure counts
912        for (int i = 0; i < mSsids.length; i++) {
913            assertFailureCountEquals(mBssids[i], 0, 0, 0);
914        }
915
916        //Increment failure count for each network and failure type
917        int net = 0;
918        for (int i = 0; i < associationRejections; i++) {
919            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
920                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
921            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
922                    .get(mBssids[net]).associationRejection);
923        }
924        net = 1;
925        for (int i = 0; i < authenticationFailures; i++) {
926            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
927                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
928            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
929                    .get(mBssids[net]).authenticationFailure);
930        }
931        net = 2;
932        for (int i = 0; i < dhcpFailures; i++) {
933            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
934                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
935            assertEquals(i + 1, mLastResortWatchdog.getRecentAvailableNetworks()
936                    .get(mBssids[net]).dhcpFailure);
937        }
938        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
939        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
940        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
941        assertFailureCountEquals(mBssids[3], 0, 0, 0);
942    }
943
944    /**
945     * Case 16: Test Failure Counting, entering ConnectedState clears all failure counts
946     * 4 Networks are buffered, cause various failures to 3 of them. Transition to ConnectedState
947     * Expected behavior: After transitioning, failure counts are reset to 0
948     */
949    @Test
950    public void testFailureCounting_enteringWifiConnectedStateClearsCounts() throws Exception {
951        int associationRejections = 5;
952        int authenticationFailures = 9;
953        int dhcpFailures = 11;
954
955        // Buffer potential candidates 1,2,3 & 4
956        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
957                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
958        mLastResortWatchdog.updateAvailableNetworks(candidates);
959
960        //Increment failure count for each network and failure type
961        int net = 0;
962        for (int i = 0; i < associationRejections; i++) {
963            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
964                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
965        }
966        net = 1;
967        for (int i = 0; i < authenticationFailures; i++) {
968            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
969                    WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
970        }
971        net = 2;
972        for (int i = 0; i < dhcpFailures; i++) {
973            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(mSsids[net], mBssids[net],
974                    WifiLastResortWatchdog.FAILURE_CODE_DHCP);
975        }
976
977        // Check that we have Failures
978        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
979        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
980        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
981
982        // Transition to 'ConnectedState'
983        mLastResortWatchdog.connectedStateTransition(true);
984
985        // Check that we have no failures
986        for (int i = 0; i < mSsids.length; i++) {
987            assertFailureCountEquals(mBssids[i], 0, 0, 0);
988        }
989    }
990
991    /**
992     * Case 17: Test Trigger Condition, only some networks over threshold
993     * We have 4 buffered networks, increment failure counts on 3 of them, until all 3 are over
994     * threshold.
995     * Expected Behavior: Watchdog does not trigger
996     */
997    @Test
998    public void testTriggerCondition_someNetworksOverFailureThreshold_allHaveEverConnected()
999            throws Exception {
1000        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1001        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1002        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1003        boolean[] hasEverConnected = {true, true, true, true};
1004
1005        // Buffer potential candidates 1,2,3 & 4
1006        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1007                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, hasEverConnected);
1008        mLastResortWatchdog.updateAvailableNetworks(candidates);
1009
1010        // Increment failure count for 3 networks and failure types, asserting each time that it
1011        // does not trigger, with only 3 over threshold
1012        boolean watchdogTriggered = false;
1013        int net = 0;
1014        for (int i = 0; i < associationRejections; i++) {
1015            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1016                mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1017            assertEquals(false, watchdogTriggered);
1018        }
1019        net = 1;
1020        for (int i = 0; i < authenticationFailures; i++) {
1021            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1022                mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1023            assertEquals(false, watchdogTriggered);
1024        }
1025        net = 2;
1026        for (int i = 0; i < dhcpFailures; i++) {
1027            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1028                mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1029            assertEquals(false, watchdogTriggered);
1030        }
1031
1032        // Check that we have Failures
1033        assertFailureCountEquals(mBssids[0], associationRejections, 0, 0);
1034        assertFailureCountEquals(mBssids[1], 0, authenticationFailures, 0);
1035        assertFailureCountEquals(mBssids[2], 0, 0, dhcpFailures);
1036
1037        // Add one more failure to one of the already over threshold networks, assert that it
1038        // does not trigger
1039        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1040                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1041        assertEquals(false, watchdogTriggered);
1042    }
1043
1044    /**
1045     * Case 18: Test Trigger Condition, watchdog fires once, then deactivates
1046     * In this test we have 4 networks, which we have connected to in the past. Failures are
1047     * incremented until all networks but one are over failure threshold, and then a few more times.
1048     *
1049     * Expected behavior: The watchdog triggers once as soon as all failures are over threshold,
1050     * but stops triggering for subsequent failures
1051     */
1052    @Test
1053    public void testTriggerCondition_allNetworksOverFailureThreshold_allHaveEverConnected()
1054            throws Exception {
1055        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1056        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1057        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1058        boolean[] hasEverConnected = {true, true, true, true};
1059
1060        // Buffer potential candidates 1,2,3 & 4
1061        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1062                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, hasEverConnected);
1063        mLastResortWatchdog.updateAvailableNetworks(candidates);
1064
1065        // Bring 3 of the 4 networks over failure Threshold without triggering watchdog
1066        boolean watchdogTriggered = false;
1067        int net = 0;
1068        for (int i = 0; i < associationRejections; i++) {
1069            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1070                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1071            assertEquals(false, watchdogTriggered);
1072        }
1073        net = 1;
1074        for (int i = 0; i < authenticationFailures; i++) {
1075            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1076                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1077            assertEquals(false, watchdogTriggered);
1078        }
1079        net = 2;
1080        for (int i = 0; i < dhcpFailures; i++) {
1081            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1082                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1083            assertEquals(false, watchdogTriggered);
1084        }
1085
1086        // Bring the remaining unfailed network upto 1 less than the failure threshold
1087        net = 3;
1088        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD - 1; i++) {
1089            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1090                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1091            assertEquals(false, watchdogTriggered);
1092        }
1093        // Increment failure count once more, check that watchdog triggered this time
1094        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1095                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1096        mLastResortWatchdog.updateAvailableNetworks(candidates);
1097        assertEquals(true, watchdogTriggered);
1098
1099        // Increment failure count 5 more times, watchdog should not trigger
1100        for (int i = 0; i < 5; i++) {
1101            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1102                        mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1103            assertEquals(false, watchdogTriggered);
1104        }
1105    }
1106
1107    /**
1108     * Case 19: Test Trigger Condition, all networks over failure threshold, one has ever connected
1109     * In this test we have 4 networks, only one has connected in the past. Failures are
1110     * incremented until all networks but one are over failure threshold, and then a few more times.
1111     *
1112     * Expected behavior: The watchdog triggers once as soon as all failures are over threshold,
1113     * but stops triggering for subsequent failures
1114     */
1115    @Test
1116    public void testTriggerCondition_allNetworksOverFailureThreshold_oneHaveEverConnected()
1117            throws Exception {
1118        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1119        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1120        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1121        boolean[] hasEverConnected = {false, true, false, false};
1122
1123        // Buffer potential candidates 1,2,3 & 4
1124        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1125                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, hasEverConnected);
1126        mLastResortWatchdog.updateAvailableNetworks(candidates);
1127
1128        // Bring 3 of the 4 networks over failure Threshold without triggering watchdog
1129        boolean watchdogTriggered = false;
1130        int net = 0;
1131        for (int i = 0; i < associationRejections; i++) {
1132            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1133                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1134            assertEquals(false, watchdogTriggered);
1135        }
1136        net = 1;
1137        for (int i = 0; i < authenticationFailures; i++) {
1138            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1139                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1140            assertEquals(false, watchdogTriggered);
1141        }
1142        net = 2;
1143        for (int i = 0; i < dhcpFailures; i++) {
1144            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1145                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1146            assertEquals(false, watchdogTriggered);
1147        }
1148
1149        // Bring the remaining unfailed network upto 1 less than the failure threshold
1150        net = 3;
1151        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD - 1; i++) {
1152            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1153                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1154            assertEquals(false, watchdogTriggered);
1155        }
1156        // Increment failure count once more, check that watchdog triggered this time
1157        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1158                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1159        mLastResortWatchdog.updateAvailableNetworks(candidates);
1160        assertEquals(true, watchdogTriggered);
1161
1162        // Increment failure count 5 more times, watchdog should not trigger
1163        for (int i = 0; i < 5; i++) {
1164            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1165                        mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1166            assertEquals(false, watchdogTriggered);
1167        }
1168    }
1169
1170    /**
1171     * Case 20: Test Trigger Condition, all networks over failure threshold, 0 have ever connected
1172     * In this test we have 4 networks, none have ever connected. Failures are
1173     * incremented until all networks but one are over failure threshold, and then a few more times.
1174     *
1175     * Expected behavior: The watchdog does not trigger
1176     */
1177    @Test
1178    public void testTriggerCondition_allNetworksOverFailureThreshold_zeroHaveEverConnected()
1179            throws Exception {
1180        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD + 1;
1181        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1182        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1183
1184        // Buffer potential candidates 1,2,3 & 4
1185        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1186                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
1187        mLastResortWatchdog.updateAvailableNetworks(candidates);
1188
1189        // Count failures on all 4 networks until all of them are over the failure threshold
1190        boolean watchdogTriggered = false;
1191        int net = 0;
1192        for (int i = 0; i < associationRejections; i++) {
1193            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1194                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1195            assertEquals(false, watchdogTriggered);
1196        }
1197        net = 1;
1198        for (int i = 0; i < authenticationFailures; i++) {
1199            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1200                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1201            assertEquals(false, watchdogTriggered);
1202        }
1203        net = 2;
1204        for (int i = 0; i < dhcpFailures; i++) {
1205            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1206                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1207            assertEquals(false, watchdogTriggered);
1208        }
1209        net = 3;
1210        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD + 1; i++) {
1211            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1212                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1213            assertEquals(false, watchdogTriggered);
1214        }
1215    }
1216
1217    /**
1218     * Case 21: Test Trigger Condition, Conditions right to trigger, but wifi is connected
1219     * In this test we have 4 networks, all have connected in the past
1220     * incremented until all networks but one are over failure threshold, and then a few more times.
1221     *
1222     * Expected behavior: The watchdog does not trigger
1223     */
1224    @Test
1225    public void testTriggerCondition_allNetworksOverFailureThreshold_isConnected()
1226        throws Exception {
1227        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD + 1;
1228        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1229        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1230
1231        // Buffer potential candidates 1,2,3 & 4
1232        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1233                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, mHasEverConnected);
1234        mLastResortWatchdog.updateAvailableNetworks(candidates);
1235
1236        // Set Watchdogs internal wifi state tracking to 'connected'
1237        mLastResortWatchdog.connectedStateTransition(true);
1238
1239        // Count failures on all 4 networks until all of them are over the failure threshold
1240        boolean watchdogTriggered = false;
1241        int net = 0;
1242        for (int i = 0; i < associationRejections; i++) {
1243            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1244                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1245            assertEquals(false, watchdogTriggered);
1246        }
1247        net = 1;
1248        for (int i = 0; i < authenticationFailures; i++) {
1249            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1250                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1251            assertEquals(false, watchdogTriggered);
1252        }
1253        net = 2;
1254        for (int i = 0; i < dhcpFailures; i++) {
1255            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1256                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1257            assertEquals(false, watchdogTriggered);
1258        }
1259        net = 3;
1260        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD + 1; i++) {
1261            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1262                    mSsids[net], mBssids[net], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1263            assertEquals(false, watchdogTriggered);
1264        }
1265    }
1266
1267    private void incrementFailuresUntilTrigger(String[] ssids, String[] bssids) {
1268        // Bring 3 of the 4 networks over failure Threshold without triggering watchdog
1269        boolean watchdogTriggered = false;
1270        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1271            for (int j = 0; j < ssids.length - 1; j++) {
1272                watchdogTriggered = mLastResortWatchdog
1273                        .noteConnectionFailureAndTriggerIfNeeded(ssids[j], bssids[j],
1274                        WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1275                assertEquals(false, watchdogTriggered);
1276            }
1277        }
1278        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD - 1; i++) {
1279            watchdogTriggered = mLastResortWatchdog
1280                    .noteConnectionFailureAndTriggerIfNeeded(ssids[ssids.length - 1],
1281                    bssids[ssids.length - 1], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1282            assertEquals(false, watchdogTriggered);
1283        }
1284
1285        // Increment failure count once more, check that watchdog triggered this time
1286        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1287                    ssids[ssids.length - 1], bssids[ssids.length - 1],
1288                    WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1289        assertEquals(true, watchdogTriggered);
1290        verify(mSelfRecovery).trigger(eq(SelfRecovery.REASON_LAST_RESORT_WATCHDOG));
1291        reset(mSelfRecovery);
1292    }
1293
1294    /**
1295     * Case 22: Test enabling/disabling of Watchdog Trigger, disabled after triggering
1296     * In this test, we have 4 networks. Increment failures until Watchdog triggers. Increment some
1297     * more failures.
1298     * Expected behavior: Watchdog trigger gets deactivated after triggering, and stops triggering
1299     */
1300    @Test
1301    public void testTriggerEnabling_disabledAfterTriggering() throws Exception {
1302        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1303        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1304        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1305        boolean[] hasEverConnected = {false, true, false, false};
1306
1307        // Buffer potential candidates 1,2,3 & 4
1308        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1309                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, hasEverConnected);
1310        mLastResortWatchdog.updateAvailableNetworks(candidates);
1311
1312        incrementFailuresUntilTrigger(mSsids, mBssids);
1313
1314        // Increment failure count 5 more times, watchdog should not trigger
1315        for (int i = 0; i < 5; i++) {
1316            boolean watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1317                        mSsids[3], mBssids[3], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1318            assertEquals(false, watchdogTriggered);
1319        }
1320    }
1321
1322    /**
1323     * Case 23: Test enabling/disabling of Watchdog Trigger, trigger re-enabled after connecting
1324     * In this test, we have 4 networks. Increment failures until Watchdog triggers and deactivates,
1325     * transition wifi to connected state, then increment failures until all networks over threshold
1326     * Expected behavior: Watchdog able to trigger again after transitioning to and from connected
1327     * state
1328     */
1329    @Test
1330    public void testTriggerEnabling_enabledAfterConnecting() throws Exception {
1331        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1332        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1333        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1334        boolean[] hasEverConnected = {false, true, false, false};
1335        boolean watchdogTriggered;
1336        // Buffer potential candidates 1,2,3 & 4
1337        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(mSsids,
1338                mBssids, mFrequencies, mCaps, mLevels, mIsEphemeral, hasEverConnected);
1339        mLastResortWatchdog.updateAvailableNetworks(candidates);
1340
1341        incrementFailuresUntilTrigger(mSsids, mBssids);
1342
1343        // Increment failure count 5 more times, ensure trigger is deactivated
1344        for (int i = 0; i < 5; i++) {
1345            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1346                        mSsids[3], mBssids[3], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1347            mLastResortWatchdog.updateAvailableNetworks(candidates);
1348            assertEquals(false, watchdogTriggered);
1349        }
1350
1351        // transition Watchdog wifi state tracking to 'connected' then back to 'disconnected'
1352        mLastResortWatchdog.connectedStateTransition(true);
1353        mLastResortWatchdog.connectedStateTransition(false);
1354
1355        // Fail 3/4 networks until they're over threshold
1356        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD + 1; i++) {
1357            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1358                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1359            assertEquals(false, watchdogTriggered);
1360            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1361                    mSsids[1], mBssids[1], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1362            assertEquals(false, watchdogTriggered);
1363            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1364                    mSsids[2], mBssids[2], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1365            assertEquals(false, watchdogTriggered);
1366        }
1367
1368        // Bring the remaining unfailed network upto 1 less than the failure threshold
1369        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD - 1; i++) {
1370            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1371                    mSsids[3], mBssids[3], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1372            assertEquals(false, watchdogTriggered);
1373        }
1374        // Increment failure count once more, check that watchdog triggered this time
1375        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1376                    mSsids[3], mBssids[3], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1377        assertEquals(true, watchdogTriggered);
1378    }
1379
1380    /**
1381     * Case 24: Test enabling/disabling of Watchdog Trigger, trigger re-enabled after new network
1382     * In this test, we have 3 networks. Increment failures until Watchdog triggers and deactivates,
1383     * we then buffer a new network (network 4), then increment failures until all networks over
1384     * threshold Expected behavior: Watchdog able to trigger again after discovering a new network
1385     */
1386    @Test
1387    public void testTriggerEnabling_enabledAfterNewNetwork() {
1388        int associationRejections = WifiLastResortWatchdog.FAILURE_THRESHOLD;
1389        int authenticationFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 2;
1390        int dhcpFailures = WifiLastResortWatchdog.FAILURE_THRESHOLD + 3;
1391        boolean[] hasEverConnected = {false, true, false, false};
1392        boolean watchdogTriggered;
1393
1394        // Buffer potential candidates 1,2,3
1395        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(
1396                Arrays.copyOfRange(mSsids, 0, 3),
1397                Arrays.copyOfRange(mBssids, 0, 3),
1398                Arrays.copyOfRange(mFrequencies, 0, 3),
1399                Arrays.copyOfRange(mCaps, 0, 3),
1400                Arrays.copyOfRange(mLevels, 0, 3),
1401                Arrays.copyOfRange(mIsEphemeral, 0, 3),
1402                Arrays.copyOfRange(hasEverConnected, 0, 3));
1403        mLastResortWatchdog.updateAvailableNetworks(candidates);
1404
1405        incrementFailuresUntilTrigger(Arrays.copyOfRange(mSsids, 0, 3),
1406                Arrays.copyOfRange(mBssids, 0, 3));
1407
1408        // Increment failure count 5 more times, ensure trigger is deactivated
1409        for (int i = 0; i < 5; i++) {
1410            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1411                        mSsids[2], mBssids[2], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1412            mLastResortWatchdog.updateAvailableNetworks(candidates);
1413            assertEquals(false, watchdogTriggered);
1414        }
1415
1416        candidates = createFilteredQnsCandidates(mSsids, mBssids, mFrequencies, mCaps, mLevels,
1417                mIsEphemeral, hasEverConnected);
1418        mLastResortWatchdog.updateAvailableNetworks(candidates);
1419
1420        incrementFailuresUntilTrigger(mSsids, mBssids);
1421
1422    }
1423
1424    /**
1425     * Case 28: Test Metrics collection
1426     * Setup 5 networks (unique SSIDs). Fail them until watchdog triggers, with 1 network failing
1427     * association, 1 failing authentication, 2 failing dhcp and one failing both authentication and
1428     * dhcp, (over threshold for all these failures)
1429     * Expected behavior: Metrics are updated as follows
1430     *  Triggers++
1431     *  # of Networks += 5
1432     *  Triggers with Bad association++
1433     *  Triggers with Bad authentication++
1434     *  Triggers with Bad dhcp++
1435     *  Number of networks with bad association += 1
1436     *  Number of networks with bad authentication += 2
1437     *  Number of networks with bad dhcp += 3
1438     */
1439    @Test
1440    public void testMetricsCollection() {
1441        String[] ssids = {"\"test1\"", "\"test2\"", "\"test3\"", "\"test4\"", "\"test5\""};
1442        String[] bssids = {"6c:f3:7f:ae:8c:f3", "6c:f3:7f:ae:8c:f4", "de:ad:ba:b1:e5:55",
1443                "c0:ff:ee:ee:e3:ee", "6c:f3:7f:ae:3c:f3"};
1444        int[] frequencies = {2437, 5180, 5180, 2437, 2437};
1445        String[] caps = {"[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]",
1446                "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]", "[WPA2-EAP-CCMP][ESS]"};
1447        int[] levels = {-60, -86, -50, -62, -60};
1448        boolean[] isEphemeral = {false, false, false, false, false};
1449        boolean[] hasEverConnected = {true, false, false, false, false};
1450        // Buffer potential candidates 1,2,3,4 & 5
1451        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
1452                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
1453        mLastResortWatchdog.updateAvailableNetworks(candidates);
1454
1455        // Ensure new networks have zero'ed failure counts
1456        for (int i = 0; i < ssids.length; i++) {
1457            assertFailureCountEquals(bssids[i], 0, 0, 0);
1458        }
1459
1460        final long timeAtFailure = 100;
1461        final long timeAtReconnect = 5000;
1462        final long expectedDuration = timeAtReconnect - timeAtFailure;
1463        when(mClock.getElapsedSinceBootMillis()).thenReturn(timeAtFailure, timeAtReconnect);
1464
1465        //Increment failure counts
1466        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1467            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1468                    ssids[1], bssids[1], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1469            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1470                    ssids[2], bssids[2], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1471            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1472                    ssids[3], bssids[3], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1473            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1474                    ssids[4], bssids[4], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1475            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1476                    ssids[4], bssids[4], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1477            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1478                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1479        }
1480
1481        // Verify relevant WifiMetrics calls were made once with appropriate arguments
1482        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers();
1483        verify(mWifiMetrics, times(1)).addCountToNumLastResortWatchdogAvailableNetworksTotal(5);
1484        verify(mWifiMetrics, times(1))
1485                .addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(2);
1486        verify(mWifiMetrics, times(1))
1487                .incrementNumLastResortWatchdogTriggersWithBadAuthentication();
1488        verify(mWifiMetrics, times(1))
1489                .addCountToNumLastResortWatchdogBadAssociationNetworksTotal(1);
1490        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggersWithBadAssociation();
1491        verify(mWifiMetrics, times(1)).addCountToNumLastResortWatchdogBadDhcpNetworksTotal(3);
1492        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggersWithBadDhcp();
1493
1494        // Simulate wifi connecting after triggering
1495        mLastResortWatchdog.connectedStateTransition(true);
1496
1497        // Verify that WifiMetrics counted this as a Watchdog success
1498        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses();
1499        verify(mWifiMetrics, times(1)).setWatchdogSuccessTimeDurationMs(eq(expectedDuration));
1500
1501        // Verify takeBugReport is called
1502        mLooper.dispatchAll();
1503        verify(mWifiStateMachine, times(1)).takeBugReport(anyString(), anyString());
1504
1505        // Simulate wifi disconnecting
1506        mLastResortWatchdog.connectedStateTransition(false);
1507
1508        // Verify that WifiMetrics has still only counted one success
1509        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses();
1510        verify(mWifiMetrics, times(1)).setWatchdogSuccessTimeDurationMs(eq(expectedDuration));
1511        // Verify takeBugReport not called again
1512        mLooper.dispatchAll();
1513        verify(mWifiStateMachine, times(1)).takeBugReport(anyString(), anyString());
1514
1515        // Remove the fifth network from candidates
1516        candidates = createFilteredQnsCandidates(Arrays.copyOfRange(mSsids, 0, 4),
1517            Arrays.copyOfRange(mBssids, 0, 4),
1518            Arrays.copyOfRange(mFrequencies, 0, 4),
1519            Arrays.copyOfRange(mCaps, 0, 4),
1520            Arrays.copyOfRange(mLevels, 0, 4),
1521            Arrays.copyOfRange(mIsEphemeral, 0, 4));
1522
1523        // Age out the fifth network
1524        for (int i = 0; i < WifiLastResortWatchdog.MAX_BSSID_AGE; i++) {
1525            mLastResortWatchdog.updateAvailableNetworks(candidates);
1526        }
1527
1528        //Increment failure counts
1529        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1530            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1531                    ssids[1], bssids[1], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1532            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1533                    ssids[2], bssids[2], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1534            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1535                    ssids[3], bssids[3], WifiLastResortWatchdog.FAILURE_CODE_DHCP);
1536            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1537                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1538        }
1539
1540        // Add network #5 back into the candidates
1541        candidates = createFilteredQnsCandidates(ssids,
1542                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
1543
1544        // LastResortWatchdog should reactivate because there is a new network (#5) available,
1545        // Not because it was successful
1546        mLastResortWatchdog.updateAvailableNetworks(candidates);
1547
1548        // Simulate wifi connecting
1549        mLastResortWatchdog.connectedStateTransition(true);
1550
1551        // Verify that WifiMetrics did not count another success, as the connection could be due
1552        // to the newly available network #5
1553        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses();
1554        verify(mWifiMetrics, times(1)).setWatchdogSuccessTimeDurationMs(eq(expectedDuration));
1555    }
1556
1557    /**
1558     * Case 21: Test config updates where new config is null.
1559     * Create a scan result with an associated config and update the available networks list.
1560     * Repeat this with a second scan result where the config is null.
1561     * Expected behavior: The stored config should not be lost overwritten.
1562     */
1563    @Test
1564    public void testUpdateNetworkWithNullConfig() {
1565        List<Pair<ScanDetail, WifiConfiguration>> candidates =
1566                new ArrayList<Pair<ScanDetail, WifiConfiguration>>();
1567        String ssid = mSsids[0].replaceAll("^\"+", "").replaceAll("\"+$", "");
1568        ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssid),
1569                mBssids[0], mCaps[0], mLevels[0], mFrequencies[0], System.currentTimeMillis(), 0);
1570        WifiConfiguration config = mock(WifiConfiguration.class);
1571        WifiConfiguration.NetworkSelectionStatus networkSelectionStatus =
1572                mock(WifiConfiguration.NetworkSelectionStatus.class);
1573        when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStatus);
1574        when(networkSelectionStatus.getHasEverConnected())
1575                .thenReturn(true);
1576        candidates.add(Pair.create(scanDetail, config));
1577        mLastResortWatchdog.updateAvailableNetworks(candidates);
1578
1579        candidates.clear();
1580
1581        candidates.add(Pair.create(scanDetail, null));
1582        mLastResortWatchdog.updateAvailableNetworks(candidates);
1583
1584        boolean watchdogTriggered = false;
1585        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1586            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1587                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1588        }
1589        assertEquals(true, watchdogTriggered);
1590    }
1591
1592    /**
1593     * Case 22: Test config updates where hasEverConnected goes from false to true.
1594     * Create a scan result with an associated config and update the available networks list.
1595     * Repeat this with a second scan result where the config value for hasEverConnected
1596     * is true.
1597     * Expected behavior: The stored config should not be lost overwritten.
1598     */
1599    @Test
1600    public void testUpdateNetworkWithHasEverConnectedTrue() {
1601        List<Pair<ScanDetail, WifiConfiguration>> candidates =
1602                new ArrayList<Pair<ScanDetail, WifiConfiguration>>();
1603        String ssid = mSsids[0].replaceAll("^\"+", "").replaceAll("\"+$", "");
1604        ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssid),
1605                mBssids[0], mCaps[0], mLevels[0], mFrequencies[0], System.currentTimeMillis(), 0);
1606        WifiConfiguration configHasEverConnectedFalse = mock(WifiConfiguration.class);
1607        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusFalse =
1608                mock(WifiConfiguration.NetworkSelectionStatus.class);
1609        when(configHasEverConnectedFalse.getNetworkSelectionStatus())
1610                .thenReturn(networkSelectionStatusFalse);
1611        when(networkSelectionStatusFalse.getHasEverConnected())
1612                .thenReturn(false);
1613        candidates.add(Pair.create(scanDetail, configHasEverConnectedFalse));
1614        mLastResortWatchdog.updateAvailableNetworks(candidates);
1615
1616        boolean watchdogTriggered = false;
1617        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1618            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1619                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1620        }
1621        assertEquals(false, watchdogTriggered);
1622
1623        candidates.clear();
1624
1625        WifiConfiguration configHasEverConnectedTrue = mock(WifiConfiguration.class);
1626        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusTrue =
1627                mock(WifiConfiguration.NetworkSelectionStatus.class);
1628        when(configHasEverConnectedTrue.getNetworkSelectionStatus())
1629                .thenReturn(networkSelectionStatusTrue);
1630        when(networkSelectionStatusTrue.getHasEverConnected())
1631                .thenReturn(true);
1632        candidates.add(Pair.create(scanDetail, configHasEverConnectedTrue));
1633        mLastResortWatchdog.updateAvailableNetworks(candidates);
1634
1635        watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1636                mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1637        assertEquals(true, watchdogTriggered);
1638    }
1639
1640    /**
1641     * Case 23: Test config updates where hasEverConnected goes from true to false.
1642     * Create a scan result with an associated config and update the available networks list.
1643     * Repeat this with a second scan result where hasEverConnected is false.
1644     * Expected behavior: The stored config should not be lost overwritten.
1645     */
1646    @Test
1647    public void testUpdateNetworkWithHasEverConnectedFalse() {
1648        List<Pair<ScanDetail, WifiConfiguration>> candidates =
1649                new ArrayList<Pair<ScanDetail, WifiConfiguration>>();
1650        String ssid = mSsids[0].replaceAll("^\"+", "").replaceAll("\"+$", "");
1651        ScanDetail scanDetail = new ScanDetail(WifiSsid.createFromAsciiEncoded(ssid),
1652                mBssids[0], mCaps[0], mLevels[0], mFrequencies[0], System.currentTimeMillis(), 0);
1653
1654        WifiConfiguration configHasEverConnectedTrue = mock(WifiConfiguration.class);
1655        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusTrue =
1656                mock(WifiConfiguration.NetworkSelectionStatus.class);
1657        when(configHasEverConnectedTrue.getNetworkSelectionStatus())
1658                .thenReturn(networkSelectionStatusTrue);
1659        when(networkSelectionStatusTrue.getHasEverConnected())
1660                .thenReturn(true);
1661        candidates.add(Pair.create(scanDetail, configHasEverConnectedTrue));
1662        mLastResortWatchdog.updateAvailableNetworks(candidates);
1663
1664        boolean watchdogTriggered = false;
1665        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1666            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1667                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1668        }
1669        assertEquals(true, watchdogTriggered);
1670
1671        candidates.clear();
1672
1673        WifiConfiguration configHasEverConnectedFalse = mock(WifiConfiguration.class);
1674        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusFalse =
1675                mock(WifiConfiguration.NetworkSelectionStatus.class);
1676        when(configHasEverConnectedFalse.getNetworkSelectionStatus())
1677                .thenReturn(networkSelectionStatusFalse);
1678        when(networkSelectionStatusFalse.getHasEverConnected())
1679                .thenReturn(false);
1680        candidates.add(Pair.create(scanDetail, configHasEverConnectedFalse));
1681        mLastResortWatchdog.updateAvailableNetworks(candidates);
1682
1683        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1684            watchdogTriggered = mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1685                    mSsids[0], mBssids[0], WifiLastResortWatchdog.FAILURE_CODE_AUTHENTICATION);
1686        }
1687        assertEquals(false, watchdogTriggered);
1688    }
1689
1690    /**
1691     * Case 24: Check toString method for accurate hasEverConnected value in
1692     * AvailableNetworkFailureCount objects.
1693     * Create an AvailableNetworkFailureCount instance and check output of toString method.
1694     * Expected behavior:  String contains HasEverConnected setting or null_config if there is not
1695     * an associated config.
1696     */
1697    @Test
1698    public void testHasEverConnectedValueInAvailableNetworkFailureCountToString() {
1699        // Check with HasEverConnected true
1700        WifiConfiguration configHasEverConnectedTrue = mock(WifiConfiguration.class);
1701        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusTrue =
1702                mock(WifiConfiguration.NetworkSelectionStatus.class);
1703        when(configHasEverConnectedTrue.getNetworkSelectionStatus())
1704                .thenReturn(networkSelectionStatusTrue);
1705        when(networkSelectionStatusTrue.getHasEverConnected()).thenReturn(true);
1706        WifiLastResortWatchdog.AvailableNetworkFailureCount withConfigHECTrue =
1707                new WifiLastResortWatchdog.AvailableNetworkFailureCount(configHasEverConnectedTrue);
1708        String output = withConfigHECTrue.toString();
1709        assertTrue(output.contains("HasEverConnected: true"));
1710
1711        // check with HasEverConnected false
1712        WifiConfiguration configHasEverConnectedFalse = mock(WifiConfiguration.class);
1713        WifiConfiguration.NetworkSelectionStatus networkSelectionStatusFalse =
1714                mock(WifiConfiguration.NetworkSelectionStatus.class);
1715        when(configHasEverConnectedFalse.getNetworkSelectionStatus())
1716                .thenReturn(networkSelectionStatusFalse);
1717        when(networkSelectionStatusFalse.getHasEverConnected()).thenReturn(false);
1718        WifiLastResortWatchdog.AvailableNetworkFailureCount withConfigHECFalse =
1719                new WifiLastResortWatchdog.AvailableNetworkFailureCount(
1720                        configHasEverConnectedFalse);
1721        output = withConfigHECFalse.toString();
1722        assertTrue(output.contains("HasEverConnected: false"));
1723
1724        // Check with a null config
1725        WifiLastResortWatchdog.AvailableNetworkFailureCount withNullConfig =
1726                new WifiLastResortWatchdog.AvailableNetworkFailureCount(null);
1727        output = withNullConfig.toString();
1728        assertTrue(output.contains("HasEverConnected: null_config"));
1729    }
1730
1731    /**
1732     * Test metrics incrementing connection failure count after watchdog has already been triggered
1733     */
1734    @Test
1735    public void testIncrementingWatchdogConnectionFailuresAfterTrigger() {
1736        String[] ssids = {"\"test1\""};
1737        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
1738        int[] frequencies = {2437};
1739        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
1740        int[] levels = {-60};
1741        boolean[] isEphemeral = {false};
1742        boolean[] hasEverConnected = {true};
1743        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
1744                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
1745        mLastResortWatchdog.updateAvailableNetworks(candidates);
1746
1747        // Ensure new networks have zero'ed failure counts
1748        for (int i = 0; i < ssids.length; i++) {
1749            assertFailureCountEquals(bssids[i], 0, 0, 0);
1750        }
1751
1752        //Increment failure counts
1753        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1754            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1755                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1756        }
1757
1758        // Verify relevant WifiMetrics calls were made once with appropriate arguments
1759        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers();
1760
1761        // Verify that failure count after trigger is not incremented yet
1762        verify(mWifiMetrics, never()).incrementWatchdogTotalConnectionFailureCountAfterTrigger();
1763
1764        // Fail 1 more time and verify this time it's counted
1765        mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1766                ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1767        verify(mWifiMetrics, times(1)).incrementWatchdogTotalConnectionFailureCountAfterTrigger();
1768    }
1769
1770    /**
1771     * Test that LRWD success is only declared when the first connection after restarting wifi
1772     * is successful.
1773     *
1774     * First tests the failure case: check success metric is not incremented when the first
1775     * connection is a failure.
1776     * Then test state transition and the success case: check success metric is incremented
1777     * when the first connection is a success.
1778     */
1779    @Test
1780    public void testWatchdogAssumesSuccessOnlyIfFirstConnectionAfterRestartSucceeds() {
1781        String[] ssids = {"\"test1\""};
1782        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
1783        int[] frequencies = {2437};
1784        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
1785        int[] levels = {-60};
1786        boolean[] isEphemeral = {false};
1787        boolean[] hasEverConnected = {true};
1788        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
1789                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
1790        mLastResortWatchdog.updateAvailableNetworks(candidates);
1791
1792        // Ensure new networks have zero'ed failure counts
1793        for (int i = 0; i < ssids.length; i++) {
1794            assertFailureCountEquals(bssids[i], 0, 0, 0);
1795        }
1796
1797        //Increment failure counts
1798        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1799            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1800                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1801        }
1802
1803        // Verify watchdog has triggered a restart
1804        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers();
1805
1806        // Fail 1 more time and verify this time it's counted
1807        mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1808                ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1809
1810        // Simulate wifi connecting after triggering
1811        mLastResortWatchdog.connectedStateTransition(true);
1812        // Verify takeBugReport is not called again
1813        mLooper.dispatchAll();
1814        verify(mWifiStateMachine, never()).takeBugReport(anyString(), anyString());
1815        verify(mWifiMetrics, never()).incrementNumLastResortWatchdogSuccesses();
1816
1817        // Simulate wifi disconnecting
1818        mLastResortWatchdog.connectedStateTransition(false);
1819
1820        // Test another round, and this time successfully connect after restart trigger
1821        for (int i = 0; i < ssids.length; i++) {
1822            assertFailureCountEquals(bssids[i], 0, 0, 0);
1823        }
1824        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1825            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1826                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1827        }
1828
1829        // Verify watchdog has triggered a restart
1830        verify(mWifiMetrics, times(2)).incrementNumLastResortWatchdogTriggers();
1831        // Simulate wifi connecting after triggering
1832        mLastResortWatchdog.connectedStateTransition(true);
1833        // Verify takeBugReport is not called again
1834        mLooper.dispatchAll();
1835        verify(mWifiStateMachine, times(1)).takeBugReport(anyString(), anyString());
1836        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogSuccesses();
1837    }
1838
1839    /**
1840     * If the user changes the configuration and then we have a successful connection, don't
1841     * trigger bugreport.
1842     * Tests this specific path:
1843     * 1. watchdog triggers restart
1844     * 2. wifi configuration changes
1845     * 3. wifi successfully connects immedietly after
1846     * Expected result: bugreport should not trigger
1847     */
1848    @Test
1849    public void testWatchdogVerifiesAtLeastOneNetworkIsConnectedBeforeTriggeringBugreport() {
1850        String[] ssids = {"\"test1\""};
1851        String[] bssids = {"6c:f3:7f:ae:8c:f3"};
1852        int[] frequencies = {2437};
1853        String[] caps = {"[WPA2-EAP-CCMP][ESS]"};
1854        int[] levels = {-60};
1855        boolean[] isEphemeral = {false};
1856        boolean[] hasEverConnected = {true};
1857        List<Pair<ScanDetail, WifiConfiguration>> candidates = createFilteredQnsCandidates(ssids,
1858                bssids, frequencies, caps, levels, isEphemeral, hasEverConnected);
1859        mLastResortWatchdog.updateAvailableNetworks(candidates);
1860
1861        // Ensure new networks have zero'ed failure counts
1862        for (int i = 0; i < ssids.length; i++) {
1863            assertFailureCountEquals(bssids[i], 0, 0, 0);
1864        }
1865
1866        //Increment failure counts
1867        for (int i = 0; i < WifiLastResortWatchdog.FAILURE_THRESHOLD; i++) {
1868            mLastResortWatchdog.noteConnectionFailureAndTriggerIfNeeded(
1869                    ssids[0], bssids[0], WifiLastResortWatchdog.FAILURE_CODE_ASSOCIATION);
1870        }
1871
1872        // Verify watchdog has triggered a restart
1873        verify(mWifiMetrics, times(1)).incrementNumLastResortWatchdogTriggers();
1874
1875        // Simulate user changing the configuration
1876        when(candidates.get(0).second.getNetworkSelectionStatus().getHasEverConnected())
1877                .thenReturn(false);
1878
1879        mLastResortWatchdog.connectedStateTransition(true);
1880        // Verify takeBugReport is not called again
1881        mLooper.dispatchAll();
1882        verify(mWifiStateMachine, never()).takeBugReport(anyString(), anyString());
1883        verify(mWifiMetrics, never()).incrementNumLastResortWatchdogSuccesses();
1884    }
1885}
1886