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