WifiMetrics.java revision d582c6dfdbb3989b8b761a6cba608a0a3ce35df2
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.hardware.wifi.supplicant.V1_0.ISupplicantStaIfaceCallback;
20import android.net.NetworkAgent;
21import android.net.wifi.ScanResult;
22import android.net.wifi.SupplicantState;
23import android.net.wifi.WifiConfiguration;
24import android.net.wifi.WifiInfo;
25import android.net.wifi.WifiManager;
26import android.os.Handler;
27import android.os.Looper;
28import android.os.Message;
29import android.util.Base64;
30import android.util.Log;
31import android.util.Pair;
32import android.util.SparseIntArray;
33
34import com.android.server.wifi.aware.WifiAwareMetrics;
35import com.android.server.wifi.hotspot2.NetworkDetail;
36import com.android.server.wifi.hotspot2.PasspointManager;
37import com.android.server.wifi.hotspot2.PasspointMatch;
38import com.android.server.wifi.hotspot2.PasspointProvider;
39import com.android.server.wifi.nano.WifiMetricsProto;
40import com.android.server.wifi.nano.WifiMetricsProto.StaEvent;
41import com.android.server.wifi.nano.WifiMetricsProto.StaEvent.ConfigInfo;
42import com.android.server.wifi.util.InformationElementUtil;
43import com.android.server.wifi.util.ScanResultUtil;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.ArrayList;
48import java.util.BitSet;
49import java.util.Calendar;
50import java.util.HashSet;
51import java.util.LinkedList;
52import java.util.List;
53import java.util.Set;
54
55/**
56 * Provides storage for wireless connectivity metrics, as they are generated.
57 * Metrics logged by this class include:
58 *   Aggregated connection stats (num of connections, num of failures, ...)
59 *   Discrete connection event stats (time, duration, failure codes, ...)
60 *   Router details (technology type, authentication type, ...)
61 *   Scan stats
62 */
63public class WifiMetrics {
64    private static final String TAG = "WifiMetrics";
65    private static final boolean DBG = false;
66    /**
67     * Clamp the RSSI poll counts to values between [MIN,MAX]_RSSI_POLL
68     */
69    private static final int MAX_RSSI_POLL = 0;
70    private static final int MIN_RSSI_POLL = -127;
71    public static final int MAX_RSSI_DELTA = 127;
72    public static final int MIN_RSSI_DELTA = -127;
73    /** Maximum time period between ScanResult and RSSI poll to generate rssi delta datapoint */
74    public static final long TIMEOUT_RSSI_DELTA_MILLIS =  3000;
75    private static final int MIN_WIFI_SCORE = 0;
76    private static final int MAX_WIFI_SCORE = NetworkAgent.WIFI_BASE_SCORE;
77    private final Object mLock = new Object();
78    private static final int MAX_CONNECTION_EVENTS = 256;
79    // Largest bucket in the NumConnectableNetworkCount histogram,
80    // anything large will be stored in this bucket
81    public static final int MAX_CONNECTABLE_SSID_NETWORK_BUCKET = 20;
82    public static final int MAX_CONNECTABLE_BSSID_NETWORK_BUCKET = 50;
83    public static final int MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET = 100;
84    public static final int MAX_TOTAL_SCAN_RESULTS_BUCKET = 250;
85    private Clock mClock;
86    private boolean mScreenOn;
87    private int mWifiState;
88    private WifiAwareMetrics mWifiAwareMetrics;
89    private Handler mHandler;
90    private WifiConfigManager mWifiConfigManager;
91    private WifiNetworkSelector mWifiNetworkSelector;
92    private PasspointManager mPasspointManager;
93    /**
94     * Metrics are stored within an instance of the WifiLog proto during runtime,
95     * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
96     * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
97     * together at dump-time
98     */
99    private final WifiMetricsProto.WifiLog mWifiLogProto = new WifiMetricsProto.WifiLog();
100    /**
101     * Session information that gets logged for every Wifi connection attempt.
102     */
103    private final List<ConnectionEvent> mConnectionEventList = new ArrayList<>();
104    /**
105     * The latest started (but un-ended) connection attempt
106     */
107    private ConnectionEvent mCurrentConnectionEvent;
108    /**
109     * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
110     */
111    private final SparseIntArray mScanReturnEntries = new SparseIntArray();
112    /**
113     * Mapping of system state to the counts of scans requested in that wifi state * screenOn
114     * combination. Indexed by WifiLog.WifiState * (1 + screenOn)
115     */
116    private final SparseIntArray mWifiSystemStateEntries = new SparseIntArray();
117    /** Mapping of RSSI values to counts. */
118    private final SparseIntArray mRssiPollCounts = new SparseIntArray();
119    /** Mapping of RSSI scan-poll delta values to counts. */
120    private final SparseIntArray mRssiDeltaCounts = new SparseIntArray();
121    /** RSSI of the scan result for the last connection event*/
122    private int mScanResultRssi = 0;
123    /** Boot-relative timestamp when the last candidate scanresult was received, used to calculate
124        RSSI deltas. -1 designates no candidate scanResult being tracked */
125    private long mScanResultRssiTimestampMillis = -1;
126    /** Mapping of alert reason to the respective alert count. */
127    private final SparseIntArray mWifiAlertReasonCounts = new SparseIntArray();
128    /**
129     * Records the getElapsedSinceBootMillis (in seconds) that represents the beginning of data
130     * capture for for this WifiMetricsProto
131     */
132    private long mRecordStartTimeSec;
133    /** Mapping of Wifi Scores to counts */
134    private final SparseIntArray mWifiScoreCounts = new SparseIntArray();
135    /** Mapping of SoftApManager start SoftAp return codes to counts */
136    private final SparseIntArray mSoftApManagerReturnCodeCounts = new SparseIntArray();
137
138    private final SparseIntArray mTotalSsidsInScanHistogram = new SparseIntArray();
139    private final SparseIntArray mTotalBssidsInScanHistogram = new SparseIntArray();
140    private final SparseIntArray mAvailableOpenSsidsInScanHistogram = new SparseIntArray();
141    private final SparseIntArray mAvailableOpenBssidsInScanHistogram = new SparseIntArray();
142    private final SparseIntArray mAvailableSavedSsidsInScanHistogram = new SparseIntArray();
143    private final SparseIntArray mAvailableSavedBssidsInScanHistogram = new SparseIntArray();
144    private final SparseIntArray mAvailableOpenOrSavedSsidsInScanHistogram = new SparseIntArray();
145    private final SparseIntArray mAvailableOpenOrSavedBssidsInScanHistogram = new SparseIntArray();
146    private final SparseIntArray mAvailableSavedPasspointProviderProfilesInScanHistogram =
147            new SparseIntArray();
148    private final SparseIntArray mAvailableSavedPasspointProviderBssidsInScanHistogram =
149            new SparseIntArray();
150
151    class RouterFingerPrint {
152        private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
153        RouterFingerPrint() {
154            mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
155        }
156
157        public String toString() {
158            StringBuilder sb = new StringBuilder();
159            synchronized (mLock) {
160                sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
161                sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
162                sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
163                sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
164                sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
165                sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
166                sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
167            }
168            return sb.toString();
169        }
170        public void updateFromWifiConfiguration(WifiConfiguration config) {
171            synchronized (mLock) {
172                if (config != null) {
173                    // Is this a hidden network
174                    mRouterFingerPrintProto.hidden = config.hiddenSSID;
175                    // Config may not have a valid dtimInterval set yet, in which case dtim will be zero
176                    // (These are only populated from beacon frame scan results, which are returned as
177                    // scan results from the chip far less frequently than Probe-responses)
178                    if (config.dtimInterval > 0) {
179                        mRouterFingerPrintProto.dtim = config.dtimInterval;
180                    }
181                    mCurrentConnectionEvent.mConfigSsid = config.SSID;
182                    // Get AuthType information from config (We do this again from ScanResult after
183                    // associating with BSSID)
184                    if (config.allowedKeyManagement != null
185                            && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
186                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
187                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
188                    } else if (config.isEnterprise()) {
189                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
190                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
191                    } else {
192                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
193                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
194                    }
195                    mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
196                            .passpoint = config.isPasspoint();
197                    // If there's a ScanResult candidate associated with this config already, get it and
198                    // log (more accurate) metrics from it
199                    ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
200                    if (candidate != null) {
201                        updateMetricsFromScanResult(candidate);
202                    }
203                }
204            }
205        }
206    }
207
208    /**
209     * Log event, tracking the start time, end time and result of a wireless connection attempt.
210     */
211    class ConnectionEvent {
212        WifiMetricsProto.ConnectionEvent mConnectionEvent;
213        //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
214        //covering more than just l2 failures. see b/27652362
215        /**
216         * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
217         * more failures than just l2 though, since the proto does not have a place to log
218         * framework failures)
219         */
220        // Failure is unknown
221        public static final int FAILURE_UNKNOWN = 0;
222        // NONE
223        public static final int FAILURE_NONE = 1;
224        // ASSOCIATION_REJECTION_EVENT
225        public static final int FAILURE_ASSOCIATION_REJECTION = 2;
226        // AUTHENTICATION_FAILURE_EVENT
227        public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
228        // SSID_TEMP_DISABLED (Also Auth failure)
229        public static final int FAILURE_SSID_TEMP_DISABLED = 4;
230        // reconnect() or reassociate() call to WifiNative failed
231        public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
232        // NETWORK_DISCONNECTION_EVENT
233        public static final int FAILURE_NETWORK_DISCONNECTION = 6;
234        // NEW_CONNECTION_ATTEMPT before previous finished
235        public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
236        // New connection attempt to the same network & bssid
237        public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
238        // Roam Watchdog timer triggered (Roaming timed out)
239        public static final int FAILURE_ROAM_TIMEOUT = 9;
240        // DHCP failure
241        public static final int FAILURE_DHCP = 10;
242
243        RouterFingerPrint mRouterFingerPrint;
244        private long mRealStartTime;
245        private long mRealEndTime;
246        private String mConfigSsid;
247        private String mConfigBssid;
248        private int mWifiState;
249        private boolean mScreenOn;
250
251        private ConnectionEvent() {
252            mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
253            mRealEndTime = 0;
254            mRealStartTime = 0;
255            mRouterFingerPrint = new RouterFingerPrint();
256            mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
257            mConfigSsid = "<NULL>";
258            mConfigBssid = "<NULL>";
259            mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
260            mScreenOn = false;
261        }
262
263        public String toString() {
264            StringBuilder sb = new StringBuilder();
265            sb.append("startTime=");
266            Calendar c = Calendar.getInstance();
267            synchronized (mLock) {
268                c.setTimeInMillis(mConnectionEvent.startTimeMillis);
269                sb.append(mConnectionEvent.startTimeMillis == 0 ? "            <null>" :
270                        String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
271                sb.append(", SSID=");
272                sb.append(mConfigSsid);
273                sb.append(", BSSID=");
274                sb.append(mConfigBssid);
275                sb.append(", durationMillis=");
276                sb.append(mConnectionEvent.durationTakenToConnectMillis);
277                sb.append(", roamType=");
278                switch(mConnectionEvent.roamType) {
279                    case 1:
280                        sb.append("ROAM_NONE");
281                        break;
282                    case 2:
283                        sb.append("ROAM_DBDC");
284                        break;
285                    case 3:
286                        sb.append("ROAM_ENTERPRISE");
287                        break;
288                    case 4:
289                        sb.append("ROAM_USER_SELECTED");
290                        break;
291                    case 5:
292                        sb.append("ROAM_UNRELATED");
293                        break;
294                    default:
295                        sb.append("ROAM_UNKNOWN");
296                }
297                sb.append(", connectionResult=");
298                sb.append(mConnectionEvent.connectionResult);
299                sb.append(", level2FailureCode=");
300                switch(mConnectionEvent.level2FailureCode) {
301                    case FAILURE_NONE:
302                        sb.append("NONE");
303                        break;
304                    case FAILURE_ASSOCIATION_REJECTION:
305                        sb.append("ASSOCIATION_REJECTION");
306                        break;
307                    case FAILURE_AUTHENTICATION_FAILURE:
308                        sb.append("AUTHENTICATION_FAILURE");
309                        break;
310                    case FAILURE_SSID_TEMP_DISABLED:
311                        sb.append("SSID_TEMP_DISABLED");
312                        break;
313                    case FAILURE_CONNECT_NETWORK_FAILED:
314                        sb.append("CONNECT_NETWORK_FAILED");
315                        break;
316                    case FAILURE_NETWORK_DISCONNECTION:
317                        sb.append("NETWORK_DISCONNECTION");
318                        break;
319                    case FAILURE_NEW_CONNECTION_ATTEMPT:
320                        sb.append("NEW_CONNECTION_ATTEMPT");
321                        break;
322                    case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
323                        sb.append("REDUNDANT_CONNECTION_ATTEMPT");
324                        break;
325                    case FAILURE_ROAM_TIMEOUT:
326                        sb.append("ROAM_TIMEOUT");
327                        break;
328                    case FAILURE_DHCP:
329                        sb.append("DHCP");
330                    default:
331                        sb.append("UNKNOWN");
332                        break;
333                }
334                sb.append(", connectivityLevelFailureCode=");
335                switch(mConnectionEvent.connectivityLevelFailureCode) {
336                    case WifiMetricsProto.ConnectionEvent.HLF_NONE:
337                        sb.append("NONE");
338                        break;
339                    case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
340                        sb.append("DHCP");
341                        break;
342                    case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
343                        sb.append("NO_INTERNET");
344                        break;
345                    case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
346                        sb.append("UNWANTED");
347                        break;
348                    default:
349                        sb.append("UNKNOWN");
350                        break;
351                }
352                sb.append(", signalStrength=");
353                sb.append(mConnectionEvent.signalStrength);
354                sb.append(", wifiState=");
355                switch(mWifiState) {
356                    case WifiMetricsProto.WifiLog.WIFI_DISABLED:
357                        sb.append("WIFI_DISABLED");
358                        break;
359                    case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
360                        sb.append("WIFI_DISCONNECTED");
361                        break;
362                    case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
363                        sb.append("WIFI_ASSOCIATED");
364                        break;
365                    default:
366                        sb.append("WIFI_UNKNOWN");
367                        break;
368                }
369                sb.append(", screenOn=");
370                sb.append(mScreenOn);
371                sb.append(". mRouterFingerprint: ");
372                sb.append(mRouterFingerPrint.toString());
373            }
374            return sb.toString();
375        }
376    }
377
378    public WifiMetrics(Clock clock, Looper looper, WifiAwareMetrics awareMetrics) {
379        mClock = clock;
380        mCurrentConnectionEvent = null;
381        mScreenOn = true;
382        mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
383        mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
384        mWifiAwareMetrics = awareMetrics;
385
386        mHandler = new Handler(looper) {
387            public void handleMessage(Message msg) {
388                synchronized (mLock) {
389                    processMessage(msg);
390                }
391            }
392        };
393    }
394
395    /** Sets internal WifiConfigManager member */
396    public void setWifiConfigManager(WifiConfigManager wifiConfigManager) {
397        mWifiConfigManager = wifiConfigManager;
398    }
399
400    /** Sets internal WifiNetworkSelector member */
401    public void setWifiNetworkSelector(WifiNetworkSelector wifiNetworkSelector) {
402        mWifiNetworkSelector = wifiNetworkSelector;
403    }
404
405    /** Sets internal PasspointManager member */
406    public void setPasspointManager(PasspointManager passpointManager) {
407        mPasspointManager = passpointManager;
408    }
409
410    // Values used for indexing SystemStateEntries
411    private static final int SCREEN_ON = 1;
412    private static final int SCREEN_OFF = 0;
413
414    /**
415     * Create a new connection event. Call when wifi attempts to make a new network connection
416     * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
417     * failure code.
418     * Gathers and sets the RouterFingerPrint data as well
419     *
420     * @param config WifiConfiguration of the config used for the current connection attempt
421     * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
422     */
423    public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
424        synchronized (mLock) {
425            // Check if this is overlapping another current connection event
426            if (mCurrentConnectionEvent != null) {
427                //Is this new Connection Event the same as the current one
428                if (mCurrentConnectionEvent.mConfigSsid != null
429                        && mCurrentConnectionEvent.mConfigBssid != null
430                        && config != null
431                        && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
432                        && (mCurrentConnectionEvent.mConfigBssid.equals("any")
433                        || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
434                    mCurrentConnectionEvent.mConfigBssid = targetBSSID;
435                    // End Connection Event due to new connection attempt to the same network
436                    endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
437                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
438                } else {
439                    // End Connection Event due to new connection attempt to different network
440                    endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
441                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
442                }
443            }
444            //If past maximum connection events, start removing the oldest
445            while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
446                mConnectionEventList.remove(0);
447            }
448            mCurrentConnectionEvent = new ConnectionEvent();
449            mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
450                    mClock.getWallClockMillis();
451            mCurrentConnectionEvent.mConfigBssid = targetBSSID;
452            mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
453            mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
454            mCurrentConnectionEvent.mConfigBssid = "any";
455            mCurrentConnectionEvent.mRealStartTime = mClock.getElapsedSinceBootMillis();
456            mCurrentConnectionEvent.mWifiState = mWifiState;
457            mCurrentConnectionEvent.mScreenOn = mScreenOn;
458            mConnectionEventList.add(mCurrentConnectionEvent);
459            mScanResultRssiTimestampMillis = -1;
460            if (config != null) {
461                ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
462                if (candidate != null) {
463                    // Cache the RSSI of the candidate, as the connection event level is updated
464                    // from other sources (polls, bssid_associations) and delta requires the
465                    // scanResult rssi
466                    mScanResultRssi = candidate.level;
467                    mScanResultRssiTimestampMillis = mClock.getElapsedSinceBootMillis();
468                }
469            }
470        }
471    }
472
473    /**
474     * set the RoamType of the current ConnectionEvent (if any)
475     */
476    public void setConnectionEventRoamType(int roamType) {
477        synchronized (mLock) {
478            if (mCurrentConnectionEvent != null) {
479                mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
480            }
481        }
482    }
483
484    /**
485     * Set AP related metrics from ScanDetail
486     */
487    public void setConnectionScanDetail(ScanDetail scanDetail) {
488        synchronized (mLock) {
489            if (mCurrentConnectionEvent != null && scanDetail != null) {
490                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
491                ScanResult scanResult = scanDetail.getScanResult();
492                //Ensure that we have a networkDetail, and that it corresponds to the currently
493                //tracked connection attempt
494                if (networkDetail != null && scanResult != null
495                        && mCurrentConnectionEvent.mConfigSsid != null
496                        && mCurrentConnectionEvent.mConfigSsid
497                        .equals("\"" + networkDetail.getSSID() + "\"")) {
498                    updateMetricsFromNetworkDetail(networkDetail);
499                    updateMetricsFromScanResult(scanResult);
500                }
501            }
502        }
503    }
504
505    /**
506     * End a Connection event record. Call when wifi connection attempt succeeds or fails.
507     * If a Connection event has not been started and is active when .end is called, a new one is
508     * created with zero duration.
509     *
510     * @param level2FailureCode Level 2 failure code returned by supplicant
511     * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
512     */
513    public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
514        synchronized (mLock) {
515            if (mCurrentConnectionEvent != null) {
516                boolean result = (level2FailureCode == 1)
517                        && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
518                mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
519                mCurrentConnectionEvent.mRealEndTime = mClock.getElapsedSinceBootMillis();
520                mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
521                        (mCurrentConnectionEvent.mRealEndTime
522                        - mCurrentConnectionEvent.mRealStartTime);
523                mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
524                mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
525                        connectivityFailureCode;
526                // ConnectionEvent already added to ConnectionEvents List. Safe to null current here
527                mCurrentConnectionEvent = null;
528                if (!result) {
529                    mScanResultRssiTimestampMillis = -1;
530                }
531            }
532        }
533    }
534
535    /**
536     * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
537     */
538    private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
539        int dtimInterval = networkDetail.getDtimInterval();
540        if (dtimInterval > 0) {
541            mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
542                    dtimInterval;
543        }
544        int connectionWifiMode;
545        switch (networkDetail.getWifiMode()) {
546            case InformationElementUtil.WifiMode.MODE_UNDEFINED:
547                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
548                break;
549            case InformationElementUtil.WifiMode.MODE_11A:
550                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
551                break;
552            case InformationElementUtil.WifiMode.MODE_11B:
553                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
554                break;
555            case InformationElementUtil.WifiMode.MODE_11G:
556                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
557                break;
558            case InformationElementUtil.WifiMode.MODE_11N:
559                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
560                break;
561            case InformationElementUtil.WifiMode.MODE_11AC  :
562                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
563                break;
564            default:
565                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
566                break;
567        }
568        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
569                .routerTechnology = connectionWifiMode;
570    }
571
572    /**
573     * Set ConnectionEvent RSSI and authentication type from ScanResult
574     */
575    private void updateMetricsFromScanResult(ScanResult scanResult) {
576        mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
577        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
578                WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
579        mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
580        if (scanResult.capabilities != null) {
581            if (ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
582                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
583                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
584            } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)) {
585                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
586                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
587            } else if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
588                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
589                        WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
590            }
591        }
592        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
593                scanResult.frequency;
594    }
595
596    void setIsLocationEnabled(boolean enabled) {
597        synchronized (mLock) {
598            mWifiLogProto.isLocationEnabled = enabled;
599        }
600    }
601
602    void setIsScanningAlwaysEnabled(boolean enabled) {
603        synchronized (mLock) {
604            mWifiLogProto.isScanningAlwaysEnabled = enabled;
605        }
606    }
607
608    /**
609     * Increment Non Empty Scan Results count
610     */
611    public void incrementNonEmptyScanResultCount() {
612        if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
613        synchronized (mLock) {
614            mWifiLogProto.numNonEmptyScanResults++;
615        }
616    }
617
618    /**
619     * Increment Empty Scan Results count
620     */
621    public void incrementEmptyScanResultCount() {
622        if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
623        synchronized (mLock) {
624            mWifiLogProto.numEmptyScanResults++;
625        }
626    }
627
628    /**
629     * Increment background scan count
630     */
631    public void incrementBackgroundScanCount() {
632        if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
633        synchronized (mLock) {
634            mWifiLogProto.numBackgroundScans++;
635        }
636    }
637
638   /**
639     * Get Background scan count
640     */
641    public int getBackgroundScanCount() {
642        synchronized (mLock) {
643            return mWifiLogProto.numBackgroundScans;
644        }
645    }
646
647    /**
648     * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
649     */
650    public void incrementOneshotScanCount() {
651        synchronized (mLock) {
652            mWifiLogProto.numOneshotScans++;
653        }
654        incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
655    }
656
657    /**
658     * Get oneshot scan count
659     */
660    public int getOneshotScanCount() {
661        synchronized (mLock) {
662            return mWifiLogProto.numOneshotScans;
663        }
664    }
665
666    private String returnCodeToString(int scanReturnCode) {
667        switch(scanReturnCode){
668            case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
669                return "SCAN_UNKNOWN";
670            case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
671                return "SCAN_SUCCESS";
672            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
673                return "SCAN_FAILURE_INTERRUPTED";
674            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
675                return "SCAN_FAILURE_INVALID_CONFIGURATION";
676            case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
677                return "FAILURE_WIFI_DISABLED";
678            default:
679                return "<UNKNOWN>";
680        }
681    }
682
683    /**
684     * Increment count of scan return code occurrence
685     *
686     * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
687     */
688    public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
689        synchronized (mLock) {
690            if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
691            int entry = mScanReturnEntries.get(scanReturnCode);
692            entry += countToAdd;
693            mScanReturnEntries.put(scanReturnCode, entry);
694        }
695    }
696    /**
697     * Get the count of this scanReturnCode
698     * @param scanReturnCode that we are getting the count for
699     */
700    public int getScanReturnEntry(int scanReturnCode) {
701        synchronized (mLock) {
702            return mScanReturnEntries.get(scanReturnCode);
703        }
704    }
705
706    private String wifiSystemStateToString(int state) {
707        switch(state){
708            case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
709                return "WIFI_UNKNOWN";
710            case WifiMetricsProto.WifiLog.WIFI_DISABLED:
711                return "WIFI_DISABLED";
712            case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
713                return "WIFI_DISCONNECTED";
714            case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
715                return "WIFI_ASSOCIATED";
716            default:
717                return "default";
718        }
719    }
720
721    /**
722     * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
723     *
724     * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
725     * @param screenOn Is the screen on
726     */
727    public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
728        synchronized (mLock) {
729            if (DBG) {
730                Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
731                        + " " + screenOn);
732            }
733            int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
734            int entry = mWifiSystemStateEntries.get(index);
735            entry++;
736            mWifiSystemStateEntries.put(index, entry);
737        }
738    }
739
740    /**
741     * Get the count of this system State Entry
742     */
743    public int getSystemStateCount(int state, boolean screenOn) {
744        synchronized (mLock) {
745            int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
746            return mWifiSystemStateEntries.get(index);
747        }
748    }
749
750    /**
751     * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
752     */
753    public void incrementNumLastResortWatchdogTriggers() {
754        synchronized (mLock) {
755            mWifiLogProto.numLastResortWatchdogTriggers++;
756        }
757    }
758    /**
759     * @param count number of networks over bad association threshold when watchdog triggered
760     */
761    public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
762        synchronized (mLock) {
763            mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
764        }
765    }
766    /**
767     * @param count number of networks over bad authentication threshold when watchdog triggered
768     */
769    public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
770        synchronized (mLock) {
771            mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
772        }
773    }
774    /**
775     * @param count number of networks over bad dhcp threshold when watchdog triggered
776     */
777    public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
778        synchronized (mLock) {
779            mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
780        }
781    }
782    /**
783     * @param count number of networks over bad other threshold when watchdog triggered
784     */
785    public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
786        synchronized (mLock) {
787            mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
788        }
789    }
790    /**
791     * @param count number of networks seen when watchdog triggered
792     */
793    public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
794        synchronized (mLock) {
795            mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
796        }
797    }
798    /**
799     * Increment count of triggers with atleast one bad association network
800     */
801    public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
802        synchronized (mLock) {
803            mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
804        }
805    }
806    /**
807     * Increment count of triggers with atleast one bad authentication network
808     */
809    public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
810        synchronized (mLock) {
811            mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
812        }
813    }
814    /**
815     * Increment count of triggers with atleast one bad dhcp network
816     */
817    public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
818        synchronized (mLock) {
819            mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
820        }
821    }
822    /**
823     * Increment count of triggers with atleast one bad other network
824     */
825    public void incrementNumLastResortWatchdogTriggersWithBadOther() {
826        synchronized (mLock) {
827            mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
828        }
829    }
830
831    /**
832     * Increment number of times connectivity watchdog confirmed pno is working
833     */
834    public void incrementNumConnectivityWatchdogPnoGood() {
835        synchronized (mLock) {
836            mWifiLogProto.numConnectivityWatchdogPnoGood++;
837        }
838    }
839    /**
840     * Increment number of times connectivity watchdog found pno not working
841     */
842    public void incrementNumConnectivityWatchdogPnoBad() {
843        synchronized (mLock) {
844            mWifiLogProto.numConnectivityWatchdogPnoBad++;
845        }
846    }
847    /**
848     * Increment number of times connectivity watchdog confirmed background scan is working
849     */
850    public void incrementNumConnectivityWatchdogBackgroundGood() {
851        synchronized (mLock) {
852            mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
853        }
854    }
855    /**
856     * Increment number of times connectivity watchdog found background scan not working
857     */
858    public void incrementNumConnectivityWatchdogBackgroundBad() {
859        synchronized (mLock) {
860            mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
861        }
862    }
863
864    /**
865     * Increment various poll related metrics, and cache performance data for StaEvent logging
866     */
867    public void handlePollResult(WifiInfo wifiInfo) {
868        mLastPollRssi = wifiInfo.getRssi();
869        mLastPollLinkSpeed = wifiInfo.getLinkSpeed();
870        mLastPollFreq = wifiInfo.getFrequency();
871        incrementRssiPollRssiCount(mLastPollRssi);
872    }
873
874    /**
875     * Increment occurence count of RSSI level from RSSI poll.
876     * Ignores rssi values outside the bounds of [MIN_RSSI_POLL, MAX_RSSI_POLL]
877     */
878    public void incrementRssiPollRssiCount(int rssi) {
879        if (!(rssi >= MIN_RSSI_POLL && rssi <= MAX_RSSI_POLL)) {
880            return;
881        }
882        synchronized (mLock) {
883            int count = mRssiPollCounts.get(rssi);
884            mRssiPollCounts.put(rssi, count + 1);
885            maybeIncrementRssiDeltaCount(rssi - mScanResultRssi);
886        }
887    }
888
889    /**
890     * Increment occurence count of difference between scan result RSSI and the first RSSI poll.
891     * Ignores rssi values outside the bounds of [MIN_RSSI_DELTA, MAX_RSSI_DELTA]
892     * mLock must be held when calling this method.
893     */
894    private void maybeIncrementRssiDeltaCount(int rssi) {
895        // Check if this RSSI poll is close enough to a scan result RSSI to log a delta value
896        if (mScanResultRssiTimestampMillis >= 0) {
897            long timeDelta = mClock.getElapsedSinceBootMillis() - mScanResultRssiTimestampMillis;
898            if (timeDelta <= TIMEOUT_RSSI_DELTA_MILLIS) {
899                if (rssi >= MIN_RSSI_DELTA && rssi <= MAX_RSSI_DELTA) {
900                    int count = mRssiDeltaCounts.get(rssi);
901                    mRssiDeltaCounts.put(rssi, count + 1);
902                }
903            }
904            mScanResultRssiTimestampMillis = -1;
905        }
906    }
907
908    /**
909     * Increment count of Watchdog successes.
910     */
911    public void incrementNumLastResortWatchdogSuccesses() {
912        synchronized (mLock) {
913            mWifiLogProto.numLastResortWatchdogSuccesses++;
914        }
915    }
916
917    /**
918     * Increments the count of alerts by alert reason.
919     *
920     * @param reason The cause of the alert. The reason values are driver-specific.
921     */
922    public void incrementAlertReasonCount(int reason) {
923        if (reason > WifiLoggerHal.WIFI_ALERT_REASON_MAX
924                || reason < WifiLoggerHal.WIFI_ALERT_REASON_MIN) {
925            reason = WifiLoggerHal.WIFI_ALERT_REASON_RESERVED;
926        }
927        synchronized (mLock) {
928            int alertCount = mWifiAlertReasonCounts.get(reason);
929            mWifiAlertReasonCounts.put(reason, alertCount + 1);
930        }
931    }
932
933    /**
934     * Counts all the different types of networks seen in a set of scan results
935     */
936    public void countScanResults(List<ScanDetail> scanDetails) {
937        if (scanDetails == null) {
938            return;
939        }
940        int totalResults = 0;
941        int openNetworks = 0;
942        int personalNetworks = 0;
943        int enterpriseNetworks = 0;
944        int hiddenNetworks = 0;
945        int hotspot2r1Networks = 0;
946        int hotspot2r2Networks = 0;
947        for (ScanDetail scanDetail : scanDetails) {
948            NetworkDetail networkDetail = scanDetail.getNetworkDetail();
949            ScanResult scanResult = scanDetail.getScanResult();
950            totalResults++;
951            if (networkDetail != null) {
952                if (networkDetail.isHiddenBeaconFrame()) {
953                    hiddenNetworks++;
954                }
955                if (networkDetail.getHSRelease() != null) {
956                    if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R1) {
957                        hotspot2r1Networks++;
958                    } else if (networkDetail.getHSRelease() == NetworkDetail.HSRelease.R2) {
959                        hotspot2r2Networks++;
960                    }
961                }
962            }
963            if (scanResult != null && scanResult.capabilities != null) {
964                if (ScanResultUtil.isScanResultForEapNetwork(scanResult)) {
965                    enterpriseNetworks++;
966                } else if (ScanResultUtil.isScanResultForPskNetwork(scanResult)
967                        || ScanResultUtil.isScanResultForWepNetwork(scanResult)) {
968                    personalNetworks++;
969                } else {
970                    openNetworks++;
971                }
972            }
973        }
974        synchronized (mLock) {
975            mWifiLogProto.numTotalScanResults += totalResults;
976            mWifiLogProto.numOpenNetworkScanResults += openNetworks;
977            mWifiLogProto.numPersonalNetworkScanResults += personalNetworks;
978            mWifiLogProto.numEnterpriseNetworkScanResults += enterpriseNetworks;
979            mWifiLogProto.numHiddenNetworkScanResults += hiddenNetworks;
980            mWifiLogProto.numHotspot2R1NetworkScanResults += hotspot2r1Networks;
981            mWifiLogProto.numHotspot2R2NetworkScanResults += hotspot2r2Networks;
982            mWifiLogProto.numScans++;
983        }
984    }
985
986    /**
987     * Increments occurence of a particular wifi score calculated
988     * in WifiScoreReport by current connected network. Scores are bounded
989     * within  [MIN_WIFI_SCORE, MAX_WIFI_SCORE] to limit size of SparseArray
990     */
991    public void incrementWifiScoreCount(int score) {
992        if (score < MIN_WIFI_SCORE || score > MAX_WIFI_SCORE) {
993            return;
994        }
995        synchronized (mLock) {
996            int count = mWifiScoreCounts.get(score);
997            mWifiScoreCounts.put(score, count + 1);
998        }
999    }
1000
1001    /**
1002     * Increments occurence of the results from attempting to start SoftAp.
1003     * Maps the |result| and WifiManager |failureCode| constant to proto defined SoftApStartResult
1004     * codes.
1005     */
1006    public void incrementSoftApStartResult(boolean result, int failureCode) {
1007        synchronized (mLock) {
1008            if (result) {
1009                int count = mSoftApManagerReturnCodeCounts.get(
1010                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY);
1011                mSoftApManagerReturnCodeCounts.put(
1012                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY,
1013                        count + 1);
1014                return;
1015            }
1016
1017            // now increment failure modes - if not explicitly handled, dump into the general
1018            // error bucket.
1019            if (failureCode == WifiManager.SAP_START_FAILURE_NO_CHANNEL) {
1020                int count = mSoftApManagerReturnCodeCounts.get(
1021                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL);
1022                mSoftApManagerReturnCodeCounts.put(
1023                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL,
1024                        count + 1);
1025            } else {
1026                // failure mode not tracked at this time...  count as a general error for now.
1027                int count = mSoftApManagerReturnCodeCounts.get(
1028                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR);
1029                mSoftApManagerReturnCodeCounts.put(
1030                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR,
1031                        count + 1);
1032            }
1033        }
1034    }
1035
1036    /**
1037     * Increment number of times the HAL crashed.
1038     */
1039    public void incrementNumHalCrashes() {
1040        synchronized (mLock) {
1041            mWifiLogProto.numHalCrashes++;
1042        }
1043    }
1044
1045    /**
1046     * Increment number of times the Wificond crashed.
1047     */
1048    public void incrementNumWificondCrashes() {
1049        synchronized (mLock) {
1050            mWifiLogProto.numWificondCrashes++;
1051        }
1052    }
1053
1054    /**
1055     * Increment number of times the wifi on failed due to an error in HAL.
1056     */
1057    public void incrementNumWifiOnFailureDueToHal() {
1058        synchronized (mLock) {
1059            mWifiLogProto.numWifiOnFailureDueToHal++;
1060        }
1061    }
1062
1063    /**
1064     * Increment number of times the wifi on failed due to an error in wificond.
1065     */
1066    public void incrementNumWifiOnFailureDueToWificond() {
1067        synchronized (mLock) {
1068            mWifiLogProto.numWifiOnFailureDueToWificond++;
1069        }
1070    }
1071
1072    /**
1073     * Increment number of times Passpoint provider being installed.
1074     */
1075    public void incrementNumPasspointProviderInstallation() {
1076        synchronized (mLock) {
1077            mWifiLogProto.numPasspointProviderInstallation++;
1078        }
1079    }
1080
1081    /**
1082     * Increment number of times Passpoint provider is installed successfully.
1083     */
1084    public void incrementNumPasspointProviderInstallSuccess() {
1085        synchronized (mLock) {
1086            mWifiLogProto.numPasspointProviderInstallSuccess++;
1087        }
1088    }
1089
1090    /**
1091     * Increment number of times Passpoint provider being uninstalled.
1092     */
1093    public void incrementNumPasspointProviderUninstallation() {
1094        synchronized (mLock) {
1095            mWifiLogProto.numPasspointProviderUninstallation++;
1096        }
1097    }
1098
1099    /**
1100     * Increment number of times Passpoint provider is uninstalled successfully.
1101     */
1102    public void incrementNumPasspointProviderUninstallSuccess() {
1103        synchronized (mLock) {
1104            mWifiLogProto.numPasspointProviderUninstallSuccess++;
1105        }
1106    }
1107
1108    /**
1109     * Increment N-Way network selection decision histograms:
1110     * Counts the size of various sets of scanDetails within a scan, and increment the occurrence
1111     * of that size for the associated histogram. There are ten histograms generated for each
1112     * combination of: {SSID, BSSID} *{Total, Saved, Open, Saved_or_Open, Passpoint}
1113     * Only performs this count if isFullBand is true, otherwise, increments the partial scan count
1114     */
1115    public void incrementAvailableNetworksHistograms(List<ScanDetail> scanDetails,
1116            boolean isFullBand) {
1117        synchronized (mLock) {
1118            if (mWifiConfigManager == null || mWifiNetworkSelector == null
1119                    || mPasspointManager == null) {
1120                return;
1121            }
1122            if (!isFullBand) {
1123                mWifiLogProto.partialAllSingleScanListenerResults++;
1124                return;
1125            }
1126            Set<ScanResultMatchInfo> ssids = new HashSet<ScanResultMatchInfo>();
1127            int bssids = 0;
1128            Set<ScanResultMatchInfo> openSsids = new HashSet<ScanResultMatchInfo>();
1129            int openBssids = 0;
1130            Set<ScanResultMatchInfo> savedSsids = new HashSet<ScanResultMatchInfo>();
1131            int savedBssids = 0;
1132            // openOrSavedSsids calculated from union of savedSsids & openSsids
1133            int openOrSavedBssids = 0;
1134            Set<PasspointProvider> savedPasspointProviderProfiles =
1135                    new HashSet<PasspointProvider>();
1136            int savedPasspointProviderBssids = 0;
1137            for (ScanDetail scanDetail : scanDetails) {
1138                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
1139                ScanResult scanResult = scanDetail.getScanResult();
1140                if (mWifiNetworkSelector.isSignalTooWeak(scanResult)) {
1141                    continue;
1142                }
1143                ScanResultMatchInfo matchInfo = ScanResultMatchInfo.fromScanResult(scanResult);
1144                Pair<PasspointProvider, PasspointMatch> providerMatch = null;
1145                PasspointProvider passpointProvider = null;
1146                if (networkDetail.isInterworking()) {
1147                    providerMatch =
1148                            mPasspointManager.matchProvider(scanResult);
1149                    passpointProvider = providerMatch != null ? providerMatch.first : null;
1150                }
1151                ssids.add(matchInfo);
1152                bssids++;
1153                boolean isOpen = matchInfo.networkType == ScanResultMatchInfo.NETWORK_TYPE_OPEN;
1154                WifiConfiguration config =
1155                        mWifiConfigManager.getConfiguredNetworkForScanDetail(scanDetail);
1156                boolean isSaved = (config != null) && !config.isEphemeral()
1157                        && !config.isPasspoint();
1158                boolean isSavedPasspoint = passpointProvider != null;
1159                if (isOpen) {
1160                    openSsids.add(matchInfo);
1161                    openBssids++;
1162                }
1163                if (isSaved) {
1164                    savedSsids.add(matchInfo);
1165                    savedBssids++;
1166                }
1167                if (isOpen || isSaved) {
1168                    openOrSavedBssids++;
1169                    // Calculate openOrSavedSsids union later
1170                }
1171                if (isSavedPasspoint) {
1172                    savedPasspointProviderProfiles.add(passpointProvider);
1173                    savedPasspointProviderBssids++;
1174                }
1175            }
1176            mWifiLogProto.fullBandAllSingleScanListenerResults++;
1177            incrementTotalScanSsids(mTotalSsidsInScanHistogram, ssids.size());
1178            incrementTotalScanResults(mTotalBssidsInScanHistogram, bssids);
1179            incrementSsid(mAvailableOpenSsidsInScanHistogram, openSsids.size());
1180            incrementBssid(mAvailableOpenBssidsInScanHistogram, openBssids);
1181            incrementSsid(mAvailableSavedSsidsInScanHistogram, savedSsids.size());
1182            incrementBssid(mAvailableSavedBssidsInScanHistogram, savedBssids);
1183            openSsids.addAll(savedSsids); // openSsids = Union(openSsids, savedSsids)
1184            incrementSsid(mAvailableOpenOrSavedSsidsInScanHistogram, openSsids.size());
1185            incrementBssid(mAvailableOpenOrSavedBssidsInScanHistogram, openOrSavedBssids);
1186            incrementSsid(mAvailableSavedPasspointProviderProfilesInScanHistogram,
1187                    savedPasspointProviderProfiles.size());
1188            incrementBssid(mAvailableSavedPasspointProviderBssidsInScanHistogram,
1189                    savedPasspointProviderBssids);
1190        }
1191    }
1192
1193    public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
1194    public static final String CLEAN_DUMP_ARG = "clean";
1195
1196    /**
1197     * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
1198     * at this time.
1199     *
1200     * @param fd unused
1201     * @param pw PrintWriter for writing dump to
1202     * @param args unused
1203     */
1204    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1205        synchronized (mLock) {
1206            if (args != null && args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
1207                // Dump serialized WifiLog proto
1208                consolidateProto(true);
1209                for (ConnectionEvent event : mConnectionEventList) {
1210                    if (mCurrentConnectionEvent != event) {
1211                        //indicate that automatic bug report has been taken for all valid
1212                        //connection events
1213                        event.mConnectionEvent.automaticBugReportTaken = true;
1214                    }
1215                }
1216                byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
1217                String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
1218                if (args.length > 1 && CLEAN_DUMP_ARG.equals(args[1])) {
1219                    // Output metrics proto bytes (base64) and nothing else
1220                    pw.print(metricsProtoDump);
1221                } else {
1222                    // Tag the start and end of the metrics proto bytes
1223                    pw.println("WifiMetrics:");
1224                    pw.println(metricsProtoDump);
1225                    pw.println("EndWifiMetrics");
1226                }
1227                clear();
1228            } else {
1229                pw.println("WifiMetrics:");
1230                pw.println("mConnectionEvents:");
1231                for (ConnectionEvent event : mConnectionEventList) {
1232                    String eventLine = event.toString();
1233                    if (event == mCurrentConnectionEvent) {
1234                        eventLine += "CURRENTLY OPEN EVENT";
1235                    }
1236                    pw.println(eventLine);
1237                }
1238                pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
1239                pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
1240                pw.println("mWifiLogProto.numPersonalNetworks="
1241                        + mWifiLogProto.numPersonalNetworks);
1242                pw.println("mWifiLogProto.numEnterpriseNetworks="
1243                        + mWifiLogProto.numEnterpriseNetworks);
1244                pw.println("mWifiLogProto.numHiddenNetworks=" + mWifiLogProto.numHiddenNetworks);
1245                pw.println("mWifiLogProto.numPasspointNetworks="
1246                        + mWifiLogProto.numPasspointNetworks);
1247                pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
1248                pw.println("mWifiLogProto.isScanningAlwaysEnabled="
1249                        + mWifiLogProto.isScanningAlwaysEnabled);
1250                pw.println("mWifiLogProto.numNetworksAddedByUser="
1251                        + mWifiLogProto.numNetworksAddedByUser);
1252                pw.println("mWifiLogProto.numNetworksAddedByApps="
1253                        + mWifiLogProto.numNetworksAddedByApps);
1254                pw.println("mWifiLogProto.numNonEmptyScanResults="
1255                        + mWifiLogProto.numNonEmptyScanResults);
1256                pw.println("mWifiLogProto.numEmptyScanResults="
1257                        + mWifiLogProto.numEmptyScanResults);
1258                pw.println("mWifiLogProto.numOneshotScans="
1259                        + mWifiLogProto.numOneshotScans);
1260                pw.println("mWifiLogProto.numBackgroundScans="
1261                        + mWifiLogProto.numBackgroundScans);
1262
1263                pw.println("mScanReturnEntries:");
1264                pw.println("  SCAN_UNKNOWN: " + getScanReturnEntry(
1265                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
1266                pw.println("  SCAN_SUCCESS: " + getScanReturnEntry(
1267                        WifiMetricsProto.WifiLog.SCAN_SUCCESS));
1268                pw.println("  SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
1269                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
1270                pw.println("  SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
1271                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
1272                pw.println("  FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
1273                        WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
1274
1275                pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
1276                pw.println("  WIFI_UNKNOWN       ON: "
1277                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
1278                pw.println("  WIFI_DISABLED      ON: "
1279                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
1280                pw.println("  WIFI_DISCONNECTED  ON: "
1281                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
1282                pw.println("  WIFI_ASSOCIATED    ON: "
1283                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
1284                pw.println("  WIFI_UNKNOWN      OFF: "
1285                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
1286                pw.println("  WIFI_DISABLED     OFF: "
1287                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
1288                pw.println("  WIFI_DISCONNECTED OFF: "
1289                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
1290                pw.println("  WIFI_ASSOCIATED   OFF: "
1291                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
1292                pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
1293                        + mWifiLogProto.numConnectivityWatchdogPnoGood);
1294                pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
1295                        + mWifiLogProto.numConnectivityWatchdogPnoBad);
1296                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
1297                        + mWifiLogProto.numConnectivityWatchdogBackgroundGood);
1298                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
1299                        + mWifiLogProto.numConnectivityWatchdogBackgroundBad);
1300                pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
1301                        + mWifiLogProto.numLastResortWatchdogTriggers);
1302                pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
1303                        + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
1304                pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
1305                        + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
1306                pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
1307                        + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
1308                pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
1309                        + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
1310                pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
1311                        + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
1312                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
1313                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
1314                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
1315                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
1316                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
1317                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
1318                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
1319                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
1320                pw.println("mWifiLogProto.numLastResortWatchdogSuccesses="
1321                        + mWifiLogProto.numLastResortWatchdogSuccesses);
1322                pw.println("mWifiLogProto.recordDurationSec="
1323                        + ((mClock.getElapsedSinceBootMillis() / 1000) - mRecordStartTimeSec));
1324                pw.println("mWifiLogProto.rssiPollRssiCount: Printing counts for [" + MIN_RSSI_POLL
1325                        + ", " + MAX_RSSI_POLL + "]");
1326                StringBuilder sb = new StringBuilder();
1327                for (int i = MIN_RSSI_POLL; i <= MAX_RSSI_POLL; i++) {
1328                    sb.append(mRssiPollCounts.get(i) + " ");
1329                }
1330                pw.println("  " + sb.toString());
1331                pw.println("mWifiLogProto.rssiPollDeltaCount: Printing counts for ["
1332                        + MIN_RSSI_DELTA + ", " + MAX_RSSI_DELTA + "]");
1333                sb.setLength(0);
1334                for (int i = MIN_RSSI_DELTA; i <= MAX_RSSI_DELTA; i++) {
1335                    sb.append(mRssiDeltaCounts.get(i) + " ");
1336                }
1337                pw.println("  " + sb.toString());
1338                pw.print("mWifiLogProto.alertReasonCounts=");
1339                sb.setLength(0);
1340                for (int i = WifiLoggerHal.WIFI_ALERT_REASON_MIN;
1341                        i <= WifiLoggerHal.WIFI_ALERT_REASON_MAX; i++) {
1342                    int count = mWifiAlertReasonCounts.get(i);
1343                    if (count > 0) {
1344                        sb.append("(" + i + "," + count + "),");
1345                    }
1346                }
1347                if (sb.length() > 1) {
1348                    sb.setLength(sb.length() - 1);  // strip trailing comma
1349                    pw.println(sb.toString());
1350                } else {
1351                    pw.println("()");
1352                }
1353                pw.println("mWifiLogProto.numTotalScanResults="
1354                        + mWifiLogProto.numTotalScanResults);
1355                pw.println("mWifiLogProto.numOpenNetworkScanResults="
1356                        + mWifiLogProto.numOpenNetworkScanResults);
1357                pw.println("mWifiLogProto.numPersonalNetworkScanResults="
1358                        + mWifiLogProto.numPersonalNetworkScanResults);
1359                pw.println("mWifiLogProto.numEnterpriseNetworkScanResults="
1360                        + mWifiLogProto.numEnterpriseNetworkScanResults);
1361                pw.println("mWifiLogProto.numHiddenNetworkScanResults="
1362                        + mWifiLogProto.numHiddenNetworkScanResults);
1363                pw.println("mWifiLogProto.numHotspot2R1NetworkScanResults="
1364                        + mWifiLogProto.numHotspot2R1NetworkScanResults);
1365                pw.println("mWifiLogProto.numHotspot2R2NetworkScanResults="
1366                        + mWifiLogProto.numHotspot2R2NetworkScanResults);
1367                pw.println("mWifiLogProto.numScans=" + mWifiLogProto.numScans);
1368                pw.println("mWifiLogProto.WifiScoreCount: [" + MIN_WIFI_SCORE + ", "
1369                        + MAX_WIFI_SCORE + "]");
1370                for (int i = 0; i <= MAX_WIFI_SCORE; i++) {
1371                    pw.print(mWifiScoreCounts.get(i) + " ");
1372                }
1373                pw.println(); // add a line after wifi scores
1374                pw.println("mWifiLogProto.SoftApManagerReturnCodeCounts:");
1375                pw.println("  SUCCESS: " + mSoftApManagerReturnCodeCounts.get(
1376                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_STARTED_SUCCESSFULLY));
1377                pw.println("  FAILED_GENERAL_ERROR: " + mSoftApManagerReturnCodeCounts.get(
1378                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_GENERAL_ERROR));
1379                pw.println("  FAILED_NO_CHANNEL: " + mSoftApManagerReturnCodeCounts.get(
1380                        WifiMetricsProto.SoftApReturnCodeCount.SOFT_AP_FAILED_NO_CHANNEL));
1381                pw.print("\n");
1382                pw.println("mWifiLogProto.numHalCrashes="
1383                        + mWifiLogProto.numHalCrashes);
1384                pw.println("mWifiLogProto.numWificondCrashes="
1385                        + mWifiLogProto.numWificondCrashes);
1386                pw.println("mWifiLogProto.numWifiOnFailureDueToHal="
1387                        + mWifiLogProto.numWifiOnFailureDueToHal);
1388                pw.println("mWifiLogProto.numWifiOnFailureDueToWificond="
1389                        + mWifiLogProto.numWifiOnFailureDueToWificond);
1390                pw.println("StaEventList:");
1391                for (StaEventWithTime event : mStaEventList) {
1392                    pw.println(event);
1393                }
1394
1395                pw.println("mWifiLogProto.numPasspointProviders="
1396                        + mWifiLogProto.numPasspointProviders);
1397                pw.println("mWifiLogProto.numPasspointProviderInstallation="
1398                        + mWifiLogProto.numPasspointProviderInstallation);
1399                pw.println("mWifiLogProto.numPasspointProviderInstallSuccess="
1400                        + mWifiLogProto.numPasspointProviderInstallSuccess);
1401                pw.println("mWifiLogProto.numPasspointProviderUninstallation="
1402                        + mWifiLogProto.numPasspointProviderUninstallation);
1403                pw.println("mWifiLogProto.numPasspointProviderUninstallSuccess="
1404                        + mWifiLogProto.numPasspointProviderUninstallSuccess);
1405                pw.println("mWifiLogProto.numPasspointProvidersSuccessfullyConnected="
1406                        + mWifiLogProto.numPasspointProvidersSuccessfullyConnected);
1407                pw.println("mTotalSsidsInScanHistogram:"
1408                        + mTotalSsidsInScanHistogram.toString());
1409                pw.println("mTotalBssidsInScanHistogram:"
1410                        + mTotalBssidsInScanHistogram.toString());
1411                pw.println("mAvailableOpenSsidsInScanHistogram:"
1412                        + mAvailableOpenSsidsInScanHistogram.toString());
1413                pw.println("mAvailableOpenBssidsInScanHistogram:"
1414                        + mAvailableOpenBssidsInScanHistogram.toString());
1415                pw.println("mAvailableSavedSsidsInScanHistogram:"
1416                        + mAvailableSavedSsidsInScanHistogram.toString());
1417                pw.println("mAvailableSavedBssidsInScanHistogram:"
1418                        + mAvailableSavedBssidsInScanHistogram.toString());
1419                pw.println("mAvailableOpenOrSavedSsidsInScanHistogram:"
1420                        + mAvailableOpenOrSavedSsidsInScanHistogram.toString());
1421                pw.println("mAvailableOpenOrSavedBssidsInScanHistogram:"
1422                        + mAvailableOpenOrSavedBssidsInScanHistogram.toString());
1423                pw.println("mAvailableSavedPasspointProviderProfilesInScanHistogram:"
1424                        + mAvailableSavedPasspointProviderProfilesInScanHistogram.toString());
1425                pw.println("mAvailableSavedPasspointProviderBssidsInScanHistogram:"
1426                        + mAvailableSavedPasspointProviderBssidsInScanHistogram.toString());
1427                pw.println("mWifiLogProto.partialAllSingleScanListenerResults="
1428                        + mWifiLogProto.partialAllSingleScanListenerResults);
1429                pw.println("mWifiLogProto.fullBandAllSingleScanListenerResults="
1430                        + mWifiLogProto.fullBandAllSingleScanListenerResults);
1431                pw.println("mWifiAwareMetrics:");
1432                mWifiAwareMetrics.dump(fd, pw, args);
1433            }
1434        }
1435    }
1436
1437    /**
1438     * Update various counts of saved network types
1439     * @param networks List of WifiConfigurations representing all saved networks, must not be null
1440     */
1441    public void updateSavedNetworks(List<WifiConfiguration> networks) {
1442        synchronized (mLock) {
1443            mWifiLogProto.numSavedNetworks = networks.size();
1444            mWifiLogProto.numOpenNetworks = 0;
1445            mWifiLogProto.numPersonalNetworks = 0;
1446            mWifiLogProto.numEnterpriseNetworks = 0;
1447            mWifiLogProto.numNetworksAddedByUser = 0;
1448            mWifiLogProto.numNetworksAddedByApps = 0;
1449            mWifiLogProto.numHiddenNetworks = 0;
1450            mWifiLogProto.numPasspointNetworks = 0;
1451            for (WifiConfiguration config : networks) {
1452                if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
1453                    mWifiLogProto.numOpenNetworks++;
1454                } else if (config.isEnterprise()) {
1455                    mWifiLogProto.numEnterpriseNetworks++;
1456                } else {
1457                    mWifiLogProto.numPersonalNetworks++;
1458                }
1459                if (config.selfAdded) {
1460                    mWifiLogProto.numNetworksAddedByUser++;
1461                } else {
1462                    mWifiLogProto.numNetworksAddedByApps++;
1463                }
1464                if (config.hiddenSSID) {
1465                    mWifiLogProto.numHiddenNetworks++;
1466                }
1467                if (config.isPasspoint()) {
1468                    mWifiLogProto.numPasspointNetworks++;
1469                }
1470            }
1471        }
1472    }
1473
1474    /**
1475     * Update metrics for saved Passpoint profiles.
1476     *
1477     * @param numSavedProfiles The number of saved Passpoint profiles
1478     * @param numConnectedProfiles The number of saved Passpoint profiles that have ever resulted
1479     *                             in a successful network connection
1480     */
1481    public void updateSavedPasspointProfiles(int numSavedProfiles, int numConnectedProfiles) {
1482        synchronized (mLock) {
1483            mWifiLogProto.numPasspointProviders = numSavedProfiles;
1484            mWifiLogProto.numPasspointProvidersSuccessfullyConnected = numConnectedProfiles;
1485        }
1486    }
1487
1488    /**
1489     * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
1490     * respective lists within mWifiLogProto
1491     *
1492     * @param incremental Only include ConnectionEvents created since last automatic bug report
1493     */
1494    private void consolidateProto(boolean incremental) {
1495        List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
1496        List<WifiMetricsProto.RssiPollCount> rssis = new ArrayList<>();
1497        List<WifiMetricsProto.RssiPollCount> rssiDeltas = new ArrayList<>();
1498        List<WifiMetricsProto.AlertReasonCount> alertReasons = new ArrayList<>();
1499        List<WifiMetricsProto.WifiScoreCount> scores = new ArrayList<>();
1500        synchronized (mLock) {
1501            for (ConnectionEvent event : mConnectionEventList) {
1502                // If this is not incremental, dump full ConnectionEvent list
1503                // Else Dump all un-dumped events except for the current one
1504                if (!incremental || ((mCurrentConnectionEvent != event)
1505                        && !event.mConnectionEvent.automaticBugReportTaken)) {
1506                    //Get all ConnectionEvents that haven not been dumped as a proto, also exclude
1507                    //the current active un-ended connection event
1508                    events.add(event.mConnectionEvent);
1509                    if (incremental) {
1510                        event.mConnectionEvent.automaticBugReportTaken = true;
1511                    }
1512                }
1513            }
1514            if (events.size() > 0) {
1515                mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
1516            }
1517
1518            //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
1519            mWifiLogProto.scanReturnEntries =
1520                    new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
1521            for (int i = 0; i < mScanReturnEntries.size(); i++) {
1522                mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
1523                mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
1524                mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
1525            }
1526
1527            // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
1528            // This one is slightly more complex, as the Sparse are indexed with:
1529            //     key: wifiState * 2 + isScreenOn, value: wifiStateCount
1530            mWifiLogProto.wifiSystemStateEntries =
1531                    new WifiMetricsProto.WifiLog
1532                    .WifiSystemStateEntry[mWifiSystemStateEntries.size()];
1533            for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
1534                mWifiLogProto.wifiSystemStateEntries[i] =
1535                        new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
1536                mWifiLogProto.wifiSystemStateEntries[i].wifiState =
1537                        mWifiSystemStateEntries.keyAt(i) / 2;
1538                mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
1539                        mWifiSystemStateEntries.valueAt(i);
1540                mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
1541                        (mWifiSystemStateEntries.keyAt(i) % 2) > 0;
1542            }
1543            mWifiLogProto.recordDurationSec = (int) ((mClock.getElapsedSinceBootMillis() / 1000)
1544                    - mRecordStartTimeSec);
1545
1546            /**
1547             * Convert the SparseIntArray of RSSI poll rssi's and counts to the proto's repeated
1548             * IntKeyVal array.
1549             */
1550            for (int i = 0; i < mRssiPollCounts.size(); i++) {
1551                WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1552                keyVal.rssi = mRssiPollCounts.keyAt(i);
1553                keyVal.count = mRssiPollCounts.valueAt(i);
1554                rssis.add(keyVal);
1555            }
1556            mWifiLogProto.rssiPollRssiCount = rssis.toArray(mWifiLogProto.rssiPollRssiCount);
1557
1558            /**
1559             * Convert the SparseIntArray of RSSI delta rssi's and counts to the proto's repeated
1560             * IntKeyVal array.
1561             */
1562            for (int i = 0; i < mRssiDeltaCounts.size(); i++) {
1563                WifiMetricsProto.RssiPollCount keyVal = new WifiMetricsProto.RssiPollCount();
1564                keyVal.rssi = mRssiDeltaCounts.keyAt(i);
1565                keyVal.count = mRssiDeltaCounts.valueAt(i);
1566                rssiDeltas.add(keyVal);
1567            }
1568            mWifiLogProto.rssiPollDeltaCount = rssiDeltas.toArray(mWifiLogProto.rssiPollDeltaCount);
1569
1570            /**
1571             * Convert the SparseIntArray of alert reasons and counts to the proto's repeated
1572             * IntKeyVal array.
1573             */
1574            for (int i = 0; i < mWifiAlertReasonCounts.size(); i++) {
1575                WifiMetricsProto.AlertReasonCount keyVal = new WifiMetricsProto.AlertReasonCount();
1576                keyVal.reason = mWifiAlertReasonCounts.keyAt(i);
1577                keyVal.count = mWifiAlertReasonCounts.valueAt(i);
1578                alertReasons.add(keyVal);
1579            }
1580            mWifiLogProto.alertReasonCount = alertReasons.toArray(mWifiLogProto.alertReasonCount);
1581
1582            /**
1583            *  Convert the SparseIntArray of Wifi Score and counts to proto's repeated
1584            * IntKeyVal array.
1585            */
1586            for (int score = 0; score < mWifiScoreCounts.size(); score++) {
1587                WifiMetricsProto.WifiScoreCount keyVal = new WifiMetricsProto.WifiScoreCount();
1588                keyVal.score = mWifiScoreCounts.keyAt(score);
1589                keyVal.count = mWifiScoreCounts.valueAt(score);
1590                scores.add(keyVal);
1591            }
1592            mWifiLogProto.wifiScoreCount = scores.toArray(mWifiLogProto.wifiScoreCount);
1593
1594            /**
1595             * Convert the SparseIntArray of SoftAp Return codes and counts to proto's repeated
1596             * IntKeyVal array.
1597             */
1598            int codeCounts = mSoftApManagerReturnCodeCounts.size();
1599            mWifiLogProto.softApReturnCode = new WifiMetricsProto.SoftApReturnCodeCount[codeCounts];
1600            for (int sapCode = 0; sapCode < codeCounts; sapCode++) {
1601                mWifiLogProto.softApReturnCode[sapCode] =
1602                        new WifiMetricsProto.SoftApReturnCodeCount();
1603                mWifiLogProto.softApReturnCode[sapCode].startResult =
1604                        mSoftApManagerReturnCodeCounts.keyAt(sapCode);
1605                mWifiLogProto.softApReturnCode[sapCode].count =
1606                        mSoftApManagerReturnCodeCounts.valueAt(sapCode);
1607            }
1608
1609            /**
1610             * Convert StaEventList to array of StaEvents
1611             */
1612            mWifiLogProto.staEventList = new StaEvent[mStaEventList.size()];
1613            for (int i = 0; i < mStaEventList.size(); i++) {
1614                mWifiLogProto.staEventList[i] = mStaEventList.get(i).staEvent;
1615            }
1616            mWifiLogProto.totalSsidsInScanHistogram =
1617                    makeNumConnectableNetworksBucketArray(mTotalSsidsInScanHistogram);
1618            mWifiLogProto.totalBssidsInScanHistogram =
1619                    makeNumConnectableNetworksBucketArray(mTotalBssidsInScanHistogram);
1620            mWifiLogProto.availableOpenSsidsInScanHistogram =
1621                    makeNumConnectableNetworksBucketArray(mAvailableOpenSsidsInScanHistogram);
1622            mWifiLogProto.availableOpenBssidsInScanHistogram =
1623                    makeNumConnectableNetworksBucketArray(mAvailableOpenBssidsInScanHistogram);
1624            mWifiLogProto.availableSavedSsidsInScanHistogram =
1625                    makeNumConnectableNetworksBucketArray(mAvailableSavedSsidsInScanHistogram);
1626            mWifiLogProto.availableSavedBssidsInScanHistogram =
1627                    makeNumConnectableNetworksBucketArray(mAvailableSavedBssidsInScanHistogram);
1628            mWifiLogProto.availableOpenOrSavedSsidsInScanHistogram =
1629                    makeNumConnectableNetworksBucketArray(
1630                    mAvailableOpenOrSavedSsidsInScanHistogram);
1631            mWifiLogProto.availableOpenOrSavedBssidsInScanHistogram =
1632                    makeNumConnectableNetworksBucketArray(
1633                    mAvailableOpenOrSavedBssidsInScanHistogram);
1634            mWifiLogProto.availableSavedPasspointProviderProfilesInScanHistogram =
1635                    makeNumConnectableNetworksBucketArray(
1636                    mAvailableSavedPasspointProviderProfilesInScanHistogram);
1637            mWifiLogProto.availableSavedPasspointProviderBssidsInScanHistogram =
1638                    makeNumConnectableNetworksBucketArray(
1639                    mAvailableSavedPasspointProviderBssidsInScanHistogram);
1640            mWifiLogProto.wifiAwareLog = mWifiAwareMetrics.consolidateProto();
1641        }
1642    }
1643
1644    private WifiMetricsProto.NumConnectableNetworksBucket[] makeNumConnectableNetworksBucketArray(
1645            SparseIntArray sia) {
1646        WifiMetricsProto.NumConnectableNetworksBucket[] array =
1647                new WifiMetricsProto.NumConnectableNetworksBucket[sia.size()];
1648        for (int i = 0; i < sia.size(); i++) {
1649            WifiMetricsProto.NumConnectableNetworksBucket keyVal =
1650                    new WifiMetricsProto.NumConnectableNetworksBucket();
1651            keyVal.numConnectableNetworks = sia.keyAt(i);
1652            keyVal.count = sia.valueAt(i);
1653            array[i] = keyVal;
1654        }
1655        return array;
1656    }
1657
1658    /**
1659     * Clear all WifiMetrics, except for currentConnectionEvent.
1660     */
1661    private void clear() {
1662        synchronized (mLock) {
1663            mConnectionEventList.clear();
1664            if (mCurrentConnectionEvent != null) {
1665                mConnectionEventList.add(mCurrentConnectionEvent);
1666            }
1667            mScanReturnEntries.clear();
1668            mWifiSystemStateEntries.clear();
1669            mRecordStartTimeSec = mClock.getElapsedSinceBootMillis() / 1000;
1670            mRssiPollCounts.clear();
1671            mRssiDeltaCounts.clear();
1672            mWifiAlertReasonCounts.clear();
1673            mWifiScoreCounts.clear();
1674            mWifiLogProto.clear();
1675            mScanResultRssiTimestampMillis = -1;
1676            mSoftApManagerReturnCodeCounts.clear();
1677            mStaEventList.clear();
1678            mWifiAwareMetrics.clear();
1679            mTotalSsidsInScanHistogram.clear();
1680            mTotalBssidsInScanHistogram.clear();
1681            mAvailableOpenSsidsInScanHistogram.clear();
1682            mAvailableOpenBssidsInScanHistogram.clear();
1683            mAvailableSavedSsidsInScanHistogram.clear();
1684            mAvailableSavedBssidsInScanHistogram.clear();
1685            mAvailableOpenOrSavedSsidsInScanHistogram.clear();
1686            mAvailableOpenOrSavedBssidsInScanHistogram.clear();
1687            mAvailableSavedPasspointProviderProfilesInScanHistogram.clear();
1688            mAvailableSavedPasspointProviderBssidsInScanHistogram.clear();
1689        }
1690    }
1691
1692    /**
1693     *  Set screen state (On/Off)
1694     */
1695    public void setScreenState(boolean screenOn) {
1696        synchronized (mLock) {
1697            mScreenOn = screenOn;
1698        }
1699    }
1700
1701    /**
1702     *  Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
1703     */
1704    public void setWifiState(int wifiState) {
1705        synchronized (mLock) {
1706            mWifiState = wifiState;
1707        }
1708    }
1709
1710    /**
1711     * Message handler for interesting WifiMonitor messages. Generates StaEvents
1712     */
1713    private void processMessage(Message msg) {
1714        StaEvent event = new StaEvent();
1715        boolean logEvent = true;
1716        switch (msg.what) {
1717            case WifiMonitor.ASSOCIATION_REJECTION_EVENT:
1718                event.type = StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT;
1719                event.associationTimedOut = msg.arg1 > 0 ? true : false;
1720                event.status = msg.arg2;
1721                break;
1722            case WifiMonitor.AUTHENTICATION_FAILURE_EVENT:
1723                event.type = StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT;
1724                switch (msg.arg2) {
1725                    case WifiManager.ERROR_AUTH_FAILURE_NONE:
1726                        event.authFailureReason = StaEvent.AUTH_FAILURE_NONE;
1727                        break;
1728                    case WifiManager.ERROR_AUTH_FAILURE_TIMEOUT:
1729                        event.authFailureReason = StaEvent.AUTH_FAILURE_TIMEOUT;
1730                        break;
1731                    case WifiManager.ERROR_AUTH_FAILURE_WRONG_PSWD:
1732                        event.authFailureReason = StaEvent.AUTH_FAILURE_WRONG_PSWD;
1733                        break;
1734                    case WifiManager.ERROR_AUTH_FAILURE_EAP_FAILURE:
1735                        event.authFailureReason = StaEvent.AUTH_FAILURE_EAP_FAILURE;
1736                        break;
1737                    default:
1738                        break;
1739                }
1740                break;
1741            case WifiMonitor.NETWORK_CONNECTION_EVENT:
1742                event.type = StaEvent.TYPE_NETWORK_CONNECTION_EVENT;
1743                break;
1744            case WifiMonitor.NETWORK_DISCONNECTION_EVENT:
1745                event.type = StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT;
1746                event.reason = msg.arg2;
1747                event.localGen = msg.arg1 == 0 ? false : true;
1748                break;
1749            case WifiMonitor.SUPPLICANT_STATE_CHANGE_EVENT:
1750                logEvent = false;
1751                StateChangeResult stateChangeResult = (StateChangeResult) msg.obj;
1752                mSupplicantStateChangeBitmask |= supplicantStateToBit(stateChangeResult.state);
1753                break;
1754            case WifiStateMachine.CMD_ASSOCIATED_BSSID:
1755                event.type = StaEvent.TYPE_CMD_ASSOCIATED_BSSID;
1756                break;
1757            case WifiStateMachine.CMD_TARGET_BSSID:
1758                event.type = StaEvent.TYPE_CMD_TARGET_BSSID;
1759                break;
1760            default:
1761                return;
1762        }
1763        if (logEvent) {
1764            addStaEvent(event);
1765        }
1766    }
1767    /**
1768     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1769     * generated event types, which are logged through 'sendMessage'
1770     * @param type StaEvent.EventType describing the event
1771     */
1772    public void logStaEvent(int type) {
1773        logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, null);
1774    }
1775    /**
1776     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1777     * generated event types, which are logged through 'sendMessage'
1778     * @param type StaEvent.EventType describing the event
1779     * @param config WifiConfiguration for a framework initiated connection attempt
1780     */
1781    public void logStaEvent(int type, WifiConfiguration config) {
1782        logStaEvent(type, StaEvent.DISCONNECT_UNKNOWN, config);
1783    }
1784    /**
1785     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1786     * generated event types, which are logged through 'sendMessage'
1787     * @param type StaEvent.EventType describing the event
1788     * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
1789     *                                  initiated a FRAMEWORK_DISCONNECT
1790     */
1791    public void logStaEvent(int type, int frameworkDisconnectReason) {
1792        logStaEvent(type, frameworkDisconnectReason, null);
1793    }
1794    /**
1795     * Log a StaEvent from WifiStateMachine. The StaEvent must not be one of the supplicant
1796     * generated event types, which are logged through 'sendMessage'
1797     * @param type StaEvent.EventType describing the event
1798     * @param frameworkDisconnectReason StaEvent.FrameworkDisconnectReason explaining why framework
1799     *                                  initiated a FRAMEWORK_DISCONNECT
1800     * @param config WifiConfiguration for a framework initiated connection attempt
1801     */
1802    public void logStaEvent(int type, int frameworkDisconnectReason, WifiConfiguration config) {
1803        switch (type) {
1804            case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
1805            case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
1806            case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
1807            case StaEvent.TYPE_CMD_START_CONNECT:
1808            case StaEvent.TYPE_CMD_START_ROAM:
1809            case StaEvent.TYPE_CONNECT_NETWORK:
1810            case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
1811            case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
1812                break;
1813            default:
1814                Log.e(TAG, "Unknown StaEvent:" + type);
1815                return;
1816        }
1817        StaEvent event = new StaEvent();
1818        event.type = type;
1819        if (frameworkDisconnectReason != StaEvent.DISCONNECT_UNKNOWN) {
1820            event.frameworkDisconnectReason = frameworkDisconnectReason;
1821        }
1822        event.configInfo = createConfigInfo(config);
1823        addStaEvent(event);
1824    }
1825
1826    private void addStaEvent(StaEvent staEvent) {
1827        staEvent.startTimeMillis = mClock.getElapsedSinceBootMillis();
1828        staEvent.lastRssi = mLastPollRssi;
1829        staEvent.lastFreq = mLastPollFreq;
1830        staEvent.lastLinkSpeed = mLastPollLinkSpeed;
1831        staEvent.supplicantStateChangesBitmask = mSupplicantStateChangeBitmask;
1832        mSupplicantStateChangeBitmask = 0;
1833        mLastPollRssi = -127;
1834        mLastPollFreq = -1;
1835        mLastPollLinkSpeed = -1;
1836        mStaEventList.add(new StaEventWithTime(staEvent, mClock.getWallClockMillis()));
1837        // Prune StaEventList if it gets too long
1838        if (mStaEventList.size() > MAX_STA_EVENTS) mStaEventList.remove();
1839    }
1840
1841    private ConfigInfo createConfigInfo(WifiConfiguration config) {
1842        if (config == null) return null;
1843        ConfigInfo info = new ConfigInfo();
1844        info.allowedKeyManagement = bitSetToInt(config.allowedKeyManagement);
1845        info.allowedProtocols = bitSetToInt(config.allowedProtocols);
1846        info.allowedAuthAlgorithms = bitSetToInt(config.allowedAuthAlgorithms);
1847        info.allowedPairwiseCiphers = bitSetToInt(config.allowedPairwiseCiphers);
1848        info.allowedGroupCiphers = bitSetToInt(config.allowedGroupCiphers);
1849        info.hiddenSsid = config.hiddenSSID;
1850        info.isPasspoint = config.isPasspoint();
1851        info.isEphemeral = config.isEphemeral();
1852        info.hasEverConnected = config.getNetworkSelectionStatus().getHasEverConnected();
1853        ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
1854        if (candidate != null) {
1855            info.scanRssi = candidate.level;
1856            info.scanFreq = candidate.frequency;
1857        }
1858        return info;
1859    }
1860
1861    public Handler getHandler() {
1862        return mHandler;
1863    }
1864
1865    public WifiAwareMetrics getWifiAwareMetrics() {
1866        return mWifiAwareMetrics;
1867    }
1868
1869    // Rather than generate a StaEvent for each SUPPLICANT_STATE_CHANGE, cache these in a bitmask
1870    // and attach it to the next event which is generated.
1871    private int mSupplicantStateChangeBitmask = 0;
1872
1873    /**
1874     * Converts a SupplicantState value to a single bit, with position defined by
1875     * {@code StaEvent.SupplicantState}
1876     */
1877    public static int supplicantStateToBit(SupplicantState state) {
1878        switch(state) {
1879            case DISCONNECTED:
1880                return 1 << StaEvent.STATE_DISCONNECTED;
1881            case INTERFACE_DISABLED:
1882                return 1 << StaEvent.STATE_INTERFACE_DISABLED;
1883            case INACTIVE:
1884                return 1 << StaEvent.STATE_INACTIVE;
1885            case SCANNING:
1886                return 1 << StaEvent.STATE_SCANNING;
1887            case AUTHENTICATING:
1888                return 1 << StaEvent.STATE_AUTHENTICATING;
1889            case ASSOCIATING:
1890                return 1 << StaEvent.STATE_ASSOCIATING;
1891            case ASSOCIATED:
1892                return 1 << StaEvent.STATE_ASSOCIATED;
1893            case FOUR_WAY_HANDSHAKE:
1894                return 1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE;
1895            case GROUP_HANDSHAKE:
1896                return 1 << StaEvent.STATE_GROUP_HANDSHAKE;
1897            case COMPLETED:
1898                return 1 << StaEvent.STATE_COMPLETED;
1899            case DORMANT:
1900                return 1 << StaEvent.STATE_DORMANT;
1901            case UNINITIALIZED:
1902                return 1 << StaEvent.STATE_UNINITIALIZED;
1903            case INVALID:
1904                return 1 << StaEvent.STATE_INVALID;
1905            default:
1906                Log.wtf(TAG, "Got unknown supplicant state: " + state.ordinal());
1907                return 0;
1908        }
1909    }
1910
1911    private static String supplicantStateChangesBitmaskToString(int mask) {
1912        StringBuilder sb = new StringBuilder();
1913        sb.append("supplicantStateChangeEvents: {");
1914        if ((mask & (1 << StaEvent.STATE_DISCONNECTED)) > 0) sb.append(" DISCONNECTED");
1915        if ((mask & (1 << StaEvent.STATE_INTERFACE_DISABLED)) > 0) sb.append(" INTERFACE_DISABLED");
1916        if ((mask & (1 << StaEvent.STATE_INACTIVE)) > 0) sb.append(" INACTIVE");
1917        if ((mask & (1 << StaEvent.STATE_SCANNING)) > 0) sb.append(" SCANNING");
1918        if ((mask & (1 << StaEvent.STATE_AUTHENTICATING)) > 0) sb.append(" AUTHENTICATING");
1919        if ((mask & (1 << StaEvent.STATE_ASSOCIATING)) > 0) sb.append(" ASSOCIATING");
1920        if ((mask & (1 << StaEvent.STATE_ASSOCIATED)) > 0) sb.append(" ASSOCIATED");
1921        if ((mask & (1 << StaEvent.STATE_FOUR_WAY_HANDSHAKE)) > 0) sb.append(" FOUR_WAY_HANDSHAKE");
1922        if ((mask & (1 << StaEvent.STATE_GROUP_HANDSHAKE)) > 0) sb.append(" GROUP_HANDSHAKE");
1923        if ((mask & (1 << StaEvent.STATE_COMPLETED)) > 0) sb.append(" COMPLETED");
1924        if ((mask & (1 << StaEvent.STATE_DORMANT)) > 0) sb.append(" DORMANT");
1925        if ((mask & (1 << StaEvent.STATE_UNINITIALIZED)) > 0) sb.append(" UNINITIALIZED");
1926        if ((mask & (1 << StaEvent.STATE_INVALID)) > 0) sb.append(" INVALID");
1927        sb.append("}");
1928        return sb.toString();
1929    }
1930
1931    /**
1932     * Returns a human readable string from a Sta Event. Only adds information relevant to the event
1933     * type.
1934     */
1935    public static String staEventToString(StaEvent event) {
1936        if (event == null) return "<NULL>";
1937        StringBuilder sb = new StringBuilder();
1938        switch (event.type) {
1939            case StaEvent.TYPE_ASSOCIATION_REJECTION_EVENT:
1940                sb.append("ASSOCIATION_REJECTION_EVENT")
1941                        .append(" timedOut=").append(event.associationTimedOut)
1942                        .append(" status=").append(event.status).append(":")
1943                        .append(ISupplicantStaIfaceCallback.StatusCode.toString(event.status));
1944                break;
1945            case StaEvent.TYPE_AUTHENTICATION_FAILURE_EVENT:
1946                sb.append("AUTHENTICATION_FAILURE_EVENT reason=").append(event.authFailureReason)
1947                        .append(":").append(authFailureReasonToString(event.authFailureReason));
1948                break;
1949            case StaEvent.TYPE_NETWORK_CONNECTION_EVENT:
1950                sb.append("NETWORK_CONNECTION_EVENT");
1951                break;
1952            case StaEvent.TYPE_NETWORK_DISCONNECTION_EVENT:
1953                sb.append("NETWORK_DISCONNECTION_EVENT")
1954                        .append(" local_gen=").append(event.localGen)
1955                        .append(" reason=").append(event.reason).append(":")
1956                        .append(ISupplicantStaIfaceCallback.ReasonCode.toString(
1957                                (event.reason >= 0 ? event.reason : -1 * event.reason)));
1958                break;
1959            case StaEvent.TYPE_CMD_ASSOCIATED_BSSID:
1960                sb.append("CMD_ASSOCIATED_BSSID");
1961                break;
1962            case StaEvent.TYPE_CMD_IP_CONFIGURATION_SUCCESSFUL:
1963                sb.append("CMD_IP_CONFIGURATION_SUCCESSFUL");
1964                break;
1965            case StaEvent.TYPE_CMD_IP_CONFIGURATION_LOST:
1966                sb.append("CMD_IP_CONFIGURATION_LOST");
1967                break;
1968            case StaEvent.TYPE_CMD_IP_REACHABILITY_LOST:
1969                sb.append("CMD_IP_REACHABILITY_LOST");
1970                break;
1971            case StaEvent.TYPE_CMD_TARGET_BSSID:
1972                sb.append("CMD_TARGET_BSSID");
1973                break;
1974            case StaEvent.TYPE_CMD_START_CONNECT:
1975                sb.append("CMD_START_CONNECT");
1976                break;
1977            case StaEvent.TYPE_CMD_START_ROAM:
1978                sb.append("CMD_START_ROAM");
1979                break;
1980            case StaEvent.TYPE_CONNECT_NETWORK:
1981                sb.append("CONNECT_NETWORK");
1982                break;
1983            case StaEvent.TYPE_NETWORK_AGENT_VALID_NETWORK:
1984                sb.append("NETWORK_AGENT_VALID_NETWORK");
1985                break;
1986            case StaEvent.TYPE_FRAMEWORK_DISCONNECT:
1987                sb.append("FRAMEWORK_DISCONNECT")
1988                        .append(" reason=")
1989                        .append(frameworkDisconnectReasonToString(event.frameworkDisconnectReason));
1990                break;
1991            default:
1992                sb.append("UNKNOWN " + event.type + ":");
1993                break;
1994        }
1995        if (event.lastRssi != -127) sb.append(" lastRssi=").append(event.lastRssi);
1996        if (event.lastFreq != -1) sb.append(" lastFreq=").append(event.lastFreq);
1997        if (event.lastLinkSpeed != -1) sb.append(" lastLinkSpeed=").append(event.lastLinkSpeed);
1998        if (event.supplicantStateChangesBitmask != 0) {
1999            sb.append(", ").append(supplicantStateChangesBitmaskToString(
2000                    event.supplicantStateChangesBitmask));
2001        }
2002        if (event.configInfo != null) {
2003            sb.append(", ").append(configInfoToString(event.configInfo));
2004        }
2005
2006        return sb.toString();
2007    }
2008
2009    private static String authFailureReasonToString(int authFailureReason) {
2010        switch (authFailureReason) {
2011            case StaEvent.AUTH_FAILURE_NONE:
2012                return "ERROR_AUTH_FAILURE_NONE";
2013            case StaEvent.AUTH_FAILURE_TIMEOUT:
2014                return "ERROR_AUTH_FAILURE_TIMEOUT";
2015            case StaEvent.AUTH_FAILURE_WRONG_PSWD:
2016                return "ERROR_AUTH_FAILURE_WRONG_PSWD";
2017            case StaEvent.AUTH_FAILURE_EAP_FAILURE:
2018                return "ERROR_AUTH_FAILURE_EAP_FAILURE";
2019            default:
2020                return "";
2021        }
2022    }
2023
2024    private static String frameworkDisconnectReasonToString(int frameworkDisconnectReason) {
2025        switch (frameworkDisconnectReason) {
2026            case StaEvent.DISCONNECT_API:
2027                return "DISCONNECT_API";
2028            case StaEvent.DISCONNECT_GENERIC:
2029                return "DISCONNECT_GENERIC";
2030            case StaEvent.DISCONNECT_UNWANTED:
2031                return "DISCONNECT_UNWANTED";
2032            case StaEvent.DISCONNECT_ROAM_WATCHDOG_TIMER:
2033                return "DISCONNECT_ROAM_WATCHDOG_TIMER";
2034            case StaEvent.DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST:
2035                return "DISCONNECT_P2P_DISCONNECT_WIFI_REQUEST";
2036            case StaEvent.DISCONNECT_RESET_SIM_NETWORKS:
2037                return "DISCONNECT_RESET_SIM_NETWORKS";
2038            default:
2039                return "DISCONNECT_UNKNOWN=" + frameworkDisconnectReason;
2040        }
2041    }
2042
2043    private static String configInfoToString(ConfigInfo info) {
2044        StringBuilder sb = new StringBuilder();
2045        sb.append("ConfigInfo:")
2046                .append(" allowed_key_management=").append(info.allowedKeyManagement)
2047                .append(" allowed_protocols=").append(info.allowedProtocols)
2048                .append(" allowed_auth_algorithms=").append(info.allowedAuthAlgorithms)
2049                .append(" allowed_pairwise_ciphers=").append(info.allowedPairwiseCiphers)
2050                .append(" allowed_group_ciphers=").append(info.allowedGroupCiphers)
2051                .append(" hidden_ssid=").append(info.hiddenSsid)
2052                .append(" is_passpoint=").append(info.isPasspoint)
2053                .append(" is_ephemeral=").append(info.isEphemeral)
2054                .append(" has_ever_connected=").append(info.hasEverConnected)
2055                .append(" scan_rssi=").append(info.scanRssi)
2056                .append(" scan_freq=").append(info.scanFreq);
2057        return sb.toString();
2058    }
2059
2060    public static final int MAX_STA_EVENTS = 512;
2061    private LinkedList<StaEventWithTime> mStaEventList = new LinkedList<StaEventWithTime>();
2062    private int mLastPollRssi = -127;
2063    private int mLastPollLinkSpeed = -1;
2064    private int mLastPollFreq = -1;
2065
2066    /**
2067     * Converts the first 31 bits of a BitSet to a little endian int
2068     */
2069    private static int bitSetToInt(BitSet bits) {
2070        int value = 0;
2071        int nBits = bits.length() < 31 ? bits.length() : 31;
2072        for (int i = 0; i < nBits; i++) {
2073            value += bits.get(i) ? (1 << i) : 0;
2074        }
2075        return value;
2076    }
2077    private void incrementSsid(SparseIntArray sia, int element) {
2078        increment(sia, Math.min(element, MAX_CONNECTABLE_SSID_NETWORK_BUCKET));
2079    }
2080    private void incrementBssid(SparseIntArray sia, int element) {
2081        increment(sia, Math.min(element, MAX_CONNECTABLE_BSSID_NETWORK_BUCKET));
2082    }
2083    private void incrementTotalScanResults(SparseIntArray sia, int element) {
2084        increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULTS_BUCKET));
2085    }
2086    private void incrementTotalScanSsids(SparseIntArray sia, int element) {
2087        increment(sia, Math.min(element, MAX_TOTAL_SCAN_RESULT_SSIDS_BUCKET));
2088    }
2089    private void increment(SparseIntArray sia, int element) {
2090        int count = sia.get(element);
2091        sia.put(element, count + 1);
2092    }
2093
2094    private static class StaEventWithTime {
2095        public StaEvent staEvent;
2096        public long wallClockMillis;
2097
2098        StaEventWithTime(StaEvent event, long wallClockMillis) {
2099            staEvent = event;
2100            this.wallClockMillis = wallClockMillis;
2101        }
2102
2103        public String toString() {
2104            StringBuilder sb = new StringBuilder();
2105            Calendar c = Calendar.getInstance();
2106            c.setTimeInMillis(wallClockMillis);
2107            if (wallClockMillis != 0) {
2108                sb.append(String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
2109            } else {
2110                sb.append("                  ");
2111            }
2112            sb.append(" ").append(staEventToString(staEvent));
2113            return sb.toString();
2114        }
2115    }
2116}
2117