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