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 */
16package com.android.server.wifi;
17
18import static org.junit.Assert.assertEquals;
19import static org.junit.Assert.assertTrue;
20import static org.mockito.Mockito.*;
21
22import android.net.NetworkAgent;
23import android.net.wifi.ScanResult;
24import android.net.wifi.SupplicantState;
25import android.net.wifi.WifiConfiguration;
26import android.net.wifi.WifiManager;
27import android.net.wifi.WifiSsid;
28import android.os.Handler;
29import android.os.test.TestLooper;
30import android.test.suitebuilder.annotation.SmallTest;
31import android.util.Base64;
32
33
34import com.android.server.wifi.hotspot2.NetworkDetail;
35import com.android.server.wifi.nano.WifiMetricsProto;
36import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
37
38import org.junit.Before;
39import org.junit.Test;
40import org.mockito.Mock;
41import org.mockito.MockitoAnnotations;
42
43import java.io.ByteArrayOutputStream;
44import java.io.FileDescriptor;
45import java.io.PrintWriter;
46import java.io.StringWriter;
47import java.util.ArrayList;
48import java.util.BitSet;
49import java.util.List;
50import java.util.regex.Matcher;
51import java.util.regex.Pattern;
52
53/**
54 * Unit tests for {@link com.android.server.wifi.WifiMetrics}.
55 */
56@SmallTest
57public class WifiMetricsTest {
58
59    WifiMetrics mWifiMetrics;
60    WifiMetricsProto.WifiLog mDeserializedWifiMetrics;
61    TestLooper mTestLooper;
62    @Mock Clock mClock;
63
64    @Before
65    public void setUp() throws Exception {
66        MockitoAnnotations.initMocks(this);
67        mDeserializedWifiMetrics = null;
68        when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
69        mTestLooper = new TestLooper();
70        mWifiMetrics = new WifiMetrics(mClock, mTestLooper.getLooper());
71    }
72
73    /**
74     * Test that startConnectionEvent and endConnectionEvent can be called repeatedly and out of
75     * order. Only tests no exception occurs. Creates 3 ConnectionEvents.
76     */
77    @Test
78    public void startAndEndConnectionEventSucceeds() throws Exception {
79        //Start and end Connection event
80        mWifiMetrics.startConnectionEvent(null, "RED",
81                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
82        mWifiMetrics.endConnectionEvent(
83                WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
84                WifiMetricsProto.ConnectionEvent.HLF_DHCP);
85        //end Connection event without starting one
86        mWifiMetrics.endConnectionEvent(
87                WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
88                WifiMetricsProto.ConnectionEvent.HLF_DHCP);
89        //start two ConnectionEvents in a row
90        mWifiMetrics.startConnectionEvent(null, "BLUE",
91                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
92        mWifiMetrics.startConnectionEvent(null, "GREEN",
93                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
94    }
95
96    private static final long TEST_RECORD_DURATION_SEC = 12 * 60 * 60;
97    private static final long TEST_RECORD_DURATION_MILLIS = TEST_RECORD_DURATION_SEC * 1000;
98
99    /**
100     * Simulate how dumpsys gets the proto from mWifiMetrics, filter the proto bytes out and
101     * deserialize them into mDeserializedWifiMetrics
102     */
103    public void dumpProtoAndDeserialize() throws Exception {
104        ByteArrayOutputStream stream = new ByteArrayOutputStream();
105        PrintWriter writer = new PrintWriter(stream);
106        String[] args = new String[0];
107
108        when(mClock.getElapsedSinceBootMillis()).thenReturn(TEST_RECORD_DURATION_MILLIS);
109        //Test proto dump, by passing in proto arg option
110        args = new String[]{WifiMetrics.PROTO_DUMP_ARG};
111        mWifiMetrics.dump(null, writer, args);
112        writer.flush();
113        Pattern pattern = Pattern.compile(
114                "(?<=WifiMetrics:\\n)([\\s\\S]*)(?=EndWifiMetrics)");
115        Matcher matcher = pattern.matcher(stream.toString());
116        assertTrue("Proto Byte string found in WifiMetrics.dump():\n" + stream.toString(),
117                matcher.find());
118        String protoByteString = matcher.group(1);
119        byte[] protoBytes = Base64.decode(protoByteString, Base64.DEFAULT);
120        mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
121    }
122
123    /**
124     * Gets the 'clean dump' proto bytes from mWifiMetrics & deserializes it into
125     * mDeserializedWifiMetrics
126     */
127    public void cleanDumpProtoAndDeserialize() throws Exception {
128        ByteArrayOutputStream stream = new ByteArrayOutputStream();
129        PrintWriter writer = new PrintWriter(stream);
130        String[] args = new String[0];
131
132        when(mClock.getElapsedSinceBootMillis()).thenReturn(TEST_RECORD_DURATION_MILLIS);
133        //Test proto dump, by passing in proto arg option
134        args = new String[]{WifiMetrics.PROTO_DUMP_ARG, WifiMetrics.CLEAN_DUMP_ARG};
135        mWifiMetrics.dump(null, writer, args);
136        writer.flush();
137        String protoByteString = stream.toString();
138        byte[] protoBytes = Base64.decode(protoByteString, Base64.DEFAULT);
139        mDeserializedWifiMetrics = WifiMetricsProto.WifiLog.parseFrom(protoBytes);
140    }
141
142    /** Verifies that dump() includes the expected header */
143    @Test
144    public void stateDumpIncludesHeader() throws Exception {
145        assertStringContains(getStateDump(), "WifiMetrics");
146    }
147
148    /** Verifies that dump() includes correct alert count when there are no alerts. */
149    @Test
150    public void stateDumpAlertCountIsCorrectWithNoAlerts() throws Exception {
151        assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=()");
152    }
153
154    /** Verifies that dump() includes correct alert count when there is one alert. */
155    @Test
156    public void stateDumpAlertCountIsCorrectWithOneAlert() throws Exception {
157        mWifiMetrics.incrementAlertReasonCount(1);
158        assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=(1,1)");
159    }
160
161    /** Verifies that dump() includes correct alert count when there are multiple alerts. */
162    @Test
163    public void stateDumpAlertCountIsCorrectWithMultipleAlerts() throws Exception {
164        mWifiMetrics.incrementAlertReasonCount(1);
165        mWifiMetrics.incrementAlertReasonCount(1);
166        mWifiMetrics.incrementAlertReasonCount(16);
167        assertStringContains(getStateDump(), "mWifiLogProto.alertReasonCounts=(1,2),(16,1)");
168    }
169
170    @Test
171    public void testDumpProtoAndDeserialize() throws Exception {
172        setAndIncrementMetrics();
173        dumpProtoAndDeserialize();
174        assertDeserializedMetricsCorrect();
175    }
176
177    private static final int NUM_OPEN_NETWORKS = 2;
178    private static final int NUM_PERSONAL_NETWORKS = 3;
179    private static final int NUM_ENTERPRISE_NETWORKS = 5;
180    private static final int NUM_SAVED_NETWORKS = NUM_OPEN_NETWORKS + NUM_PERSONAL_NETWORKS
181            + NUM_ENTERPRISE_NETWORKS;
182    private static final int NUM_HIDDEN_NETWORKS = NUM_OPEN_NETWORKS;
183    private static final int NUM_PASSPOINT_NETWORKS = NUM_ENTERPRISE_NETWORKS;
184    private static final int NUM_NETWORKS_ADDED_BY_USER = 1;
185    private static final int NUM_NETWORKS_ADDED_BY_APPS = NUM_SAVED_NETWORKS
186            - NUM_NETWORKS_ADDED_BY_USER;
187    private static final boolean TEST_VAL_IS_LOCATION_ENABLED = true;
188    private static final boolean IS_SCANNING_ALWAYS_ENABLED = true;
189    private static final int NUM_EMPTY_SCAN_RESULTS = 19;
190    private static final int NUM_NON_EMPTY_SCAN_RESULTS = 23;
191    private static final int NUM_SCAN_UNKNOWN = 1;
192    private static final int NUM_SCAN_SUCCESS = 2;
193    private static final int NUM_SCAN_FAILURE_INTERRUPTED = 3;
194    private static final int NUM_SCAN_FAILURE_INVALID_CONFIGURATION = 5;
195    private static final int NUM_WIFI_UNKNOWN_SCREEN_OFF = 3;
196    private static final int NUM_WIFI_UNKNOWN_SCREEN_ON = 5;
197    private static final int NUM_WIFI_ASSOCIATED_SCREEN_OFF = 7;
198    private static final int NUM_WIFI_ASSOCIATED_SCREEN_ON = 11;
199    private static final int NUM_CONNECTIVITY_WATCHDOG_PNO_GOOD = 11;
200    private static final int NUM_CONNECTIVITY_WATCHDOG_PNO_BAD = 12;
201    private static final int NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_GOOD = 13;
202    private static final int NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_BAD = 14;
203    private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS = 1;
204    private static final int NUM_LAST_RESORT_WATCHDOG_BAD_ASSOCIATION_NETWORKS_TOTAL = 2;
205    private static final int NUM_LAST_RESORT_WATCHDOG_BAD_AUTHENTICATION_NETWORKS_TOTAL = 3;
206    private static final int NUM_LAST_RESORT_WATCHDOG_BAD_DHCP_NETWORKS_TOTAL = 4;
207    private static final int NUM_LAST_RESORT_WATCHDOG_BAD_OTHER_NETWORKS_TOTAL = 5;
208    private static final int NUM_LAST_RESORT_WATCHDOG_AVAILABLE_NETWORKS_TOTAL = 6;
209    private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_ASSOCIATION = 7;
210    private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_AUTHENTICATION = 8;
211    private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_DHCP = 9;
212    private static final int NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER = 10;
213    private static final int NUM_LAST_RESORT_WATCHDOG_SUCCESSES = 5;
214    private static final int NUM_RSSI_LEVELS_TO_INCREMENT = 20;
215    private static final int FIRST_RSSI_LEVEL = -80;
216    private static final int NUM_OPEN_NETWORK_SCAN_RESULTS = 1;
217    private static final int NUM_PERSONAL_NETWORK_SCAN_RESULTS = 4;
218    private static final int NUM_ENTERPRISE_NETWORK_SCAN_RESULTS = 3;
219    private static final int NUM_HIDDEN_NETWORK_SCAN_RESULTS = 1;
220    private static final int NUM_HOTSPOT2_R1_NETWORK_SCAN_RESULTS = 1;
221    private static final int NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS = 2;
222    private static final int NUM_SCANS = 5;
223    private static final int NUM_TOTAL_SCAN_RESULTS = 8;
224    private static final int MIN_RSSI_LEVEL = -127;
225    private static final int MAX_RSSI_LEVEL = 0;
226    private static final int WIFI_SCORE_RANGE_MIN = 0;
227    private static final int NUM_WIFI_SCORES_TO_INCREMENT = 20;
228    private static final int WIFI_SCORE_RANGE_MAX = 60;
229    private static final int NUM_OUT_OF_BOUND_ENTRIES = 10;
230    private static final int MAX_NUM_SOFTAP_RETURN_CODES = 3;
231    private static final int NUM_SOFTAP_START_SUCCESS = 3;
232    private static final int NUM_SOFTAP_FAILED_GENERAL_ERROR = 2;
233    private static final int NUM_SOFTAP_FAILED_NO_CHANNEL = 1;
234    private static final int NUM_HAL_CRASHES = 11;
235    private static final int NUM_WIFICOND_CRASHES = 12;
236    private static final int NUM_WIFI_ON_FAILURE_DUE_TO_HAL = 13;
237    private static final int NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND = 14;
238
239    private ScanDetail buildMockScanDetail(boolean hidden, NetworkDetail.HSRelease hSRelease,
240            String capabilities) {
241        ScanDetail mockScanDetail = mock(ScanDetail.class);
242        NetworkDetail mockNetworkDetail = mock(NetworkDetail.class);
243        ScanResult mockScanResult = mock(ScanResult.class);
244        when(mockScanDetail.getNetworkDetail()).thenReturn(mockNetworkDetail);
245        when(mockScanDetail.getScanResult()).thenReturn(mockScanResult);
246        when(mockNetworkDetail.isHiddenBeaconFrame()).thenReturn(hidden);
247        when(mockNetworkDetail.getHSRelease()).thenReturn(hSRelease);
248        mockScanResult.capabilities = capabilities;
249        return mockScanDetail;
250    }
251
252    private List<ScanDetail> buildMockScanDetailList() {
253        List<ScanDetail> mockScanDetails = new ArrayList<ScanDetail>();
254        mockScanDetails.add(buildMockScanDetail(true, null, "[ESS]"));
255        mockScanDetails.add(buildMockScanDetail(false, null, "[WPA2-PSK-CCMP][ESS]"));
256        mockScanDetails.add(buildMockScanDetail(false, null, "[WPA-PSK-CCMP]"));
257        mockScanDetails.add(buildMockScanDetail(false, null, "[WPA-PSK-CCMP]"));
258        mockScanDetails.add(buildMockScanDetail(false, null, "[WEP]"));
259        mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R2,
260                "[WPA-EAP-CCMP]"));
261        mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R2,
262                "[WPA2-EAP+FT/EAP-CCMP]"));
263        mockScanDetails.add(buildMockScanDetail(false, NetworkDetail.HSRelease.R1,
264                "[WPA-EAP-CCMP]"));
265        return mockScanDetails;
266    }
267
268    private List<WifiConfiguration> buildSavedNetworkList() {
269        List<WifiConfiguration> testSavedNetworks = new ArrayList<WifiConfiguration>();
270        for (int i = 0; i < NUM_OPEN_NETWORKS; i++) {
271            testSavedNetworks.add(WifiConfigurationTestUtil.createOpenHiddenNetwork());
272        }
273        for (int i = 0; i < NUM_PERSONAL_NETWORKS; i++) {
274            testSavedNetworks.add(WifiConfigurationTestUtil.createPskNetwork());
275        }
276        for (int i = 0; i < NUM_ENTERPRISE_NETWORKS; i++) {
277            // Passpoint networks are counted in both Passpoint and Enterprise counters
278            testSavedNetworks.add(WifiConfigurationTestUtil.createPasspointNetwork());
279        }
280        testSavedNetworks.get(0).selfAdded = true;
281        return testSavedNetworks;
282    }
283
284    /**
285     * Set simple metrics, increment others
286     */
287    public void setAndIncrementMetrics() throws Exception {
288        mWifiMetrics.updateSavedNetworks(buildSavedNetworkList());
289        mWifiMetrics.setIsLocationEnabled(TEST_VAL_IS_LOCATION_ENABLED);
290        mWifiMetrics.setIsScanningAlwaysEnabled(IS_SCANNING_ALWAYS_ENABLED);
291
292        for (int i = 0; i < NUM_EMPTY_SCAN_RESULTS; i++) {
293            mWifiMetrics.incrementEmptyScanResultCount();
294        }
295        for (int i = 0; i < NUM_NON_EMPTY_SCAN_RESULTS; i++) {
296            mWifiMetrics.incrementNonEmptyScanResultCount();
297        }
298        mWifiMetrics.incrementScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_UNKNOWN,
299                NUM_SCAN_UNKNOWN);
300        mWifiMetrics.incrementScanReturnEntry(WifiMetricsProto.WifiLog.SCAN_SUCCESS,
301                NUM_SCAN_SUCCESS);
302        mWifiMetrics.incrementScanReturnEntry(
303                WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
304                NUM_SCAN_FAILURE_INTERRUPTED);
305        mWifiMetrics.incrementScanReturnEntry(
306                WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION,
307                NUM_SCAN_FAILURE_INVALID_CONFIGURATION);
308        for (int i = 0; i < NUM_WIFI_UNKNOWN_SCREEN_OFF; i++) {
309            mWifiMetrics.incrementWifiSystemScanStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN,
310                    false);
311        }
312        for (int i = 0; i < NUM_WIFI_UNKNOWN_SCREEN_ON; i++) {
313            mWifiMetrics.incrementWifiSystemScanStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN,
314                    true);
315        }
316        for (int i = 0; i < NUM_WIFI_ASSOCIATED_SCREEN_OFF; i++) {
317            mWifiMetrics.incrementWifiSystemScanStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED,
318                    false);
319        }
320        for (int i = 0; i < NUM_WIFI_ASSOCIATED_SCREEN_ON; i++) {
321            mWifiMetrics.incrementWifiSystemScanStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED,
322                    true);
323        }
324        for (int i = 0; i < NUM_CONNECTIVITY_WATCHDOG_PNO_GOOD; i++) {
325            mWifiMetrics.incrementNumConnectivityWatchdogPnoGood();
326        }
327        for (int i = 0; i < NUM_CONNECTIVITY_WATCHDOG_PNO_BAD; i++) {
328            mWifiMetrics.incrementNumConnectivityWatchdogPnoBad();
329        }
330        for (int i = 0; i < NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_GOOD; i++) {
331            mWifiMetrics.incrementNumConnectivityWatchdogBackgroundGood();
332        }
333        for (int i = 0; i < NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_BAD; i++) {
334            mWifiMetrics.incrementNumConnectivityWatchdogBackgroundBad();
335        }
336        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS; i++) {
337            mWifiMetrics.incrementNumLastResortWatchdogTriggers();
338        }
339        mWifiMetrics.addCountToNumLastResortWatchdogBadAssociationNetworksTotal(
340                NUM_LAST_RESORT_WATCHDOG_BAD_ASSOCIATION_NETWORKS_TOTAL);
341        mWifiMetrics.addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(
342                NUM_LAST_RESORT_WATCHDOG_BAD_AUTHENTICATION_NETWORKS_TOTAL);
343        mWifiMetrics.addCountToNumLastResortWatchdogBadDhcpNetworksTotal(
344                NUM_LAST_RESORT_WATCHDOG_BAD_DHCP_NETWORKS_TOTAL);
345        mWifiMetrics.addCountToNumLastResortWatchdogBadOtherNetworksTotal(
346                NUM_LAST_RESORT_WATCHDOG_BAD_OTHER_NETWORKS_TOTAL);
347        mWifiMetrics.addCountToNumLastResortWatchdogAvailableNetworksTotal(
348                NUM_LAST_RESORT_WATCHDOG_AVAILABLE_NETWORKS_TOTAL);
349        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_ASSOCIATION; i++) {
350            mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAssociation();
351        }
352        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_AUTHENTICATION; i++) {
353            mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadAuthentication();
354        }
355        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_DHCP; i++) {
356            mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadDhcp();
357        }
358        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER; i++) {
359            mWifiMetrics.incrementNumLastResortWatchdogTriggersWithBadOther();
360        }
361        for (int i = 0; i < NUM_LAST_RESORT_WATCHDOG_SUCCESSES; i++) {
362            mWifiMetrics.incrementNumLastResortWatchdogSuccesses();
363        }
364        for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
365            for (int j = 0; j <= i; j++) {
366                mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL + i);
367            }
368        }
369        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
370            mWifiMetrics.incrementRssiPollRssiCount(MIN_RSSI_LEVEL - i);
371        }
372        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
373            mWifiMetrics.incrementRssiPollRssiCount(MAX_RSSI_LEVEL + i);
374        }
375        // Test alert-reason clamping.
376        mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MIN - 1);
377        mWifiMetrics.incrementAlertReasonCount(WifiLoggerHal.WIFI_ALERT_REASON_MAX + 1);
378        // Simple cases for alert reason.
379        mWifiMetrics.incrementAlertReasonCount(1);
380        mWifiMetrics.incrementAlertReasonCount(1);
381        mWifiMetrics.incrementAlertReasonCount(1);
382        mWifiMetrics.incrementAlertReasonCount(2);
383        List<ScanDetail> mockScanDetails = buildMockScanDetailList();
384        for (int i = 0; i < NUM_SCANS; i++) {
385            mWifiMetrics.countScanResults(mockScanDetails);
386        }
387        for (int score = WIFI_SCORE_RANGE_MIN; score < NUM_WIFI_SCORES_TO_INCREMENT; score++) {
388            for (int offset = 0; offset <= score; offset++) {
389                mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN + score);
390            }
391        }
392        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
393            mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MIN - i);
394        }
395        for (int i = 1; i < NUM_OUT_OF_BOUND_ENTRIES; i++) {
396            mWifiMetrics.incrementWifiScoreCount(WIFI_SCORE_RANGE_MAX + i);
397        }
398
399        // increment soft ap start return codes
400        for (int i = 0; i < NUM_SOFTAP_START_SUCCESS; i++) {
401            mWifiMetrics.incrementSoftApStartResult(true, 0);
402        }
403        for (int i = 0; i < NUM_SOFTAP_FAILED_GENERAL_ERROR; i++) {
404            mWifiMetrics.incrementSoftApStartResult(false, WifiManager.SAP_START_FAILURE_GENERAL);
405        }
406        for (int i = 0; i < NUM_SOFTAP_FAILED_NO_CHANNEL; i++) {
407            mWifiMetrics.incrementSoftApStartResult(false,
408                    WifiManager.SAP_START_FAILURE_NO_CHANNEL);
409        }
410        for (int i = 0; i < NUM_HAL_CRASHES; i++) {
411            mWifiMetrics.incrementNumHalCrashes();
412        }
413        for (int i = 0; i < NUM_WIFICOND_CRASHES; i++) {
414            mWifiMetrics.incrementNumWificondCrashes();
415        }
416        for (int i = 0; i < NUM_WIFI_ON_FAILURE_DUE_TO_HAL; i++) {
417            mWifiMetrics.incrementNumWifiOnFailureDueToHal();
418        }
419        for (int i = 0; i < NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND; i++) {
420            mWifiMetrics.incrementNumWifiOnFailureDueToWificond();
421        }
422    }
423
424    /**
425     * Assert that values in deserializedWifiMetrics match those set in 'setAndIncrementMetrics'
426     */
427    public void assertDeserializedMetricsCorrect() throws Exception {
428        assertEquals("mDeserializedWifiMetrics.numSavedNetworks == NUM_SAVED_NETWORKS",
429                mDeserializedWifiMetrics.numSavedNetworks, NUM_SAVED_NETWORKS);
430        assertEquals("mDeserializedWifiMetrics.numOpenNetworks == NUM_OPEN_NETWORKS",
431                mDeserializedWifiMetrics.numOpenNetworks, NUM_OPEN_NETWORKS);
432        assertEquals("mDeserializedWifiMetrics.numPersonalNetworks == NUM_PERSONAL_NETWORKS",
433                mDeserializedWifiMetrics.numPersonalNetworks, NUM_PERSONAL_NETWORKS);
434        assertEquals("mDeserializedWifiMetrics.numEnterpriseNetworks "
435                        + "== NUM_ENTERPRISE_NETWORKS",
436                mDeserializedWifiMetrics.numEnterpriseNetworks, NUM_ENTERPRISE_NETWORKS);
437        assertEquals("mDeserializedWifiMetrics.numNetworksAddedByUser "
438                        + "== NUM_NETWORKS_ADDED_BY_USER",
439                mDeserializedWifiMetrics.numNetworksAddedByUser, NUM_NETWORKS_ADDED_BY_USER);
440        assertEquals(NUM_HIDDEN_NETWORKS, mDeserializedWifiMetrics.numHiddenNetworks);
441        assertEquals(NUM_PASSPOINT_NETWORKS, mDeserializedWifiMetrics.numPasspointNetworks);
442        assertEquals("mDeserializedWifiMetrics.numNetworksAddedByApps "
443                        + "== NUM_NETWORKS_ADDED_BY_APPS",
444                mDeserializedWifiMetrics.numNetworksAddedByApps, NUM_NETWORKS_ADDED_BY_APPS);
445        assertEquals("mDeserializedWifiMetrics.isLocationEnabled == TEST_VAL_IS_LOCATION_ENABLED",
446                mDeserializedWifiMetrics.isLocationEnabled, TEST_VAL_IS_LOCATION_ENABLED);
447        assertEquals("mDeserializedWifiMetrics.isScanningAlwaysEnabled "
448                        + "== IS_SCANNING_ALWAYS_ENABLED",
449                mDeserializedWifiMetrics.isScanningAlwaysEnabled, IS_SCANNING_ALWAYS_ENABLED);
450        assertEquals("mDeserializedWifiMetrics.numEmptyScanResults == NUM_EMPTY_SCAN_RESULTS",
451                mDeserializedWifiMetrics.numEmptyScanResults, NUM_EMPTY_SCAN_RESULTS);
452        assertEquals("mDeserializedWifiMetrics.numNonEmptyScanResults == "
453                        + "NUM_NON_EMPTY_SCAN_RESULTS",
454                mDeserializedWifiMetrics.numNonEmptyScanResults, NUM_NON_EMPTY_SCAN_RESULTS);
455        assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_UNKNOWN,
456                NUM_SCAN_UNKNOWN);
457        assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_SUCCESS,
458                NUM_SCAN_SUCCESS);
459        assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED,
460                NUM_SCAN_FAILURE_INTERRUPTED);
461        assertScanReturnEntryEquals(WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION,
462                NUM_SCAN_FAILURE_INVALID_CONFIGURATION);
463        assertSystemStateEntryEquals(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false,
464                NUM_WIFI_UNKNOWN_SCREEN_OFF);
465        assertSystemStateEntryEquals(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true,
466                NUM_WIFI_UNKNOWN_SCREEN_ON);
467        assertSystemStateEntryEquals(
468                WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false, NUM_WIFI_ASSOCIATED_SCREEN_OFF);
469        assertSystemStateEntryEquals(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true,
470                NUM_WIFI_ASSOCIATED_SCREEN_ON);
471        assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogPnoGood,
472                NUM_CONNECTIVITY_WATCHDOG_PNO_GOOD);
473        assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogPnoBad,
474                NUM_CONNECTIVITY_WATCHDOG_PNO_BAD);
475        assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogBackgroundGood,
476                NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_GOOD);
477        assertEquals(mDeserializedWifiMetrics.numConnectivityWatchdogBackgroundBad,
478                NUM_CONNECTIVITY_WATCHDOG_BACKGROUND_BAD);
479        assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS,
480                mDeserializedWifiMetrics.numLastResortWatchdogTriggers);
481        assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_ASSOCIATION_NETWORKS_TOTAL,
482                mDeserializedWifiMetrics.numLastResortWatchdogBadAssociationNetworksTotal);
483        assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_AUTHENTICATION_NETWORKS_TOTAL,
484                mDeserializedWifiMetrics.numLastResortWatchdogBadAuthenticationNetworksTotal);
485        assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_DHCP_NETWORKS_TOTAL,
486                mDeserializedWifiMetrics.numLastResortWatchdogBadDhcpNetworksTotal);
487        assertEquals(NUM_LAST_RESORT_WATCHDOG_BAD_OTHER_NETWORKS_TOTAL,
488                mDeserializedWifiMetrics.numLastResortWatchdogBadOtherNetworksTotal);
489        assertEquals(NUM_LAST_RESORT_WATCHDOG_AVAILABLE_NETWORKS_TOTAL,
490                mDeserializedWifiMetrics.numLastResortWatchdogAvailableNetworksTotal);
491        assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_ASSOCIATION,
492                mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadAssociation);
493        assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_AUTHENTICATION,
494                mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadAuthentication);
495        assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_DHCP,
496                mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadDhcp);
497        assertEquals(NUM_LAST_RESORT_WATCHDOG_TRIGGERS_WITH_BAD_OTHER,
498                mDeserializedWifiMetrics.numLastResortWatchdogTriggersWithBadOther);
499        assertEquals(NUM_LAST_RESORT_WATCHDOG_SUCCESSES,
500                mDeserializedWifiMetrics.numLastResortWatchdogSuccesses);
501        assertEquals(TEST_RECORD_DURATION_SEC,
502                mDeserializedWifiMetrics.recordDurationSec);
503        for (int i = 0; i < NUM_RSSI_LEVELS_TO_INCREMENT; i++) {
504            assertEquals(MIN_RSSI_LEVEL + i, mDeserializedWifiMetrics.rssiPollRssiCount[i].rssi);
505            assertEquals(i + 1, mDeserializedWifiMetrics.rssiPollRssiCount[i].count);
506        }
507        StringBuilder sb_rssi = new StringBuilder();
508        sb_rssi.append("Number of RSSIs = " + mDeserializedWifiMetrics.rssiPollRssiCount.length);
509        assertTrue(sb_rssi.toString(), (mDeserializedWifiMetrics.rssiPollRssiCount.length
510                     <= (MAX_RSSI_LEVEL - MIN_RSSI_LEVEL + 1)));
511        assertEquals(2, mDeserializedWifiMetrics.alertReasonCount[0].count);  // Clamped reasons.
512        assertEquals(3, mDeserializedWifiMetrics.alertReasonCount[1].count);
513        assertEquals(1, mDeserializedWifiMetrics.alertReasonCount[2].count);
514        assertEquals(3, mDeserializedWifiMetrics.alertReasonCount.length);
515        assertEquals(NUM_TOTAL_SCAN_RESULTS * NUM_SCANS,
516                mDeserializedWifiMetrics.numTotalScanResults);
517        assertEquals(NUM_OPEN_NETWORK_SCAN_RESULTS * NUM_SCANS,
518                mDeserializedWifiMetrics.numOpenNetworkScanResults);
519        assertEquals(NUM_PERSONAL_NETWORK_SCAN_RESULTS * NUM_SCANS,
520                mDeserializedWifiMetrics.numPersonalNetworkScanResults);
521        assertEquals(NUM_ENTERPRISE_NETWORK_SCAN_RESULTS * NUM_SCANS,
522                mDeserializedWifiMetrics.numEnterpriseNetworkScanResults);
523        assertEquals(NUM_HIDDEN_NETWORK_SCAN_RESULTS * NUM_SCANS,
524                mDeserializedWifiMetrics.numHiddenNetworkScanResults);
525        assertEquals(NUM_HOTSPOT2_R1_NETWORK_SCAN_RESULTS * NUM_SCANS,
526                mDeserializedWifiMetrics.numHotspot2R1NetworkScanResults);
527        assertEquals(NUM_HOTSPOT2_R2_NETWORK_SCAN_RESULTS * NUM_SCANS,
528                mDeserializedWifiMetrics.numHotspot2R2NetworkScanResults);
529        assertEquals(NUM_SCANS,
530                mDeserializedWifiMetrics.numScans);
531        for (int score_index = 0; score_index < NUM_WIFI_SCORES_TO_INCREMENT; score_index++) {
532            assertEquals(WIFI_SCORE_RANGE_MIN + score_index,
533                    mDeserializedWifiMetrics.wifiScoreCount[score_index].score);
534            assertEquals(score_index + 1,
535                    mDeserializedWifiMetrics.wifiScoreCount[score_index].count);
536        }
537        StringBuilder sb_wifi_score = new StringBuilder();
538        sb_wifi_score.append("Number of wifi_scores = "
539                + mDeserializedWifiMetrics.wifiScoreCount.length);
540        assertTrue(sb_wifi_score.toString(), (mDeserializedWifiMetrics.wifiScoreCount.length
541                <= (WIFI_SCORE_RANGE_MAX - WIFI_SCORE_RANGE_MIN + 1)));
542        StringBuilder sb_wifi_limits = new StringBuilder();
543        sb_wifi_limits.append("Wifi Score limit is " +  NetworkAgent.WIFI_BASE_SCORE
544                + ">= " + WIFI_SCORE_RANGE_MAX);
545        assertTrue(sb_wifi_limits.toString(), NetworkAgent.WIFI_BASE_SCORE <= WIFI_SCORE_RANGE_MAX);
546        assertEquals(MAX_NUM_SOFTAP_RETURN_CODES, mDeserializedWifiMetrics.softApReturnCode.length);
547        assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
548                     mDeserializedWifiMetrics.softApReturnCode[0].startResult);
549        assertEquals(NUM_SOFTAP_START_SUCCESS, mDeserializedWifiMetrics.softApReturnCode[0].count);
550        assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
551                     mDeserializedWifiMetrics.softApReturnCode[1].startResult);
552        assertEquals(NUM_SOFTAP_FAILED_GENERAL_ERROR,
553                     mDeserializedWifiMetrics.softApReturnCode[1].count);
554        assertEquals(WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
555                     mDeserializedWifiMetrics.softApReturnCode[2].startResult);
556        assertEquals(NUM_SOFTAP_FAILED_NO_CHANNEL,
557                     mDeserializedWifiMetrics.softApReturnCode[2].count);
558        assertEquals(NUM_HAL_CRASHES, mDeserializedWifiMetrics.numHalCrashes);
559        assertEquals(NUM_WIFICOND_CRASHES, mDeserializedWifiMetrics.numWificondCrashes);
560        assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_HAL,
561                mDeserializedWifiMetrics.numWifiOnFailureDueToHal);
562        assertEquals(NUM_WIFI_ON_FAILURE_DUE_TO_WIFICOND,
563                mDeserializedWifiMetrics.numWifiOnFailureDueToWificond);
564    }
565
566    /**
567     *  Assert deserialized metrics Scan Return Entry equals count
568     */
569    public void assertScanReturnEntryEquals(int returnCode, int count) {
570        for (int i = 0; i < mDeserializedWifiMetrics.scanReturnEntries.length; i++) {
571            if (mDeserializedWifiMetrics.scanReturnEntries[i].scanReturnCode == returnCode) {
572                assertEquals(mDeserializedWifiMetrics.scanReturnEntries[i].scanResultsCount, count);
573                return;
574            }
575        }
576        assertEquals(null, count);
577    }
578
579    /**
580     *  Assert deserialized metrics SystemState entry equals count
581     */
582    public void assertSystemStateEntryEquals(int state, boolean screenOn, int count) {
583        for (int i = 0; i < mDeserializedWifiMetrics.wifiSystemStateEntries.length; i++) {
584            if (mDeserializedWifiMetrics.wifiSystemStateEntries[i].wifiState == state
585                    && mDeserializedWifiMetrics.wifiSystemStateEntries[i].isScreenOn == screenOn) {
586                assertEquals(mDeserializedWifiMetrics.wifiSystemStateEntries[i].wifiStateCount,
587                        count);
588                return;
589            }
590        }
591        assertEquals(null, count);
592    }
593    /**
594     * Combination of all other WifiMetrics unit tests, an internal-integration test, or functional
595     * test
596     */
597    @Test
598    public void setMetricsSerializeDeserializeAssertMetricsSame() throws Exception {
599        setAndIncrementMetrics();
600        startAndEndConnectionEventSucceeds();
601        dumpProtoAndDeserialize();
602        assertDeserializedMetricsCorrect();
603        assertEquals("mDeserializedWifiMetrics.connectionEvent.length",
604                2, mDeserializedWifiMetrics.connectionEvent.length);
605        //<TODO> test individual connectionEvents for correctness,
606        // check scanReturnEntries & wifiSystemStateEntries counts and individual elements
607        // pending their implementation</TODO>
608    }
609
610    private static final String SSID = "red";
611    private static final int CONFIG_DTIM = 3;
612    private static final int NETWORK_DETAIL_WIFIMODE = 5;
613    private static final int NETWORK_DETAIL_DTIM = 7;
614    private static final int SCAN_RESULT_LEVEL = -30;
615    /**
616     * Test that WifiMetrics is correctly getting data from ScanDetail and WifiConfiguration
617     */
618    @Test
619    public void testScanDetailAndWifiConfigurationUsage() throws Exception {
620        //Setup mock configs and scan details
621        NetworkDetail networkDetail = mock(NetworkDetail.class);
622        when(networkDetail.getWifiMode()).thenReturn(NETWORK_DETAIL_WIFIMODE);
623        when(networkDetail.getSSID()).thenReturn(SSID);
624        when(networkDetail.getDtimInterval()).thenReturn(NETWORK_DETAIL_DTIM);
625        ScanResult scanResult = mock(ScanResult.class);
626        scanResult.level = SCAN_RESULT_LEVEL;
627        WifiConfiguration config = mock(WifiConfiguration.class);
628        config.SSID = "\"" + SSID + "\"";
629        config.dtimInterval = CONFIG_DTIM;
630        WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
631                mock(WifiConfiguration.NetworkSelectionStatus.class);
632        when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
633        when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStat);
634        ScanDetail scanDetail = mock(ScanDetail.class);
635        when(scanDetail.getNetworkDetail()).thenReturn(networkDetail);
636        when(scanDetail.getScanResult()).thenReturn(scanResult);
637
638        //Create a connection event using only the config
639        mWifiMetrics.startConnectionEvent(config, "Red",
640                WifiMetricsProto.ConnectionEvent.ROAM_NONE);
641        mWifiMetrics.endConnectionEvent(
642                WifiMetrics.ConnectionEvent.FAILURE_NONE,
643                WifiMetricsProto.ConnectionEvent.HLF_NONE);
644
645        //Create a connection event using the config and a scan detail
646        mWifiMetrics.startConnectionEvent(config, "Green",
647                WifiMetricsProto.ConnectionEvent.ROAM_NONE);
648        mWifiMetrics.setConnectionScanDetail(scanDetail);
649        mWifiMetrics.endConnectionEvent(
650                WifiMetrics.ConnectionEvent.FAILURE_NONE,
651                WifiMetricsProto.ConnectionEvent.HLF_NONE);
652
653        //Dump proto from mWifiMetrics and deserialize it to mDeserializedWifiMetrics
654        dumpProtoAndDeserialize();
655
656        //Check that the correct values are being flowed through
657        assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 2);
658        assertEquals(mDeserializedWifiMetrics.connectionEvent[0].routerFingerprint.dtim,
659                CONFIG_DTIM);
660        assertEquals(mDeserializedWifiMetrics.connectionEvent[0].signalStrength, SCAN_RESULT_LEVEL);
661        assertEquals(mDeserializedWifiMetrics.connectionEvent[1].routerFingerprint.dtim,
662                NETWORK_DETAIL_DTIM);
663        assertEquals(mDeserializedWifiMetrics.connectionEvent[1].signalStrength,
664                SCAN_RESULT_LEVEL);
665        assertEquals(mDeserializedWifiMetrics.connectionEvent[1].routerFingerprint.routerTechnology,
666                NETWORK_DETAIL_WIFIMODE);
667    }
668
669    /**
670     * Test that WifiMetrics is being cleared after dumping via proto
671     */
672    @Test
673    public void testMetricsClearedAfterProtoRequested() throws Exception {
674        // Create 3 ConnectionEvents
675        mWifiMetrics.startConnectionEvent(null, "RED",
676                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
677        mWifiMetrics.endConnectionEvent(
678                WifiMetrics.ConnectionEvent.FAILURE_NONE,
679                WifiMetricsProto.ConnectionEvent.HLF_NONE);
680        mWifiMetrics.startConnectionEvent(null, "YELLOW",
681                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
682        mWifiMetrics.endConnectionEvent(
683                WifiMetrics.ConnectionEvent.FAILURE_NONE,
684                WifiMetricsProto.ConnectionEvent.HLF_NONE);
685        mWifiMetrics.startConnectionEvent(null, "GREEN",
686                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
687        mWifiMetrics.endConnectionEvent(
688                WifiMetrics.ConnectionEvent.FAILURE_NONE,
689                WifiMetricsProto.ConnectionEvent.HLF_NONE);
690        mWifiMetrics.startConnectionEvent(null, "ORANGE",
691                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
692        mWifiMetrics.endConnectionEvent(
693                WifiMetrics.ConnectionEvent.FAILURE_NONE,
694                WifiMetricsProto.ConnectionEvent.HLF_NONE);
695
696        //Dump proto and deserialize
697        //This should clear all the metrics in mWifiMetrics,
698        dumpProtoAndDeserialize();
699        //Check there are only 3 connection events
700        assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 4);
701        assertEquals(mDeserializedWifiMetrics.rssiPollRssiCount.length, 0);
702        assertEquals(mDeserializedWifiMetrics.alertReasonCount.length, 0);
703
704        // Create 2 ConnectionEvents
705        mWifiMetrics.startConnectionEvent(null,  "BLUE",
706                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
707        mWifiMetrics.endConnectionEvent(
708                WifiMetrics.ConnectionEvent.FAILURE_NONE,
709                WifiMetricsProto.ConnectionEvent.HLF_NONE);
710        mWifiMetrics.startConnectionEvent(null, "RED",
711                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
712        mWifiMetrics.endConnectionEvent(
713                WifiMetrics.ConnectionEvent.FAILURE_NONE,
714                WifiMetricsProto.ConnectionEvent.HLF_NONE);
715
716        //Dump proto and deserialize
717        dumpProtoAndDeserialize();
718        //Check there are only 2 connection events
719        assertEquals(mDeserializedWifiMetrics.connectionEvent.length, 2);
720    }
721
722    /**
723     * Tests that after setting metrics values they can be serialized and deserialized with the
724     *   $ adb shell dumpsys wifi wifiMetricsProto clean
725     */
726    @Test
727    public void testClearMetricsDump() throws Exception {
728        setAndIncrementMetrics();
729        startAndEndConnectionEventSucceeds();
730        cleanDumpProtoAndDeserialize();
731        assertDeserializedMetricsCorrect();
732        assertEquals("mDeserializedWifiMetrics.connectionEvent.length",
733                2, mDeserializedWifiMetrics.connectionEvent.length);
734    }
735
736    private static final int NUM_REPEATED_DELTAS = 7;
737    private static final int REPEATED_DELTA = 0;
738    private static final int SINGLE_GOOD_DELTA = 1;
739    private static final int SINGLE_TIMEOUT_DELTA = 2;
740    private static final int NUM_REPEATED_BOUND_DELTAS = 2;
741    private static final int MAX_DELTA_LEVEL = 127;
742    private static final int MIN_DELTA_LEVEL = -127;
743    private static final int ARBITRARY_DELTA_LEVEL = 20;
744
745    /**
746     * Sunny day RSSI delta logging scenario.
747     * Logs one rssi delta value multiple times
748     * Logs a different delta value a single time
749     */
750    @Test
751    public void testRssiDeltasSuccessfulLogging() throws Exception {
752        // Generate some repeated deltas
753        for (int i = 0; i < NUM_REPEATED_DELTAS; i++) {
754            generateRssiDelta(MIN_RSSI_LEVEL, REPEATED_DELTA,
755                    WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
756        }
757        // Generate a single delta
758        generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_GOOD_DELTA,
759                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
760        dumpProtoAndDeserialize();
761        assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
762        // Check the repeated deltas
763        assertEquals(NUM_REPEATED_DELTAS, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
764        assertEquals(REPEATED_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
765        // Check the single delta
766        assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
767        assertEquals(SINGLE_GOOD_DELTA, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
768    }
769
770    /**
771     * Tests that Rssi Delta events whose scanResult and Rssi Poll come too far apart, timeout,
772     * and are not logged.
773     */
774    @Test
775    public void testRssiDeltasTimeout() throws Exception {
776        // Create timed out rssi deltas
777        generateRssiDelta(MIN_RSSI_LEVEL, REPEATED_DELTA,
778                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS + 1);
779        generateRssiDelta(MIN_RSSI_LEVEL, SINGLE_TIMEOUT_DELTA,
780                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS + 1);
781        dumpProtoAndDeserialize();
782        assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
783    }
784
785    /**
786     * Tests the exact inclusive boundaries of RSSI delta logging.
787     */
788    @Test
789    public void testRssiDeltaSuccessfulLoggingExactBounds() throws Exception {
790        generateRssiDelta(MIN_RSSI_LEVEL, MAX_DELTA_LEVEL,
791                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
792        generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL,
793                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
794        dumpProtoAndDeserialize();
795        assertEquals(2, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
796        assertEquals(MIN_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
797        assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
798        assertEquals(MAX_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[1].rssi);
799        assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[1].count);
800    }
801
802    /**
803     * Tests the exact exclusive boundaries of RSSI delta logging.
804     * This test ensures that too much data is not generated.
805     */
806    @Test
807    public void testRssiDeltaOutOfBounds() throws Exception {
808        generateRssiDelta(MIN_RSSI_LEVEL, MAX_DELTA_LEVEL + 1,
809                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
810        generateRssiDelta(MAX_RSSI_LEVEL, MIN_DELTA_LEVEL - 1,
811                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS);
812        dumpProtoAndDeserialize();
813        assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
814    }
815
816    /**
817     * This test ensures no rssi Delta is logged after an unsuccessful ConnectionEvent
818     */
819    @Test
820    public void testUnsuccesfulConnectionEventRssiDeltaIsNotLogged() throws Exception {
821        generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
822                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
823                false, // successfulConnectionEvent
824                true, // completeConnectionEvent
825                true, // useValidScanResult
826                true // dontDeserializeBeforePoll
827        );
828
829        dumpProtoAndDeserialize();
830        assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
831    }
832
833    /**
834     * This test ensures rssi Deltas can be logged during a ConnectionEvent
835     */
836    @Test
837    public void testIncompleteConnectionEventRssiDeltaIsLogged() throws Exception {
838        generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
839                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
840                true, // successfulConnectionEvent
841                false, // completeConnectionEvent
842                true, // useValidScanResult
843                true // dontDeserializeBeforePoll
844        );
845        dumpProtoAndDeserialize();
846        assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
847        assertEquals(ARBITRARY_DELTA_LEVEL, mDeserializedWifiMetrics.rssiPollDeltaCount[0].rssi);
848        assertEquals(1, mDeserializedWifiMetrics.rssiPollDeltaCount[0].count);
849    }
850
851    /**
852     * This test ensures that no delta is logged for a null ScanResult Candidate
853     */
854    @Test
855    public void testRssiDeltaNotLoggedForNullCandidateScanResult() throws Exception {
856        generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
857                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
858                true, // successfulConnectionEvent
859                true, // completeConnectionEvent
860                false, // useValidScanResult
861                true // dontDeserializeBeforePoll
862        );
863        dumpProtoAndDeserialize();
864        assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
865    }
866
867    /**
868     * This test ensures that Rssi Deltas are not logged over a 'clear()' call (Metrics Serialized)
869     */
870    @Test
871    public void testMetricsSerializedDuringRssiDeltaEventLogsNothing() throws Exception {
872        generateRssiDelta(MIN_RSSI_LEVEL, ARBITRARY_DELTA_LEVEL,
873                WifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS,
874                true, // successfulConnectionEvent
875                true, // completeConnectionEvent
876                true, // useValidScanResult
877                false // dontDeserializeBeforePoll
878        );
879        dumpProtoAndDeserialize();
880        assertEquals(0, mDeserializedWifiMetrics.rssiPollDeltaCount.length);
881    }
882
883    private static final int DEAUTH_REASON = 7;
884    private static final int ASSOC_STATUS = 11;
885    private static final int ASSOC_TIMEOUT = 1;
886    private static final int LOCAL_GEN = 1;
887    private static final int AUTH_FAILURE_REASON = WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD;
888    private static final int NUM_TEST_STA_EVENTS = 14;
889    private static final String   sSSID = "\"SomeTestSsid\"";
890    private static final WifiSsid sWifiSsid = WifiSsid.createFromAsciiEncoded(sSSID);
891    private static final String   sBSSID = "01:02:03:04:05:06";
892
893    private final StateChangeResult mStateDisconnected =
894            new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.DISCONNECTED);
895    private final StateChangeResult mStateCompleted =
896            new StateChangeResult(0, sWifiSsid, sBSSID, SupplicantState.COMPLETED);
897    // Test bitmasks of supplicant state changes
898    private final int mSupBm1 = WifiMetrics.supplicantStateToBit(mStateDisconnected.state);
899    private final int mSupBm2 = WifiMetrics.supplicantStateToBit(mStateDisconnected.state)
900            | WifiMetrics.supplicantStateToBit(mStateCompleted.state);
901    // An invalid but interesting wifiConfiguration that exercises the StaEvent.ConfigInfo encoding
902    private final WifiConfiguration mTestWifiConfig = createComplexWifiConfig();
903    // <msg.what> <msg.arg1> <msg.arg2>
904    private int[][] mTestStaMessageInts = {
905        {WifiMonitor.ASSOCIATION_REJECTION_EVENT,   ASSOC_TIMEOUT,      ASSOC_STATUS},
906        {WifiMonitor.AUTHENTICATION_FAILURE_EVENT,  0,                  AUTH_FAILURE_REASON},
907        {WifiMonitor.NETWORK_CONNECTION_EVENT,      0,                  0},
908        {WifiMonitor.NETWORK_DISCONNECTION_EVENT,   LOCAL_GEN,          DEAUTH_REASON},
909        {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0,                  0},
910        {WifiStateMachine.CMD_ASSOCIATED_BSSID,     0,                  0},
911        {WifiStateMachine.CMD_TARGET_BSSID,         0,                  0},
912        {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0,                  0},
913        {WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT, 0,                  0}
914    };
915    private Object[] mTestStaMessageObjs = {
916        null,
917        null,
918        null,
919        null,
920        mStateDisconnected,
921        null,
922        null,
923        mStateDisconnected,
924        mStateCompleted
925    };
926    // Values used to generate the StaEvent log calls from WifiStateMachine
927    // <StaEvent.Type>, <StaEvent.FrameworkDisconnectReason>, <1|0>(testWifiConfiguration, null)
928    private int[][] mTestStaLogInts = {
929        {StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL, 0,                          0},
930        {StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST,       0,                          0},
931        {StaEvent.TYPE_CMD_IP_REACHABILITY_LOST,        0,                          0},
932        {StaEvent.TYPE_CMD_START_CONNECT,               0,                          1},
933        {StaEvent.TYPE_CMD_START_ROAM,                  0,                          1},
934        {StaEvent.TYPE_CONNECT_NETWORK,                 0,                          1},
935        {StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK,     0,                          0},
936        {StaEvent.TYPE_FRAMEWORK_DISCONNECT,            StaEvent.DISCONNECT_API,    0}
937    };
938    // Values used to generate the StaEvent log calls from WifiMonitor
939    // <type>, <reason>, <status>, <local_gen>,
940    // <auth_fail_reason>, <assoc_timed_out> <supplicantStateChangeBitmask> <1|0>(has ConfigInfo)
941    private int[][] mExpectedValues = {
942        {StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT,     -1,  ASSOC_STATUS,         0,
943            /**/                               0, ASSOC_TIMEOUT,        0, 0},    /**/
944        {StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT,    -1,            -1,         0,
945            /**/StaEvent.AUTH_FAILURE_WRONG_PSWD,             0,        0, 0},    /**/
946        {StaEvent.TYPE_NETWORK_CONNECTION_EVENT,        -1,            -1,         0,
947            /**/                               0,             0,        0, 0},    /**/
948        {StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT, DEAUTH_REASON,     -1, LOCAL_GEN,
949            /**/                               0,             0,        0, 0},    /**/
950        {StaEvent.TYPE_CMD_ASSOCIATED_BSSID,            -1,            -1,         0,
951            /**/                               0,             0,  mSupBm1, 0},    /**/
952        {StaEvent.TYPE_CMD_TARGET_BSSID,                -1,            -1,         0,
953            /**/                               0,             0,        0, 0},    /**/
954        {StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL, -1,            -1,         0,
955            /**/                               0,             0,  mSupBm2, 0},    /**/
956        {StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST,       -1,            -1,         0,
957            /**/                               0,             0,        0, 0},    /**/
958        {StaEvent.TYPE_CMD_IP_REACHABILITY_LOST,        -1,            -1,         0,
959            /**/                               0,             0,        0, 0},    /**/
960        {StaEvent.TYPE_CMD_START_CONNECT,               -1,            -1,         0,
961            /**/                               0,             0,        0, 1},    /**/
962        {StaEvent.TYPE_CMD_START_ROAM,                  -1,            -1,         0,
963            /**/                               0,             0,        0, 1},    /**/
964        {StaEvent.TYPE_CONNECT_NETWORK,                 -1,            -1,         0,
965            /**/                               0,             0,        0, 1},    /**/
966        {StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK,     -1,            -1,         0,
967            /**/                               0,             0,        0, 0},    /**/
968        {StaEvent.TYPE_FRAMEWORK_DISCONNECT,            -1,            -1,         0,
969            /**/                               0,             0,        0, 0}     /**/
970    };
971
972    /**
973     * Generates events from all the rows in mTestStaMessageInts, and then mTestStaLogInts
974     */
975    private void generateStaEvents(WifiMetrics wifiMetrics) {
976        Handler handler = wifiMetrics.getHandler();
977        for (int i = 0; i < mTestStaMessageInts.length; i++) {
978            int[] mia = mTestStaMessageInts[i];
979            handler.sendMessage(
980                    handler.obtainMessage(mia[0], mia[1], mia[2], mTestStaMessageObjs[i]));
981        }
982        mTestLooper.dispatchAll();
983        for (int i = 0; i < mTestStaLogInts.length; i++) {
984            int[] lia = mTestStaLogInts[i];
985            wifiMetrics.logStaEvent(lia[0], lia[1], lia[2] == 1 ? mTestWifiConfig : null);
986        }
987    }
988    private void verifyDeserializedStaEvents(WifiMetricsProto.WifiLog wifiLog) {
989        assertEquals(NUM_TEST_STA_EVENTS, wifiLog.staEventList.length);
990        int j = 0; // De-serialized event index
991        for (int i = 0; i < mTestStaMessageInts.length; i++) {
992            StaEvent event = wifiLog.staEventList[j];
993            int[] mia = mTestStaMessageInts[i];
994            int[] evs = mExpectedValues[j];
995            if (mia[0] != WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT) {
996                assertEquals(evs[0], event.type);
997                assertEquals(evs[1], event.reason);
998                assertEquals(evs[2], event.status);
999                assertEquals(evs[3] == 1 ? true : false, event.localGen);
1000                assertEquals(evs[4], event.authFailureReason);
1001                assertEquals(evs[5] == 1 ? true : false, event.associationTimedOut);
1002                assertEquals(evs[6], event.supplicantStateChangesBitmask);
1003                assertConfigInfoEqualsWifiConfig(
1004                        evs[7] == 1 ? mTestWifiConfig : null, event.configInfo);
1005                j++;
1006            }
1007        }
1008    }
1009
1010    /**
1011     * Generate StaEvents of each type, ensure all the different values are logged correctly,
1012     * and that they survive serialization & de-serialization
1013     */
1014    @Test
1015    public void testStaEventsLogSerializeDeserialize() throws Exception {
1016        generateStaEvents(mWifiMetrics);
1017        dumpProtoAndDeserialize();
1018        verifyDeserializedStaEvents(mDeserializedWifiMetrics);
1019    }
1020
1021    /**
1022     * Ensure the number of StaEvents does not exceed MAX_STA_EVENTS by generating lots of events
1023     * and checking how many are deserialized
1024     */
1025    @Test
1026    public void testStaEventBounding() throws Exception {
1027        for (int i = 0; i < (WifiMetrics.MAX_STA_EVENTS + 10); i++) {
1028            mWifiMetrics.logStaEvent(StaEvent.TYPE_CMD_START_CONNECT);
1029        }
1030        dumpProtoAndDeserialize();
1031        assertEquals(WifiMetrics.MAX_STA_EVENTS, mDeserializedWifiMetrics.staEventList.length);
1032    }
1033
1034    /**
1035     * Ensure WifiMetrics doesn't cause a null pointer exception when called with null args
1036     */
1037    @Test
1038    public void testDumpNullArg() {
1039        mWifiMetrics.dump(new FileDescriptor(), new PrintWriter(new StringWriter()), null);
1040    }
1041
1042    /**
1043     * Generate an RSSI delta event by creating a connection event and an RSSI poll within
1044     * 'interArrivalTime' milliseconds of each other.
1045     * Event will not be logged if interArrivalTime > mWifiMetrics.TIMEOUT_RSSI_DELTA_MILLIS
1046     * successfulConnectionEvent, completeConnectionEvent, useValidScanResult and
1047     * dontDeserializeBeforePoll
1048     * each create an anomalous condition when set to false.
1049     */
1050    private void generateRssiDelta(int scanRssi, int rssiDelta,
1051            long interArrivalTime, boolean successfulConnectionEvent,
1052            boolean completeConnectionEvent, boolean useValidScanResult,
1053            boolean dontDeserializeBeforePoll) throws Exception {
1054        when(mClock.getElapsedSinceBootMillis()).thenReturn((long) 0);
1055        ScanResult scanResult = null;
1056        if (useValidScanResult) {
1057            scanResult = mock(ScanResult.class);
1058            scanResult.level = scanRssi;
1059        }
1060        WifiConfiguration config = mock(WifiConfiguration.class);
1061        WifiConfiguration.NetworkSelectionStatus networkSelectionStat =
1062                mock(WifiConfiguration.NetworkSelectionStatus.class);
1063        when(networkSelectionStat.getCandidate()).thenReturn(scanResult);
1064        when(config.getNetworkSelectionStatus()).thenReturn(networkSelectionStat);
1065        mWifiMetrics.startConnectionEvent(config, "TestNetwork",
1066                WifiMetricsProto.ConnectionEvent.ROAM_ENTERPRISE);
1067        if (completeConnectionEvent) {
1068            if (successfulConnectionEvent) {
1069                mWifiMetrics.endConnectionEvent(
1070                        WifiMetrics.ConnectionEvent.FAILURE_NONE,
1071                        WifiMetricsProto.ConnectionEvent.HLF_NONE);
1072            } else {
1073                mWifiMetrics.endConnectionEvent(
1074                        WifiMetrics.ConnectionEvent.FAILURE_AUTHENTICATION_FAILURE,
1075                        WifiMetricsProto.ConnectionEvent.HLF_NONE);
1076            }
1077        }
1078        when(mClock.getElapsedSinceBootMillis()).thenReturn(interArrivalTime);
1079        if (!dontDeserializeBeforePoll) {
1080            dumpProtoAndDeserialize();
1081        }
1082        mWifiMetrics.incrementRssiPollRssiCount(scanRssi + rssiDelta);
1083    }
1084    /**
1085     * Generate an RSSI delta event, with all extra conditions set to true.
1086     */
1087    private void generateRssiDelta(int scanRssi, int rssiDelta,
1088            long interArrivalTime) throws Exception {
1089        generateRssiDelta(scanRssi, rssiDelta, interArrivalTime, true, true, true, true);
1090    }
1091
1092    private void assertStringContains(
1093            String actualString, String expectedSubstring) {
1094        assertTrue("Expected text not found in: " + actualString,
1095                actualString.contains(expectedSubstring));
1096    }
1097
1098    private String getStateDump() {
1099        ByteArrayOutputStream stream = new ByteArrayOutputStream();
1100        PrintWriter writer = new PrintWriter(stream);
1101        String[] args = new String[0];
1102        mWifiMetrics.dump(null, writer, args);
1103        writer.flush();
1104        return stream.toString();
1105    }
1106
1107    private static final int TEST_ALLOWED_KEY_MANAGEMENT = 83;
1108    private static final int TEST_ALLOWED_PROTOCOLS = 22;
1109    private static final int TEST_ALLOWED_AUTH_ALGORITHMS = 11;
1110    private static final int TEST_ALLOWED_PAIRWISE_CIPHERS = 67;
1111    private static final int TEST_ALLOWED_GROUP_CIPHERS = 231;
1112    private static final int TEST_CANDIDATE_LEVEL = -80;
1113    private static final int TEST_CANDIDATE_FREQ = 2345;
1114
1115    private WifiConfiguration createComplexWifiConfig() {
1116        WifiConfiguration config = new WifiConfiguration();
1117        config.allowedKeyManagement = intToBitSet(TEST_ALLOWED_KEY_MANAGEMENT);
1118        config.allowedProtocols = intToBitSet(TEST_ALLOWED_PROTOCOLS);
1119        config.allowedAuthAlgorithms = intToBitSet(TEST_ALLOWED_AUTH_ALGORITHMS);
1120        config.allowedPairwiseCiphers = intToBitSet(TEST_ALLOWED_PAIRWISE_CIPHERS);
1121        config.allowedGroupCiphers = intToBitSet(TEST_ALLOWED_GROUP_CIPHERS);
1122        config.hiddenSSID = true;
1123        config.ephemeral = true;
1124        config.getNetworkSelectionStatus().setHasEverConnected(true);
1125        ScanResult candidate = new ScanResult();
1126        candidate.level = TEST_CANDIDATE_LEVEL;
1127        candidate.frequency = TEST_CANDIDATE_FREQ;
1128        config.getNetworkSelectionStatus().setCandidate(candidate);
1129        return config;
1130    }
1131
1132    private void assertConfigInfoEqualsWifiConfig(WifiConfiguration config,
1133            StaEvent.ConfigInfo info) {
1134        if (config == null && info == null) return;
1135        assertEquals(config.allowedKeyManagement,   intToBitSet(info.allowedKeyManagement));
1136        assertEquals(config.allowedProtocols,       intToBitSet(info.allowedProtocols));
1137        assertEquals(config.allowedAuthAlgorithms,  intToBitSet(info.allowedAuthAlgorithms));
1138        assertEquals(config.allowedPairwiseCiphers, intToBitSet(info.allowedPairwiseCiphers));
1139        assertEquals(config.allowedGroupCiphers,    intToBitSet(info.allowedGroupCiphers));
1140        assertEquals(config.hiddenSSID, info.hiddenSsid);
1141        assertEquals(config.ephemeral, info.isEphemeral);
1142        assertEquals(config.getNetworkSelectionStatus().getHasEverConnected(),
1143                info.hasEverConnected);
1144        assertEquals(config.getNetworkSelectionStatus().getCandidate().level, info.scanRssi);
1145        assertEquals(config.getNetworkSelectionStatus().getCandidate().frequency, info.scanFreq);
1146    }
1147
1148    /**
1149     * Sets the values of bitSet to match an int mask
1150     */
1151    private static BitSet intToBitSet(int mask) {
1152        BitSet bitSet = new BitSet();
1153        for (int bitIndex = 0; mask > 0; mask >>>= 1, bitIndex++) {
1154            if ((mask & 1) != 0) bitSet.set(bitIndex);
1155        }
1156        return bitSet;
1157    }
1158}
1159