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