WifiMetrics.java revision 44ce135519e22d8c4746abac21c14111f1ce8667
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 android.net.NetworkAgent;
20import android.net.wifi.ScanResult;
21import android.net.wifi.WifiConfiguration;
22import android.net.wifi.WifiManager;
23import android.util.Base64;
24import android.util.Log;
25import android.util.SparseIntArray;
26
27import com.android.server.wifi.hotspot2.NetworkDetail;
28import com.android.server.wifi.util.InformationElementUtil;
29import com.android.server.wifi.util.ScanResultUtil;
30
31import java.io.FileDescriptor;
32import java.io.PrintWriter;
33import java.util.ArrayList;
34import java.util.Calendar;
35import java.util.List;
36
37/**
38 * Provides storage for wireless connectivity metrics, as they are generated.
39 * Metrics logged by this class include:
40 *   Aggregated connection stats (num of connections, num of failures, ...)
41 *   Discrete connection event stats (time, duration, failure codes, ...)
42 *   Router details (technology type, authentication type, ...)
43 *   Scan stats
44 */
45public class WifiMetrics {
46    private static final String TAG = "WifiMetrics";
47    private static final boolean DBG = false;
48    /**
49     * Clamp the RSSI poll counts to values between [MIN,MAX]_RSSI_POLL
50     */
51    private static final int MAX_RSSI_POLL = 0;
52    private static final int MIN_RSSI_POLL = -127;
53    public static final int MAX_RSSI_DELTA = 127;
54    public static final int MIN_RSSI_DELTA = -127;
55    /** Maximum time period between ScanResult and RSSI poll to generate rssi delta datapoint */
56    public static final long TIMEOUT_RSSI_DELTA_MILLIS =  3000;
57    private static final int MIN_WIFI_SCORE = 0;
58    private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
59    private final Object mLock = new Object();
60    private static final int MAX_CONNECTION_EVENTS = 256;
61    private Clock mClock;
62    private boolean mScreenOn;
63    private int mWifiState;
64    /**
65     * Metrics are stored within an instance of the WifiLog proto during runtime,
66     * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
67     * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
68     * together at dump-time
69     */
70    private final WifiMetricsProto.WifiLog mWifiLogProto = new WifiMetricsProto.WifiLog();
71    /**
72     * Session information that gets logged for every Wifi connection attempt.
73     */
74    private final List<ConnectionEvent> mConnectionEventList = new ArrayList<>();
75    /**
76     * The latest started (but un-ended) connection attempt
77     */
78    private ConnectionEvent mCurrentConnectionEvent;
79    /**
80     * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
81     */
82    private final SparseIntArray mScanReturnEntries = new SparseIntArray();
83    /**
84     * Mapping of system state to the counts of scans requested in that wifi state * screenOn
85     * combination. Indexed by WifiLog.WifiState * (1 + screenOn)
86     */
87    private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
88    /** Mapping of RSSI values to counts. */
89    private final SparseIntArray mRssiPollCounts = new SparseIntArray();
90    /** Mapping of RSSI scan-poll delta values to counts. */
91    private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
92    /** RSSI of the scan result for the last connection event*/
93    private int mScanResultRssi = 0;
94    /** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
95        RSSI deltas. -1 designates no candidate scanResult being tracked */
96    private long mScanResultRssiTimestampMillis = -1;
97    /** Mapping of alert reason to the respective alert count. */
98    private final SparseIntArray mWifiAlertReasonCounts = new SparseIntArray();
99    /**
100     * Records the getElapsedSinceBootMillis (in seconds) that represents the beginning of data
101     * capture for for this WifiMetricsProto
102     */
103    private long mRecordStartTimeSec;
104    /** Mapping of Wifi Scores to counts */
105    private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
106    /** Mapping of SoftApManager start SoftAp return codes to counts */
107    private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
108    class RouterFingerPrint {
109        private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
110        RouterFingerPrint() {
111            mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
112        }
113
114        public String toString() {
115            StringBuilder sb = new StringBuilder();
116            synchronized (mLock) {
117                sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
118                sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
119                sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
120                sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
121                sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
122                sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
123                sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
124            }
125            return sb.toString();
126        }
127        public void updateFromWifiConfiguration(WifiConfiguration config) {
128            synchronized (mLock) {
129                if (config != null) {
130                    // Is this a hidden network
131                    mRouterFingerPrintProto.hidden = config.hiddenSSID;
132                    // Config may not have a valid dtimInterval set yet, in which case dtim will be zero
133                    // (These are only populated from beacon frame scan results, which are returned as
134                    // scan results from the chip far less frequently than Probe-responses)
135                    if (config.dtimInterval > 0) {
136                        mRouterFingerPrintProto.dtim = config.dtimInterval;
137                    }
138                    mCurrentConnectionEvent.mConfigSsid = config.SSID;
139                    // Get AuthType information from config (We do this again from ScanResult after
140                    // associating with BSSID)
141                    if (config.allowedKeyManagement != null
142                            && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
143                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
144                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
145                    } else if (config.isEnterprise()) {
146                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
147                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
148                    } else {
149                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
150                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
151                    }
152                    mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
153                            .passpoint = config.isPasspoint();
154                    // If there's a ScanResult candidate associated with this config already, get it and
155                    // log (more accurate) metrics from it
156                    ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
157                    if (candidate != null) {
158                        updateMetricsFromScanResult(candidate);
159                    }
160                }
161            }
162        }
163    }
164
165    /**
166     * Log event, tracking the start time, end time and result of a wireless connection attempt.
167     */
168    class ConnectionEvent {
169        WifiMetricsProto.ConnectionEvent mConnectionEvent;
170        //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
171        //covering more than just l2 failures. see b/27652362
172        /**
173         * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
174         * more failures than just l2 though, since the proto does not have a place to log
175         * framework failures)
176         */
177        // Failure is unknown
178        public static final int FAILURE_UNKNOWN = 0;
179        // NONE
180        public static final int FAILURE_NONE = 1;
181        // ASSOCIATION_REJECTION_EVENT
182        public static final int FAILURE_ASSOCIATION_REJECTION = 2;
183        // AUTHENTICATION_FAILURE_EVENT
184        public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
185        // SSID_TEMP_DISABLED (Also Auth failure)
186        public static final int FAILURE_SSID_TEMP_DISABLED = 4;
187        // reconnect() or reassociate() call to WifiNative failed
188        public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
189        // NETWORK_DISCONNECTION_EVENT
190        public static final int FAILURE_NETWORK_DISCONNECTION = 6;
191        // NEW_CONNECTION_ATTEMPT before previous finished
192        public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
193        // New connection attempt to the same network & bssid
194        public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
195        // Roam Watchdog timer triggered (Roaming timed out)
196        public static final int FAILURE_ROAM_TIMEOUT = 9;
197        // DHCP failure
198        public static final int FAILURE_DHCP = 10;
199
200        RouterFingerPrint mRouterFingerPrint;
201        private long mRealStartTime;
202        private long mRealEndTime;
203        private String mConfigSsid;
204        private String mConfigBssid;
205        private int mWifiState;
206        private boolean mScreenOn;
207
208        private ConnectionEvent() {
209            mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
210            mRealEndTime = 0;
211            mRealStartTime = 0;
212            mRouterFingerPrint = new RouterFingerPrint();
213            mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
214            mConfigSsid = "<NULL>";
215            mConfigBssid = "<NULL>";
216            mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
217            mScreenOn = false;
218        }
219
220        public String toString() {
221            StringBuilder sb = new StringBuilder();
222            sb.append("startTime=");
223            Calendar c = Calendar.getInstance();
224            synchronized (mLock) {
225                c.setTimeInMillis(mConnectionEvent.startTimeMillis);
226                sb.append(mConnectionEvent.startTimeMillis == 0 ? "            <null>" :
227                        String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
228                sb.append(", SSID=");
229                sb.append(mConfigSsid);
230                sb.append(", BSSID=");
231                sb.append(mConfigBssid);
232                sb.append(", durationMillis=");
233                sb.append(mConnectionEvent.durationTakenToConnectMillis);
234                sb.append(", roamType=");
235                switch(mConnectionEvent.roamType) {
236                    case 1:
237                        sb.append("ROAM_NONE");
238                        break;
239                    case 2:
240                        sb.append("ROAM_DBDC");
241                        break;
242                    case 3:
243                        sb.append("ROAM_ENTERPRISE");
244                        break;
245                    case 4:
246                        sb.append("ROAM_USER_SELECTED");
247                        break;
248                    case 5:
249                        sb.append("ROAM_UNRELATED");
250                        break;
251                    default:
252                        sb.append("ROAM_UNKNOWN");
253                }
254                sb.append(", connectionResult=");
255                sb.append(mConnectionEvent.connectionResult);
256                sb.append(", level2FailureCode=");
257                switch(mConnectionEvent.level2FailureCode) {
258                    case FAILURE_NONE:
259                        sb.append("NONE");
260                        break;
261                    case FAILURE_ASSOCIATION_REJECTION:
262                        sb.append("ASSOCIATION_REJECTION");
263                        break;
264                    case FAILURE_AUTHENTICATION_FAILURE:
265                        sb.append("AUTHENTICATION_FAILURE");
266                        break;
267                    case FAILURE_SSID_TEMP_DISABLED:
268                        sb.append("SSID_TEMP_DISABLED");
269                        break;
270                    case FAILURE_CONNECT_NETWORK_FAILED:
271                        sb.append("CONNECT_NETWORK_FAILED");
272                        break;
273                    case FAILURE_NETWORK_DISCONNECTION:
274                        sb.append("NETWORK_DISCONNECTION");
275                        break;
276                    case FAILURE_NEW_CONNECTION_ATTEMPT:
277                        sb.append("NEW_CONNECTION_ATTEMPT");
278                        break;
279                    case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
280                        sb.append("REDUNDANT_CONNECTION_ATTEMPT");
281                        break;
282                    case FAILURE_ROAM_TIMEOUT:
283                        sb.append("ROAM_TIMEOUT");
284                        break;
285                    case FAILURE_DHCP:
286                        sb.append("DHCP");
287                    default:
288                        sb.append("UNKNOWN");
289                        break;
290                }
291                sb.append(", connectivityLevelFailureCode=");
292                switch(mConnectionEvent.connectivityLevelFailureCode) {
293                    case WifiMetricsProto.ConnectionEvent.HLF_NONE:
294                        sb.append("NONE");
295                        break;
296                    case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
297                        sb.append("DHCP");
298                        break;
299                    case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
300                        sb.append("NO_INTERNET");
301                        break;
302                    case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
303                        sb.append("UNWANTED");
304                        break;
305                    default:
306                        sb.append("UNKNOWN");
307                        break;
308                }
309                sb.append(", signalStrength=");
310                sb.append(mConnectionEvent.signalStrength);
311                sb.append(", wifiState=");
312                switch(mWifiState) {
313                    case WifiMetricsProto.WifiLog.WIFI_DISABLED:
314                        sb.append("WIFI_DISABLED");
315                        break;
316                    case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
317                        sb.append("WIFI_DISCONNECTED");
318                        break;
319                    case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
320                        sb.append("WIFI_ASSOCIATED");
321                        break;
322                    default:
323                        sb.append("WIFI_UNKNOWN");
324                        break;
325                }
326                sb.append(", screenOn=");
327                sb.append(mScreenOn);
328                sb.append(". mRouterFingerprint: ");
329                sb.append(mRouterFingerPrint.toString());
330            }
331            return sb.toString();
332        }
333    }
334
335    public WifiMetrics(Clock clock) {
336        mClock = clock;
337        mCurrentConnectionEvent = null;
338        mScreenOn = true;
339        mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
340        mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
341    }
342
343    // Values used for indexing SystemStateEntries
344    private static final int SCREEN_ON = 1;
345    private static final int SCREEN_OFF = 0;
346
347    /**
348     * Create a new connection event. Call when wifi attempts to make a new network connection
349     * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
350     * failure code.
351     * Gathers and sets the RouterFingerPrint data as well
352     *
353     * @param config WifiConfiguration of the config used for the current connection attempt
354     * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
355     */
356    public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
357        synchronized (mLock) {
358            // Check if this is overlapping another current connection event
359            if (mCurrentConnectionEvent != null) {
360                //Is this new Connection Event the same as the current one
361                if (mCurrentConnectionEvent.mConfigSsid != null
362                        && mCurrentConnectionEvent.mConfigBssid != null
363                        && config != null
364                        && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
365                        && (mCurrentConnectionEvent.mConfigBssid.equals("any")
366                        || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
367                    mCurrentConnectionEvent.mConfigBssid = targetBSSID;
368                    // End Connection Event due to new connection attempt to the same network
369                    endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
370                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
371                } else {
372                    // End Connection Event due to new connection attempt to different network
373                    endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
374                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
375                }
376            }
377            //If past maximum connection events, start removing the oldest
378            while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
379                mConnectionEventList.remove(0);
380            }
381            mCurrentConnectionEvent = new ConnectionEvent();
382            mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
383                    mClock.getWallClockMillis();
384            mCurrentConnectionEvent.mConfigBssid = targetBSSID;
385            mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
386            mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
387            mCurrentConnectionEvent.mConfigBssid = "any";
388            mCurrentConnectionEvent.mRealStartTime = mClock.getElapsedSinceBootMillis();
389            mCurrentConnectionEvent.mWifiState = mWifiState;
390            mCurrentConnectionEvent.mScreenOn = mScreenOn;
391            mConnectionEventList.add(mCurrentConnectionEvent);
392            mScanResultRssiTimestampMillis = -1;
393            if (config != null) {
394                ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
395                if (candidate != null) {
396                    // Cache the RSSI of the candidate, as the connection event level is updated
397                    // from other sources (polls, bssid_associations) and delta requires the
398                    // scanResult rssi
399                    mScanResultRssi = candidate.level;
400                    mScanResultRssiTimestampMillis = mClock.getElapsedSinceBootMillis();
401                }
402            }
403        }
404    }
405
406    /**
407     * set the RoamType of the current ConnectionEvent (if any)
408     */
409    public void setConnectionEventRoamType(int roamType) {
410        synchronized (mLock) {
411            if (mCurrentConnectionEvent != null) {
412                mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
413            }
414        }
415    }
416
417    /**
418     * Set AP related metrics from ScanDetail
419     */
420    public void setConnectionScanDetail(ScanDetail scanDetail) {
421        synchronized (mLock) {
422            if (mCurrentConnectionEvent != null && scanDetail != null) {
423                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
424                ScanResult scanResult = scanDetail.getScanResult();
425                //Ensure that we have a networkDetail, and that it corresponds to the currently
426                //tracked connection attempt
427                if (networkDetail != null && scanResult != null
428                        && mCurrentConnectionEvent.mConfigSsid != null
429                        && mCurrentConnectionEvent.mConfigSsid
430                        .equals("\"" + networkDetail.getSSID() + "\"")) {
431                    updateMetricsFromNetworkDetail(networkDetail);
432                    updateMetricsFromScanResult(scanResult);
433                }
434            }
435        }
436    }
437
438    /**
439     * End a Connection event record. Call when wifi connection attempt succeeds or fails.
440     * If a Connection event has not been started and is active when .end is called, a new one is
441     * created with zero duration.
442     *
443     * @param level2FailureCode Level 2 failure code returned by supplicant
444     * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
445     */
446    public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
447        synchronized (mLock) {
448            if (mCurrentConnectionEvent != null) {
449                boolean result = (level2FailureCode == 1)
450                        && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
451                mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
452                mCurrentConnectionEvent.mRealEndTime = mClock.getElapsedSinceBootMillis();
453                mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
454                        (mCurrentConnectionEvent.mRealEndTime
455                        - mCurrentConnectionEvent.mRealStartTime);
456                mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
457                mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
458                        connectivityFailureCode;
459                // ConnectionEvent already added to ConnectionEvents List. Safe to null current here
460                mCurrentConnectionEvent = null;
461                if (!result) {
462                    mScanResultRssiTimestampMillis = -1;
463                }
464            }
465        }
466    }
467
468    /**
469     * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
470     */
471    private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
472        int dtimInterval = networkDetail.getDtimInterval();
473        if (dtimInterval > 0) {
474            mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
475                    dtimInterval;
476        }
477        int connectionWifiMode;
478        switch (networkDetail.getWifiMode()) {
479            case InformationElementUtil.WifiMode.MODE_UNDEFINED:
480                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
481                break;
482            case InformationElementUtil.WifiMode.MODE_11A:
483                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
484                break;
485            case InformationElementUtil.WifiMode.MODE_11B:
486                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
487                break;
488            case InformationElementUtil.WifiMode.MODE_11G:
489                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
490                break;
491            case InformationElementUtil.WifiMode.MODE_11N:
492                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
493                break;
494            case InformationElementUtil.WifiMode.MODE_11AC  :
495                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
496                break;
497            default:
498                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
499                break;
500        }
501        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
502                .routerTechnology = connectionWifiMode;
503    }
504
505    /**
506     * Set ConnectionEvent RSSI and authentication type from ScanResult
507     */
508    private void updateMetricsFromScanResult(ScanResult scanResult) {
509        mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
510        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
511                WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
512        mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
513        if (scanResult.capabilities != null) {
514            if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
515                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
516                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
517            } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
518                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
519                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
520            } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
521                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
522                        WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
523            }
524        }
525        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
526                scanResult.frequency;
527    }
528
529    void setIsLocationEnabled(boolean enabled) {
530        synchronized (mLock) {
531            mWifiLogProto.isLocationEnabled = enabled;
532        }
533    }
534
535    void setIsScanningAlwaysEnabled(boolean enabled) {
536        synchronized (mLock) {
537            mWifiLogProto.isScanningAlwaysEnabled = enabled;
538        }
539    }
540
541    /**
542     * Increment Non Empty Scan Results count
543     */
544    public void incrementNonEmptyScanResultCount() {
545        if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
546        synchronized (mLock) {
547            mWifiLogProto.numNonEmptyScanResults++;
548        }
549    }
550
551    /**
552     * Increment Empty Scan Results count
553     */
554    public void incrementEmptyScanResultCount() {
555        if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
556        synchronized (mLock) {
557            mWifiLogProto.numEmptyScanResults++;
558        }
559    }
560
561    /**
562     * Increment background scan count
563     */
564    public void incrementBackgroundScanCount() {
565        if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
566        synchronized (mLock) {
567            mWifiLogProto.numBackgroundScans++;
568        }
569    }
570
571   /**
572     * Get Background scan count
573     */
574    public int getBackgroundScanCount() {
575        synchronized (mLock) {
576            return mWifiLogProto.numBackgroundScans;
577        }
578    }
579
580    /**
581     * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
582     */
583    public void incrementOneshotScanCount() {
584        synchronized (mLock) {
585            mWifiLogProto.numOneshotScans++;
586        }
587        incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
588    }
589
590    /**
591     * Get oneshot scan count
592     */
593    public int getOneshotScanCount() {
594        synchronized (mLock) {
595            return mWifiLogProto.numOneshotScans;
596        }
597    }
598
599    private String returnCodeToString(int scanReturnCode) {
600        switch(scanReturnCode){
601            case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
602                return "SCAN_UNKNOWN";
603            case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
604                return "SCAN_SUCCESS";
605            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
606                return "SCAN_FAILURE_INTERRUPTED";
607            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
608                return "SCAN_FAILURE_INVALID_CONFIGURATION";
609            case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
610                return "FAILURE_WIFI_DISABLED";
611            default:
612                return "<UNKNOWN>";
613        }
614    }
615
616    /**
617     * Increment count of scan return code occurrence
618     *
619     * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
620     */
621    public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
622        synchronized (mLock) {
623            if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
624            int entry = mScanReturnEntries.get(scanReturnCode);
625            entry += countToAdd;
626            mScanReturnEntries.put(scanReturnCode, entry);
627        }
628    }
629    /**
630     * Get the count of this scanReturnCode
631     * @param scanReturnCode that we are getting the count for
632     */
633    public int getScanReturnEntry(int scanReturnCode) {
634        synchronized (mLock) {
635            return mScanReturnEntries.get(scanReturnCode);
636        }
637    }
638
639    private String wifiSystemStateToString(int state) {
640        switch(state){
641            case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
642                return "WIFI_UNKNOWN";
643            case WifiMetricsProto.WifiLog.WIFI_DISABLED:
644                return "WIFI_DISABLED";
645            case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
646                return "WIFI_DISCONNECTED";
647            case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
648                return "WIFI_ASSOCIATED";
649            default:
650                return "default";
651        }
652    }
653
654    /**
655     * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
656     *
657     * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
658     * @param screenOn Is the screen on
659     */
660    public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
661        synchronized (mLock) {
662            if (DBG) {
663                Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
664                        + " " + screenOn);
665            }
666            int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
667            int entry = mWifiSystemStateEntries.get(index);
668            entry++;
669            mWifiSystemStateEntries.put(index, entry);
670        }
671    }
672
673    /**
674     * Get the count of this system State Entry
675     */
676    public int getSystemStateCount(int state, boolean screenOn) {
677        synchronized (mLock) {
678            int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
679            return mWifiSystemStateEntries.get(index);
680        }
681    }
682
683    /**
684     * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
685     */
686    public void incrementNumLastResortWatchdogTriggers() {
687        synchronized (mLock) {
688            mWifiLogProto.numLastResortWatchdogTriggers++;
689        }
690    }
691    /**
692     * @param count number of networks over bad association threshold when watchdog triggered
693     */
694    public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
695        synchronized (mLock) {
696            mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
697        }
698    }
699    /**
700     * @param count number of networks over bad authentication threshold when watchdog triggered
701     */
702    public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
703        synchronized (mLock) {
704            mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
705        }
706    }
707    /**
708     * @param count number of networks over bad dhcp threshold when watchdog triggered
709     */
710    public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
711        synchronized (mLock) {
712            mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
713        }
714    }
715    /**
716     * @param count number of networks over bad other threshold when watchdog triggered
717     */
718    public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
719        synchronized (mLock) {
720            mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
721        }
722    }
723    /**
724     * @param count number of networks seen when watchdog triggered
725     */
726    public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
727        synchronized (mLock) {
728            mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
729        }
730    }
731    /**
732     * Increment count of triggers with atleast one bad association network
733     */
734    public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
735        synchronized (mLock) {
736            mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
737        }
738    }
739    /**
740     * Increment count of triggers with atleast one bad authentication network
741     */
742    public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
743        synchronized (mLock) {
744            mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
745        }
746    }
747    /**
748     * Increment count of triggers with atleast one bad dhcp network
749     */
750    public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
751        synchronized (mLock) {
752            mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
753        }
754    }
755    /**
756     * Increment count of triggers with atleast one bad other network
757     */
758    public void incrementNumLastResortWatchdogTriggersWithBadOther() {
759        synchronized (mLock) {
760            mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
761        }
762    }
763
764    /**
765     * Increment number of times connectivity watchdog confirmed pno is working
766     */
767    public void incrementNumConnectivityWatchdogPnoGood() {
768        synchronized (mLock) {
769            mWifiLogProto.numConnectivityWatchdogPnoGood++;
770        }
771    }
772    /**
773     * Increment number of times connectivity watchdog found pno not working
774     */
775    public void incrementNumConnectivityWatchdogPnoBad() {
776        synchronized (mLock) {
777            mWifiLogProto.numConnectivityWatchdogPnoBad++;
778        }
779    }
780    /**
781     * Increment number of times connectivity watchdog confirmed background scan is working
782     */
783    public void incrementNumConnectivityWatchdogBackgroundGood() {
784        synchronized (mLock) {
785            mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
786        }
787    }
788    /**
789     * Increment number of times connectivity watchdog found background scan not working
790     */
791    public void incrementNumConnectivityWatchdogBackgroundBad() {
792        synchronized (mLock) {
793            mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
794        }
795    }
796
797    /**
798     * Increment occurence count of RSSI level from RSSI poll.
799     * Ignores rssi values outside the bounds of [MIN_RSSI_POLL, MAX_RSSI_POLL]
800     */
801    public void incrementRssiPollRssiCount(int rssi) {
802        if (!(rssi >= MIN_RSSI_POLL && rssi <= MAX_RSSI_POLL)) {
803            return;
804        }
805        synchronized (mLock) {
806            int count = mRssiPollCounts.get(rssi);
807            mRssiPollCounts.put(rssi, count + 1);
808            maybeIncrementRssiDeltaCount(rssi - mScanResultRssi);
809        }
810    }
811
812    /**
813     * Increment occurence count of difference between scan result RSSI and the first RSSI poll.
814     * Ignores rssi values outside the bounds of [MIN_RSSI_DELTA, MAX_RSSI_DELTA]
815     * mLock must be held when calling this method.
816     */
817    private void maybeIncrementRssiDeltaCount(int rssi) {
818        // Check if this RSSI poll is close enough to a scan result RSSI to log a delta value
819        if (mScanResultRssiTimestampMillis >= 0) {
820            long timeDelta = mClock.getElapsedSinceBootMillis() - mScanResultRssiTimestampMillis;
821            if (timeDelta <= TIMEOUT_RSSI_DELTA_MILLIS) {
822                if (rssi >= MIN_RSSI_DELTA && rssi <= MAX_RSSI_DELTA) {
823                    int count = mRssiDeltaCounts.get(rssi);
824                    mRssiDeltaCounts.put(rssi, count + 1);
825                }
826            }
827            mScanResultRssiTimestampMillis = -1;
828        }
829    }
830
831    /**
832     * Increment count of Watchdog successes.
833     */
834    public void incrementNumLastResortWatchdogSuccesses() {
835        synchronized (mLock) {
836            mWifiLogProto.numLastResortWatchdogSuccesses++;
837        }
838    }
839
840    /**
841     * Increments the count of alerts by alert reason.
842     *
843     * @param reason The cause of the alert. The reason values are driver-specific.
844     */
845    public void incrementAlertReasonCount(int reason) {
846        if (reason > WifiLoggerHal.WIFI_ALERT_REASON_MAX
847                || reason < WifiLoggerHal.WIFI_ALERT_REASON_MIN) {
848            reason = WifiLoggerHal.WIFI_ALERT_REASON_RESERVED;
849        }
850        synchronized (mLock) {
851            int alertCount = mWifiAlertReasonCounts.get(reason);
852            mWifiAlertReasonCounts.put(reason, alertCount + 1);
853        }
854    }
855
856    /**
857     * Counts all the different types of networks seen in a set of scan results
858     */
859    public void countScanResults(List<ScanDetail> scanDetails) {
860        if (scanDetails == null) {
861            return;
862        }
863        int totalResults = 0;
864        int openNetworks = 0;
865        int personalNetworks = 0;
866        int enterpriseNetworks = 0;
867        int hiddenNetworks = 0;
868        int hotspot2r1Networks = 0;
869        int hotspot2r2Networks = 0;
870        for (ScanDetail scanDetail : scanDetails) {
871            NetworkDetail networkDetail = scanDetail.getNetworkDetail();
872            ScanResult scanResult = scanDetail.getScanResult();
873            totalResults++;
874            if (networkDetail != null) {
875                if (networkDetail.isHiddenBeaconFrame()) {
876                    hiddenNetworks++;
877                }
878                if (networkDetail.getHSRelease() != null) {
879                    if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
880                        hotspot2r1Networks++;
881                    } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
882                        hotspot2r2Networks++;
883                    }
884                }
885            }
886            if (scanResult != null && scanResult.capabilities != null) {
887                if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
888                    enterpriseNetworks++;
889                } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
890                        || ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
891                    personalNetworks++;
892                } else {
893                    openNetworks++;
894                }
895            }
896        }
897        synchronized (mLock) {
898            mWifiLogProto.numTotalScanResults += totalResults;
899            mWifiLogProto.numOpenNetworkScanResults += openNetworks;
900            mWifiLogProto.numPersonalNetworkScanResults += personalNetworks;
901            mWifiLogProto.numEnterpriseNetworkScanResults += enterpriseNetworks;
902            mWifiLogProto.numHiddenNetworkScanResults += hiddenNetworks;
903            mWifiLogProto.numHotspot2R1NetworkScanResults += hotspot2r1Networks;
904            mWifiLogProto.numHotspot2R2NetworkScanResults += hotspot2r2Networks;
905            mWifiLogProto.numScans++;
906        }
907    }
908
909    /**
910     * Increments occurence of a particular wifi score calculated
911     * in WifiScoreReport by current connected network. Scores are bounded
912     * within  [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray
913     */
914    public void incrementWifiScoreCount(int score) {
915        if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) {
916            return;
917        }
918        synchronized (mLock) {
919            int count = mWifiScoreCounts.get(score);
920            mWifiScoreCounts.put(score, count + 1);
921        }
922    }
923
924    /**
925     * Increments occurence of the results from attempting to start SoftAp.
926     * Maps the |result| and WifiManager |failureCode| constant to proto defined SoftApStartResult
927     * codes.
928     */
929    public void incrementSoftApStartResult(boolean result, int failureCode) {
930        synchronized (mLock) {
931            if (result) {
932                int count = mSoftApManagerReturnCodeCounts.get(
933                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY);
934                mSoftApManagerReturnCodeCounts.put(
935                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
936                        count + 1);
937                return;
938            }
939
940            // now increment failure modes - if not explicitly handled, dump into the general
941            // error bucket.
942            if (failureCode == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
943                int count = mSoftApManagerReturnCodeCounts.get(
944                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL);
945                mSoftApManagerReturnCodeCounts.put(
946                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
947                        count + 1);
948            } else {
949                // failure mode not tracked at this time...  count as a general error for now.
950                int count = mSoftApManagerReturnCodeCounts.get(
951                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR);
952                mSoftApManagerReturnCodeCounts.put(
953                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
954                        count + 1);
955            }
956        }
957    }
958
959    public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
960    public static final String CLEAN_DUMP_ARG = "clean";
961
962    /**
963     * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
964     * at this time.
965     *
966     * @param fd unused
967     * @param pw PrintWriter for writing dump to
968     * @param args unused
969     */
970    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
971        synchronized (mLock) {
972            if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
973                // Dump serialized WifiLog proto
974                consolidateProto(true);
975                for (ConnectionEvent event : mConnectionEventList) {
976                    if (mCurrentConnectionEvent != event) {
977                        //indicate that automatic bug report has been taken for all valid
978                        //connection events
979                        event.mConnectionEvent.automaticBugReportTaken = true;
980                    }
981                }
982                byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
983                String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
984                if (args.length > 1 && CLEAN_DUMP_ARG.equals(args[1])) {
985                    // Output metrics proto bytes (base64) and nothing else
986                    pw.print(metricsProtoDump);
987                } else {
988                    // Tag the start and end of the metrics proto bytes
989                    pw.println("WifiMetrics:");
990                    pw.println(metricsProtoDump);
991                    pw.println("EndWifiMetrics");
992                }
993                clear();
994            } else {
995                pw.println("WifiMetrics:");
996                pw.println("mConnectionEvents:");
997                for (ConnectionEvent event : mConnectionEventList) {
998                    String eventLine = event.toString();
999                    if (event == mCurrentConnectionEvent) {
1000                        eventLine += "CURRENTLY OPEN EVENT";
1001                    }
1002                    pw.println(eventLine);
1003                }
1004                pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
1005                pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
1006                pw.println("mWifiLogProto.numPersonalNetworks="
1007                        + mWifiLogProto.numPersonalNetworks);
1008                pw.println("mWifiLogProto.numEnterpriseNetworks="
1009                        + mWifiLogProto.numEnterpriseNetworks);
1010                pw.println("mWifiLogProto.numHiddenNetworks=" + mWifiLogProto.numHiddenNetworks);
1011                pw.println("mWifiLogProto.numPasspointNetworks="
1012                        + mWifiLogProto.numPasspointNetworks);
1013                pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
1014                pw.println("mWifiLogProto.isScanningAlwaysEnabled="
1015                        + mWifiLogProto.isScanningAlwaysEnabled);
1016                pw.println("mWifiLogProto.numNetworksAddedByUser="
1017                        + mWifiLogProto.numNetworksAddedByUser);
1018                pw.println("mWifiLogProto.numNetworksAddedByApps="
1019                        + mWifiLogProto.numNetworksAddedByApps);
1020                pw.println("mWifiLogProto.numNonEmptyScanResults="
1021                        + mWifiLogProto.numNonEmptyScanResults);
1022                pw.println("mWifiLogProto.numEmptyScanResults="
1023                        + mWifiLogProto.numEmptyScanResults);
1024                pw.println("mWifiLogProto.numOneshotScans="
1025                        + mWifiLogProto.numOneshotScans);
1026                pw.println("mWifiLogProto.numBackgroundScans="
1027                        + mWifiLogProto.numBackgroundScans);
1028
1029                pw.println("mScanReturnEntries:");
1030                pw.println("  SCAN_UNKNOWN: " + getScanReturnEntry(
1031                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
1032                pw.println("  SCAN_SUCCESS: " + getScanReturnEntry(
1033                        WifiMetricsProto.WifiLog.SCAN_SUCCESS));
1034                pw.println("  SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
1035                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
1036                pw.println("  SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
1037                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
1038                pw.println("  FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
1039                        WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
1040
1041                pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
1042                pw.println("  WIFI_UNKNOWN       ON: "
1043                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
1044                pw.println("  WIFI_DISABLED      ON: "
1045                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
1046                pw.println("  WIFI_DISCONNECTED  ON: "
1047                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
1048                pw.println("  WIFI_ASSOCIATED    ON: "
1049                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
1050                pw.println("  WIFI_UNKNOWN      OFF: "
1051                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
1052                pw.println("  WIFI_DISABLED     OFF: "
1053                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
1054                pw.println("  WIFI_DISCONNECTED OFF: "
1055                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
1056                pw.println("  WIFI_ASSOCIATED   OFF: "
1057                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
1058                pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
1059                        + mWifiLogProto.numConnectivityWatchdogPnoGood);
1060                pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
1061                        + mWifiLogProto.numConnectivityWatchdogPnoBad);
1062                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
1063                        + mWifiLogProto.numConnectivityWatchdogBackgroundGood);
1064                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
1065                        + mWifiLogProto.numConnectivityWatchdogBackgroundBad);
1066                pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
1067                        + mWifiLogProto.numLastResortWatchdogTriggers);
1068                pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
1069                        + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
1070                pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
1071                        + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
1072                pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
1073                        + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
1074                pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
1075                        + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
1076                pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
1077                        + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
1078                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
1079                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
1080                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
1081                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
1082                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
1083                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
1084                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
1085                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
1086                pw.println("mWifiLogProto.numLastResortWatchdogSuccesses="
1087                        + mWifiLogProto.numLastResortWatchdogSuccesses);
1088                pw.println("mWifiLogProto.recordDurationSec="
1089                        + ((mClock.getElapsedSinceBootMillis() / 1000) - mRecordStartTimeSec));
1090                pw.println("mWifiLogProto.rssiPollRssiCount: Printing counts for [" + MIN_RSSI_POLL
1091                        + ", " + MAX_RSSI_POLL + "]");
1092                StringBuilder sb = new StringBuilder();
1093                for (int i = MIN_RSSI_POLL; i <= MAX_RSSI_POLL; i++) {
1094                    sb.append(mRssiPollCounts.get(i) + " ");
1095                }
1096                pw.println("  " + sb.toString());
1097                pw.println("mWifiLogProto.rssiPollDeltaCount: Printing counts for ["
1098                        + MIN_RSSI_DELTA + ", " + MAX_RSSI_DELTA + "]");
1099                sb.setLength(0);
1100                for (int i = MIN_RSSI_DELTA; i <= MAX_RSSI_DELTA; i++) {
1101                    sb.append(mRssiDeltaCounts.get(i) + " ");
1102                }
1103                pw.println("  " + sb.toString());
1104                pw.print("mWifiLogProto.alertReasonCounts=");
1105                sb.setLength(0);
1106                for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN;
1107                        i <= WifiLoggerHal.WIFI_ALERT_REASON_MAX; i++) {
1108                    int count = mWifiAlertReasonCounts.get(i);
1109                    if (count > 0) {
1110                        sb.append("(" + i + "," + count + "),");
1111                    }
1112                }
1113                if (sb.length() > 1) {
1114                    sb.setLength(sb.length() - 1);  // strip trailing comma
1115                    pw.println(sb.toString());
1116                } else {
1117                    pw.println("()");
1118                }
1119                pw.println("mWifiLogProto.numTotalScanResults="
1120                        + mWifiLogProto.numTotalScanResults);
1121                pw.println("mWifiLogProto.numOpenNetworkScanResults="
1122                        + mWifiLogProto.numOpenNetworkScanResults);
1123                pw.println("mWifiLogProto.numPersonalNetworkScanResults="
1124                        + mWifiLogProto.numPersonalNetworkScanResults);
1125                pw.println("mWifiLogProto.numEnterpriseNetworkScanResults="
1126                        + mWifiLogProto.numEnterpriseNetworkScanResults);
1127                pw.println("mWifiLogProto.numHiddenNetworkScanResults="
1128                        + mWifiLogProto.numHiddenNetworkScanResults);
1129                pw.println("mWifiLogProto.numHotspot2R1NetworkScanResults="
1130                        + mWifiLogProto.numHotspot2R1NetworkScanResults);
1131                pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults="
1132                        + mWifiLogProto.numHotspot2R2NetworkScanResults);
1133                pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans);
1134                pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", "
1135                        + MAX_WIFI_SCORE + "]");
1136                for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
1137                    pw.print(mWifiScoreCounts.get(i) + " ");
1138                }
1139                pw.println(); // add a line after wifi scores
1140                pw.println("mWifiLogProto.SoftApManagerReturnCodeCounts:");
1141                pw.println("  SUCCESS: " + mSoftApManagerReturnCodeCounts.get(
1142                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY));
1143                pw.println("  FAILED_GENERAL_ERROR: " + mSoftApManagerReturnCodeCounts.get(
1144                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR));
1145                pw.println("  FAILED_NO_CHANNEL: " + mSoftApManagerReturnCodeCounts.get(
1146                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL));
1147                pw.print("\n");
1148            }
1149        }
1150    }
1151
1152
1153    /**
1154     * Update various counts of saved network types
1155     * @param networks List of WifiConfigurations representing all saved networks, must not be null
1156     */
1157    public void updateSavedNetworks(List<WifiConfiguration> networks) {
1158        synchronized (mLock) {
1159            mWifiLogProto.numSavedNetworks = networks.size();
1160            mWifiLogProto.numOpenNetworks = 0;
1161            mWifiLogProto.numPersonalNetworks = 0;
1162            mWifiLogProto.numEnterpriseNetworks = 0;
1163            mWifiLogProto.numNetworksAddedByUser = 0;
1164            mWifiLogProto.numNetworksAddedByApps = 0;
1165            mWifiLogProto.numHiddenNetworks = 0;
1166            mWifiLogProto.numPasspointNetworks = 0;
1167            for (WifiConfiguration config : networks) {
1168                if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
1169                    mWifiLogProto.numOpenNetworks++;
1170                } else if (config.isEnterprise()) {
1171                    mWifiLogProto.numEnterpriseNetworks++;
1172                } else {
1173                    mWifiLogProto.numPersonalNetworks++;
1174                }
1175                if (config.selfAdded) {
1176                    mWifiLogProto.numNetworksAddedByUser++;
1177                } else {
1178                    mWifiLogProto.numNetworksAddedByApps++;
1179                }
1180                if (config.hiddenSSID) {
1181                    mWifiLogProto.numHiddenNetworks++;
1182                }
1183                if (config.isPasspoint()) {
1184                    mWifiLogProto.numPasspointNetworks++;
1185                }
1186            }
1187        }
1188    }
1189
1190    /**
1191     * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
1192     * respective lists within mWifiLogProto
1193     *
1194     * @param incremental Only include ConnectionEvents created since last automatic bug report
1195     */
1196    private void consolidateProto(boolean incremental) {
1197        List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
1198        List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
1199        List<WifiMetricsProto.RssiPollCount> rssiDeltas = new ArrayList<>();
1200        List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
1201        List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
1202        synchronized (mLock) {
1203            for (ConnectionEvent event : mConnectionEventList) {
1204                // If this is not incremental, dump full ConnectionEvent list
1205                // Else Dump all un-dumped events except for the current one
1206                if (!incremental || ((mCurrentConnectionEvent != event)
1207                        && !event.mConnectionEvent.automaticBugReportTaken)) {
1208                    //Get all ConnectionEvents that haven not been dumped as a proto, also exclude
1209                    //the current active un-ended connection event
1210                    events.add(event.mConnectionEvent);
1211                    if (incremental) {
1212                        event.mConnectionEvent.automaticBugReportTaken = true;
1213                    }
1214                }
1215            }
1216            if (events.size() > 0) {
1217                mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
1218            }
1219
1220            //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
1221            mWifiLogProto.scanReturnEntries =
1222                    new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
1223            for (int i = 0; i < mScanReturnEntries.size(); i++) {
1224                mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
1225                mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
1226                mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
1227            }
1228
1229            // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
1230            // This one is slightly more complex, as the Sparse are indexed with:
1231            //     key: wifiState * 2 + isScreenOn, value: wifiStateCount
1232            mWifiLogProto.wifiSystemStateEntries =
1233                    new WifiMetricsProto.WifiLog
1234                    .WifiSystemStateEntry[mWifiSystemStateEntries.size()];
1235            for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
1236                mWifiLogProto.wifiSystemStateEntries[i] =
1237                        new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
1238                mWifiLogProto.wifiSystemStateEntries[i].wifiState =
1239                        mWifiSystemStateEntries.keyAt(i) / 2;
1240                mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
1241                        mWifiSystemStateEntries.valueAt(i);
1242                mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
1243                        (mWifiSystemStateEntries.keyAt(i) % 2) > 0;
1244            }
1245            mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
1246                    - mRecordStartTimeSec);
1247
1248            /**
1249             * Convert the SparseIntArray of RSSI poll rssi's and counts to the proto's repeated
1250             * IntKeyVal array.
1251             */
1252            for (int i = 0; i < mRssiPollCounts.size(); i++) {
1253                WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1254                keyVal.rssi = mRssiPollCounts.keyAt(i);
1255                keyVal.count = mRssiPollCounts.valueAt(i);
1256                rssis.add(keyVal);
1257            }
1258            mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount);
1259
1260            /**
1261             * Convert the SparseIntArray of RSSI delta rssi's and counts to the proto's repeated
1262             * IntKeyVal array.
1263             */
1264            for (int i = 0; i < mRssiDeltaCounts.size(); i++) {
1265                WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1266                keyVal.rssi = mRssiDeltaCounts.keyAt(i);
1267                keyVal.count = mRssiDeltaCounts.valueAt(i);
1268                rssiDeltas.add(keyVal);
1269            }
1270            mWifiLogProto.rssiPollDeltaCount = rssiDeltas.toArray(mWifiLogProto.rssiPollDeltaCount);
1271
1272            /**
1273             * Convert the SparseIntArray of alert reasons and counts to the proto's repeated
1274             * IntKeyVal array.
1275             */
1276            for (int i = 0; i < mWifiAlertReasonCounts.size(); i++) {
1277                WifiMetricsProto.AlertReasonCount keyVal = new WifiMetricsProto.AlertReasonCount();
1278                keyVal.reason = mWifiAlertReasonCounts.keyAt(i);
1279                keyVal.count = mWifiAlertReasonCounts.valueAt(i);
1280                alertReasons.add(keyVal);
1281            }
1282            mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
1283            /**
1284            *  Convert the SparseIntArray of Wifi Score and counts to proto's repeated
1285            * IntKeyVal array.
1286            */
1287            for (int score = 0; score < mWifiScoreCounts.size(); score++) {
1288                WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount();
1289                keyVal.score = mWifiScoreCounts.keyAt(score);
1290                keyVal.count = mWifiScoreCounts.valueAt(score);
1291                scores.add(keyVal);
1292            }
1293            mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
1294
1295            /**
1296             * Convert the SparseIntArray of SoftAp Return codes and counts to proto's repeated
1297             * IntKeyVal array.
1298             */
1299            int codeCounts = mSoftApManagerReturnCodeCounts.size();
1300            mWifiLogProto.softApReturnCode = new WifiMetricsProto.SoftApReturnCodeCount[codeCounts];
1301            for (int sapCode = 0; sapCode < codeCounts; sapCode++) {
1302                mWifiLogProto.softApReturnCode[sapCode] =
1303                        new WifiMetricsProto.SoftApReturnCodeCount();
1304                mWifiLogProto.softApReturnCode[sapCode].startResult =
1305                        mSoftApManagerReturnCodeCounts.keyAt(sapCode);
1306                mWifiLogProto.softApReturnCode[sapCode].count =
1307                        mSoftApManagerReturnCodeCounts.valueAt(sapCode);
1308            }
1309        }
1310    }
1311
1312    /**
1313     * Clear all WifiMetrics, except for currentConnectionEvent.
1314     */
1315    private void clear() {
1316        synchronized (mLock) {
1317            mConnectionEventList.clear();
1318            if (mCurrentConnectionEvent != null) {
1319                mConnectionEventList.add(mCurrentConnectionEvent);
1320            }
1321            mScanReturnEntries.clear();
1322            mWifiSystemStateEntries.clear();
1323            mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
1324            mRssiPollCounts.clear();
1325            mRssiDeltaCounts.clear();
1326            mWifiAlertReasonCounts.clear();
1327            mWifiScoreCounts.clear();
1328            mWifiLogProto.clear();
1329            mScanResultRssiTimestampMillis = -1;
1330            mSoftApManagerReturnCodeCounts.clear();
1331        }
1332    }
1333
1334    /**
1335     *  Set screen state (On/Off)
1336     */
1337    public void setScreenState(boolean screenOn) {
1338        synchronized (mLock) {
1339            mScreenOn = screenOn;
1340        }
1341    }
1342
1343    /**
1344     *  Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
1345     */
1346    public void setWifiState(int wifiState) {
1347        synchronized (mLock) {
1348            mWifiState = wifiState;
1349        }
1350    }
1351}
1352