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