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