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