WifiMetrics.java revision 49410c13ba2284fb7c836fd3527dc526ea5681a0
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.net.wifi.ScanResult;
20import android.net.wifi.WifiConfiguration;
21import android.util.Base64;
22import android.util.Log;
23import android.util.SparseIntArray;
24
25import com.android.server.wifi.hotspot2.NetworkDetail;
26import com.android.server.wifi.util.InformationElementUtil;
27
28import java.io.FileDescriptor;
29import java.io.PrintWriter;
30import java.util.ArrayList;
31import java.util.Calendar;
32import java.util.List;
33
34/**
35 * Provides storage for wireless connectivity metrics, as they are generated.
36 * Metrics logged by this class include:
37 *   Aggregated connection stats (num of connections, num of failures, ...)
38 *   Discrete connection event stats (time, duration, failure codes, ...)
39 *   Router details (technology type, authentication type, ...)
40 *   Scan stats
41 */
42public class WifiMetrics {
43    private static final String TAG = "WifiMetrics";
44    private static final boolean DBG = false;
45    private final Object mLock = new Object();
46    private static final int MAX_CONNECTION_EVENTS = 256;
47    private Clock mClock;
48    private boolean mScreenOn;
49    private int mWifiState;
50    /**
51     * Metrics are stored within an instance of the WifiLog proto during runtime,
52     * The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
53     * runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
54     * together at dump-time
55     */
56    private final WifiMetricsProto.WifiLog mWifiLogProto;
57    /**
58     * Session information that gets logged for every Wifi connection attempt.
59     */
60    private final List<ConnectionEvent> mConnectionEventList;
61    /**
62     * The latest started (but un-ended) connection attempt
63     */
64    private ConnectionEvent mCurrentConnectionEvent;
65    /**
66     * Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
67     */
68    private SparseIntArray mScanReturnEntries;
69    /**
70     * Mapping of system state to the counts of scans requested in that wifi state * screenOn
71     * combination. Indexed by WifiLog.WifiState * (1 + screenOn)
72     */
73    private SparseIntArray mWifiSystemStateEntries;
74    /**
75     * Records the elapsedRealtime (in seconds) that represents the beginning of data
76     * capture for for this WifiMetricsProto
77     */
78    private long mRecordStartTimeSec;
79
80    class RouterFingerPrint {
81        private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
82        RouterFingerPrint() {
83            mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
84        }
85
86        public String toString() {
87            StringBuilder sb = new StringBuilder();
88            synchronized (mLock) {
89                sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
90                sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
91                sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
92                sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
93                sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
94                sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
95                sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
96            }
97            return sb.toString();
98        }
99        public void updateFromWifiConfiguration(WifiConfiguration config) {
100            synchronized (mLock) {
101                if (config != null) {
102                    // Is this a hidden network
103                    mRouterFingerPrintProto.hidden = config.hiddenSSID;
104                    // Config may not have a valid dtimInterval set yet, in which case dtim will be zero
105                    // (These are only populated from beacon frame scan results, which are returned as
106                    // scan results from the chip far less frequently than Probe-responses)
107                    if (config.dtimInterval > 0) {
108                        mRouterFingerPrintProto.dtim = config.dtimInterval;
109                    }
110                    mCurrentConnectionEvent.mConfigSsid = config.SSID;
111                    // Get AuthType information from config (We do this again from ScanResult after
112                    // associating with BSSID)
113                    if (config.allowedKeyManagement != null
114                            && config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
115                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
116                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
117                    } else if (config.isEnterprise()) {
118                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
119                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
120                    } else {
121                        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
122                                .authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
123                    }
124                    // If there's a ScanResult candidate associated with this config already, get it and
125                    // log (more accurate) metrics from it
126                    ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
127                    if (candidate != null) {
128                        updateMetricsFromScanResult(candidate);
129                    }
130                }
131            }
132        }
133    }
134
135    /**
136     * Log event, tracking the start time, end time and result of a wireless connection attempt.
137     */
138    class ConnectionEvent {
139        WifiMetricsProto.ConnectionEvent mConnectionEvent;
140        //<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
141        //covering more than just l2 failures. see b/27652362
142        /**
143         * Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
144         * more failures than just l2 though, since the proto does not have a place to log
145         * framework failures)
146         */
147        // Failure is unknown
148        public static final int FAILURE_UNKNOWN = 0;
149        // NONE
150        public static final int FAILURE_NONE = 1;
151        // ASSOCIATION_REJECTION_EVENT
152        public static final int FAILURE_ASSOCIATION_REJECTION = 2;
153        // AUTHENTICATION_FAILURE_EVENT
154        public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
155        // SSID_TEMP_DISABLED (Also Auth failure)
156        public static final int FAILURE_SSID_TEMP_DISABLED = 4;
157        // reconnect() or reassociate() call to WifiNative failed
158        public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
159        // NETWORK_DISCONNECTION_EVENT
160        public static final int FAILURE_NETWORK_DISCONNECTION = 6;
161        // NEW_CONNECTION_ATTEMPT before previous finished
162        public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
163        // New connection attempt to the same network & bssid
164        public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
165        // Roam Watchdog timer triggered (Roaming timed out)
166        public static final int FAILURE_ROAM_TIMEOUT = 9;
167        // DHCP failure
168        public static final int FAILURE_DHCP = 10;
169
170        RouterFingerPrint mRouterFingerPrint;
171        private long mRealStartTime;
172        private long mRealEndTime;
173        private String mConfigSsid;
174        private String mConfigBssid;
175        private int mWifiState;
176        private boolean mScreenOn;
177
178        private ConnectionEvent() {
179            mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
180            mRealEndTime = 0;
181            mRealStartTime = 0;
182            mRouterFingerPrint = new RouterFingerPrint();
183            mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
184            mConfigSsid = "<NULL>";
185            mConfigBssid = "<NULL>";
186            mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
187            mScreenOn = false;
188        }
189
190        public String toString() {
191            StringBuilder sb = new StringBuilder();
192            sb.append("startTime=");
193            Calendar c = Calendar.getInstance();
194            synchronized (mLock) {
195                c.setTimeInMillis(mConnectionEvent.startTimeMillis);
196                sb.append(mConnectionEvent.startTimeMillis == 0 ? "            <null>" :
197                        String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
198                sb.append(", SSID=");
199                sb.append(mConfigSsid);
200                sb.append(", BSSID=");
201                sb.append(mConfigBssid);
202                sb.append(", durationMillis=");
203                sb.append(mConnectionEvent.durationTakenToConnectMillis);
204                sb.append(", roamType=");
205                switch(mConnectionEvent.roamType) {
206                    case 1:
207                        sb.append("ROAM_NONE");
208                        break;
209                    case 2:
210                        sb.append("ROAM_DBDC");
211                        break;
212                    case 3:
213                        sb.append("ROAM_ENTERPRISE");
214                        break;
215                    case 4:
216                        sb.append("ROAM_USER_SELECTED");
217                        break;
218                    case 5:
219                        sb.append("ROAM_UNRELATED");
220                        break;
221                    default:
222                        sb.append("ROAM_UNKNOWN");
223                }
224                sb.append(", connectionResult=");
225                sb.append(mConnectionEvent.connectionResult);
226                sb.append(", level2FailureCode=");
227                switch(mConnectionEvent.level2FailureCode) {
228                    case FAILURE_NONE:
229                        sb.append("NONE");
230                        break;
231                    case FAILURE_ASSOCIATION_REJECTION:
232                        sb.append("ASSOCIATION_REJECTION");
233                        break;
234                    case FAILURE_AUTHENTICATION_FAILURE:
235                        sb.append("AUTHENTICATION_FAILURE");
236                        break;
237                    case FAILURE_SSID_TEMP_DISABLED:
238                        sb.append("SSID_TEMP_DISABLED");
239                        break;
240                    case FAILURE_CONNECT_NETWORK_FAILED:
241                        sb.append("CONNECT_NETWORK_FAILED");
242                        break;
243                    case FAILURE_NETWORK_DISCONNECTION:
244                        sb.append("NETWORK_DISCONNECTION");
245                        break;
246                    case FAILURE_NEW_CONNECTION_ATTEMPT:
247                        sb.append("NEW_CONNECTION_ATTEMPT");
248                        break;
249                    case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
250                        sb.append("REDUNDANT_CONNECTION_ATTEMPT");
251                        break;
252                    case FAILURE_ROAM_TIMEOUT:
253                        sb.append("ROAM_TIMEOUT");
254                        break;
255                    case FAILURE_DHCP:
256                        sb.append("DHCP");
257                    default:
258                        sb.append("UNKNOWN");
259                        break;
260                }
261                sb.append(", connectivityLevelFailureCode=");
262                switch(mConnectionEvent.connectivityLevelFailureCode) {
263                    case WifiMetricsProto.ConnectionEvent.HLF_NONE:
264                        sb.append("NONE");
265                        break;
266                    case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
267                        sb.append("DHCP");
268                        break;
269                    case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
270                        sb.append("NO_INTERNET");
271                        break;
272                    case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
273                        sb.append("UNWANTED");
274                        break;
275                    default:
276                        sb.append("UNKNOWN");
277                        break;
278                }
279                sb.append(", signalStrength=");
280                sb.append(mConnectionEvent.signalStrength);
281                sb.append(", wifiState=");
282                switch(mWifiState) {
283                    case WifiMetricsProto.WifiLog.WIFI_DISABLED:
284                        sb.append("WIFI_DISABLED");
285                        break;
286                    case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
287                        sb.append("WIFI_DISCONNECTED");
288                        break;
289                    case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
290                        sb.append("WIFI_ASSOCIATED");
291                        break;
292                    default:
293                        sb.append("WIFI_UNKNOWN");
294                        break;
295                }
296                sb.append(", screenOn=");
297                sb.append(mScreenOn);
298                sb.append(". mRouterFingerprint: ");
299                sb.append(mRouterFingerPrint.toString());
300            }
301            return sb.toString();
302        }
303    }
304
305    public WifiMetrics(Clock clock) {
306        mClock = clock;
307        mWifiLogProto = new WifiMetricsProto.WifiLog();
308        mConnectionEventList = new ArrayList<>();
309        mScanReturnEntries = new SparseIntArray();
310        mWifiSystemStateEntries = new SparseIntArray();
311        mCurrentConnectionEvent = null;
312        mScreenOn = true;
313        mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
314        mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
315    }
316
317    // Values used for indexing SystemStateEntries
318    private static final int SCREEN_ON = 1;
319    private static final int SCREEN_OFF = 0;
320
321    /**
322     * Create a new connection event. Call when wifi attempts to make a new network connection
323     * If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
324     * failure code.
325     * Gathers and sets the RouterFingerPrint data as well
326     *
327     * @param config WifiConfiguration of the config used for the current connection attempt
328     * @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
329     */
330    public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
331        synchronized (mLock) {
332            // Check if this is overlapping another current connection event
333            if (mCurrentConnectionEvent != null) {
334                //Is this new Connection Event the same as the current one
335                if (mCurrentConnectionEvent.mConfigSsid != null
336                        && mCurrentConnectionEvent.mConfigBssid != null
337                        && config != null
338                        && mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
339                        && (mCurrentConnectionEvent.mConfigBssid.equals("any")
340                        || mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
341                    mCurrentConnectionEvent.mConfigBssid = targetBSSID;
342                    // End Connection Event due to new connection attempt to the same network
343                    endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
344                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
345                } else {
346                    // End Connection Event due to new connection attempt to different network
347                    endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
348                            WifiMetricsProto.ConnectionEvent.HLF_NONE);
349                }
350            }
351            //If past maximum connection events, start removing the oldest
352            while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
353                mConnectionEventList.remove(0);
354            }
355            mCurrentConnectionEvent = new ConnectionEvent();
356            mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
357                    mClock.currentTimeMillis();
358            mCurrentConnectionEvent.mConfigBssid = targetBSSID;
359            mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
360            mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
361            mCurrentConnectionEvent.mConfigBssid = "any";
362            mCurrentConnectionEvent.mRealStartTime = mClock.elapsedRealtime();
363            mCurrentConnectionEvent.mWifiState = mWifiState;
364            mCurrentConnectionEvent.mScreenOn = mScreenOn;
365            mConnectionEventList.add(mCurrentConnectionEvent);
366        }
367    }
368
369    /**
370     * set the RoamType of the current ConnectionEvent (if any)
371     */
372    public void setConnectionEventRoamType(int roamType) {
373        synchronized (mLock) {
374            if (mCurrentConnectionEvent != null) {
375                mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
376            }
377        }
378    }
379
380    /**
381     * Set AP related metrics from ScanDetail
382     */
383    public void setConnectionScanDetail(ScanDetail scanDetail) {
384        synchronized (mLock) {
385            if (mCurrentConnectionEvent != null && scanDetail != null) {
386                NetworkDetail networkDetail = scanDetail.getNetworkDetail();
387                ScanResult scanResult = scanDetail.getScanResult();
388                //Ensure that we have a networkDetail, and that it corresponds to the currently
389                //tracked connection attempt
390                if (networkDetail != null && scanResult != null
391                        && mCurrentConnectionEvent.mConfigSsid != null
392                        && mCurrentConnectionEvent.mConfigSsid
393                        .equals("\"" + networkDetail.getSSID() + "\"")) {
394                    updateMetricsFromNetworkDetail(networkDetail);
395                    updateMetricsFromScanResult(scanResult);
396                }
397            }
398        }
399    }
400
401    /**
402     * End a Connection event record. Call when wifi connection attempt succeeds or fails.
403     * If a Connection event has not been started and is active when .end is called, a new one is
404     * created with zero duration.
405     *
406     * @param level2FailureCode Level 2 failure code returned by supplicant
407     * @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
408     */
409    public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
410        synchronized (mLock) {
411            if (mCurrentConnectionEvent != null) {
412                boolean result = (level2FailureCode == 1)
413                        && (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
414                mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
415                mCurrentConnectionEvent.mRealEndTime = mClock.elapsedRealtime();
416                mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
417                        (mCurrentConnectionEvent.mRealEndTime
418                        - mCurrentConnectionEvent.mRealStartTime);
419                mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
420                mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
421                        connectivityFailureCode;
422                // ConnectionEvent already added to ConnectionEvents List. Safe to null current here
423                mCurrentConnectionEvent = null;
424            }
425        }
426    }
427
428    /**
429     * Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
430     */
431    private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
432        int dtimInterval = networkDetail.getDtimInterval();
433        if (dtimInterval > 0) {
434            mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
435                    dtimInterval;
436        }
437        int connectionWifiMode;
438        switch (networkDetail.getWifiMode()) {
439            case InformationElementUtil.WifiMode.MODE_UNDEFINED:
440                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
441                break;
442            case InformationElementUtil.WifiMode.MODE_11A:
443                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
444                break;
445            case InformationElementUtil.WifiMode.MODE_11B:
446                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
447                break;
448            case InformationElementUtil.WifiMode.MODE_11G:
449                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
450                break;
451            case InformationElementUtil.WifiMode.MODE_11N:
452                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
453                break;
454            case InformationElementUtil.WifiMode.MODE_11AC  :
455                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
456                break;
457            default:
458                connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
459                break;
460        }
461        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
462                .routerTechnology = connectionWifiMode;
463    }
464
465    /**
466     * Set ConnectionEvent RSSI and authentication type from ScanResult
467     */
468    private void updateMetricsFromScanResult(ScanResult scanResult) {
469        mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
470        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
471                WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
472        mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
473        if (scanResult.capabilities != null) {
474            if (scanResult.capabilities.contains("WEP")) {
475                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
476                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
477            } else if (scanResult.capabilities.contains("PSK")) {
478                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
479                        WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
480            } else if (scanResult.capabilities.contains("EAP")) {
481                mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
482                        WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
483            }
484        }
485        mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
486                scanResult.frequency;
487    }
488
489    void setNumSavedNetworks(int num) {
490        synchronized (mLock) {
491            mWifiLogProto.numSavedNetworks = num;
492        }
493    }
494
495    void setNumOpenNetworks(int num) {
496        synchronized (mLock) {
497            mWifiLogProto.numOpenNetworks = num;
498        }
499    }
500
501    void setNumPersonalNetworks(int num) {
502        synchronized (mLock) {
503            mWifiLogProto.numPersonalNetworks = num;
504        }
505    }
506
507    void setNumEnterpriseNetworks(int num) {
508        synchronized (mLock) {
509            mWifiLogProto.numEnterpriseNetworks = num;
510        }
511    }
512
513    void setNumNetworksAddedByUser(int num) {
514        synchronized (mLock) {
515            mWifiLogProto.numNetworksAddedByUser = num;
516        }
517    }
518
519    void setNumNetworksAddedByApps(int num) {
520        synchronized (mLock) {
521            mWifiLogProto.numNetworksAddedByApps = num;
522        }
523    }
524
525    void setIsLocationEnabled(boolean enabled) {
526        synchronized (mLock) {
527            mWifiLogProto.isLocationEnabled = enabled;
528        }
529    }
530
531    void setIsScanningAlwaysEnabled(boolean enabled) {
532        synchronized (mLock) {
533            mWifiLogProto.isScanningAlwaysEnabled = enabled;
534        }
535    }
536
537    /**
538     * Increment Non Empty Scan Results count
539     */
540    public void incrementNonEmptyScanResultCount() {
541        if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
542        synchronized (mLock) {
543            mWifiLogProto.numNonEmptyScanResults++;
544        }
545    }
546
547    /**
548     * Increment Empty Scan Results count
549     */
550    public void incrementEmptyScanResultCount() {
551        if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
552        synchronized (mLock) {
553            mWifiLogProto.numEmptyScanResults++;
554        }
555    }
556
557    /**
558     * Increment background scan count
559     */
560    public void incrementBackgroundScanCount() {
561        if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
562        synchronized (mLock) {
563            mWifiLogProto.numBackgroundScans++;
564        }
565    }
566
567   /**
568     * Get Background scan count
569     */
570    public int getBackgroundScanCount() {
571        synchronized (mLock) {
572            return mWifiLogProto.numBackgroundScans;
573        }
574    }
575
576    /**
577     * Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
578     */
579    public void incrementOneshotScanCount() {
580        synchronized (mLock) {
581            mWifiLogProto.numOneshotScans++;
582        }
583        incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
584    }
585
586    /**
587     * Get oneshot scan count
588     */
589    public int getOneshotScanCount() {
590        synchronized (mLock) {
591            return mWifiLogProto.numOneshotScans;
592        }
593    }
594
595    private String returnCodeToString(int scanReturnCode) {
596        switch(scanReturnCode){
597            case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
598                return "SCAN_UNKNOWN";
599            case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
600                return "SCAN_SUCCESS";
601            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
602                return "SCAN_FAILURE_INTERRUPTED";
603            case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
604                return "SCAN_FAILURE_INVALID_CONFIGURATION";
605            case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
606                return "FAILURE_WIFI_DISABLED";
607            default:
608                return "<UNKNOWN>";
609        }
610    }
611
612    /**
613     * Increment count of scan return code occurrence
614     *
615     * @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
616     */
617    public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
618        synchronized (mLock) {
619            if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
620            int entry = mScanReturnEntries.get(scanReturnCode);
621            entry += countToAdd;
622            mScanReturnEntries.put(scanReturnCode, entry);
623        }
624    }
625    /**
626     * Get the count of this scanReturnCode
627     * @param scanReturnCode that we are getting the count for
628     */
629    public int getScanReturnEntry(int scanReturnCode) {
630        synchronized (mLock) {
631            return mScanReturnEntries.get(scanReturnCode);
632        }
633    }
634
635    private String wifiSystemStateToString(int state) {
636        switch(state){
637            case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
638                return "WIFI_UNKNOWN";
639            case WifiMetricsProto.WifiLog.WIFI_DISABLED:
640                return "WIFI_DISABLED";
641            case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
642                return "WIFI_DISCONNECTED";
643            case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
644                return "WIFI_ASSOCIATED";
645            default:
646                return "default";
647        }
648    }
649
650    /**
651     * Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
652     *
653     * @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
654     * @param screenOn Is the screen on
655     */
656    public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
657        synchronized (mLock) {
658            if (DBG) {
659                Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
660                        + " " + screenOn);
661            }
662            int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
663            int entry = mWifiSystemStateEntries.get(index);
664            entry++;
665            mWifiSystemStateEntries.put(index, entry);
666        }
667    }
668
669    /**
670     * Get the count of this system State Entry
671     */
672    public int getSystemStateCount(int state, boolean screenOn) {
673        synchronized (mLock) {
674            int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
675            return mWifiSystemStateEntries.get(index);
676        }
677    }
678
679    /**
680     * Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
681     */
682    public void incrementNumLastResortWatchdogTriggers() {
683        synchronized (mLock) {
684            mWifiLogProto.numLastResortWatchdogTriggers++;
685        }
686    }
687    /**
688     * @param count number of networks over bad association threshold when watchdog triggered
689     */
690    public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
691        synchronized (mLock) {
692            mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
693        }
694    }
695    /**
696     * @param count number of networks over bad authentication threshold when watchdog triggered
697     */
698    public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
699        synchronized (mLock) {
700            mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
701        }
702    }
703    /**
704     * @param count number of networks over bad dhcp threshold when watchdog triggered
705     */
706    public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
707        synchronized (mLock) {
708            mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
709        }
710    }
711    /**
712     * @param count number of networks over bad other threshold when watchdog triggered
713     */
714    public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
715        synchronized (mLock) {
716            mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
717        }
718    }
719    /**
720     * @param count number of networks seen when watchdog triggered
721     */
722    public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
723        synchronized (mLock) {
724            mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
725        }
726    }
727    /**
728     * Increment count of triggers with atleast one bad association network
729     */
730    public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
731        synchronized (mLock) {
732            mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
733        }
734    }
735    /**
736     * Increment count of triggers with atleast one bad authentication network
737     */
738    public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
739        synchronized (mLock) {
740            mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
741        }
742    }
743    /**
744     * Increment count of triggers with atleast one bad dhcp network
745     */
746    public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
747        synchronized (mLock) {
748            mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
749        }
750    }
751    /**
752     * Increment count of triggers with atleast one bad other network
753     */
754    public void incrementNumLastResortWatchdogTriggersWithBadOther() {
755        synchronized (mLock) {
756            mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
757        }
758    }
759
760    /**
761     * Increment number of times connectivity watchdog confirmed pno is working
762     */
763    public void incrementNumConnectivityWatchdogPnoGood() {
764        synchronized (mLock) {
765            mWifiLogProto.numConnectivityWatchdogPnoGood++;
766        }
767    }
768    /**
769     * Increment number of times connectivity watchdog found pno not working
770     */
771    public void incrementNumConnectivityWatchdogPnoBad() {
772        synchronized (mLock) {
773            mWifiLogProto.numConnectivityWatchdogPnoBad++;
774        }
775    }
776    /**
777     * Increment number of times connectivity watchdog confirmed background scan is working
778     */
779    public void incrementNumConnectivityWatchdogBackgroundGood() {
780        synchronized (mLock) {
781            mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
782        }
783    }
784    /**
785     * Increment number of times connectivity watchdog found background scan not working
786     */
787    public void incrementNumConnectivityWatchdogBackgroundBad() {
788        synchronized (mLock) {
789            mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
790        }
791    }
792
793    public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
794    /**
795     * Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
796     * at this time
797     *
798     * @param fd unused
799     * @param pw PrintWriter for writing dump to
800     * @param args unused
801     */
802    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
803        synchronized (mLock) {
804            pw.println("WifiMetrics:");
805            if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
806                //Dump serialized WifiLog proto
807                consolidateProto(true);
808                for (ConnectionEvent event : mConnectionEventList) {
809                    if (mCurrentConnectionEvent != event) {
810                        //indicate that automatic bug report has been taken for all valid
811                        //connection events
812                        event.mConnectionEvent.automaticBugReportTaken = true;
813                    }
814                }
815                byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
816                String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
817                pw.println(metricsProtoDump);
818                pw.println("EndWifiMetrics");
819                clear();
820            } else {
821                pw.println("mConnectionEvents:");
822                for (ConnectionEvent event : mConnectionEventList) {
823                    String eventLine = event.toString();
824                    if (event == mCurrentConnectionEvent) {
825                        eventLine += "CURRENTLY OPEN EVENT";
826                    }
827                    pw.println(eventLine);
828                }
829                pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
830                pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
831                pw.println("mWifiLogProto.numPersonalNetworks="
832                        + mWifiLogProto.numPersonalNetworks);
833                pw.println("mWifiLogProto.numEnterpriseNetworks="
834                        + mWifiLogProto.numEnterpriseNetworks);
835                pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
836                pw.println("mWifiLogProto.isScanningAlwaysEnabled="
837                        + mWifiLogProto.isScanningAlwaysEnabled);
838                pw.println("mWifiLogProto.numNetworksAddedByUser="
839                        + mWifiLogProto.numNetworksAddedByUser);
840                pw.println("mWifiLogProto.numNetworksAddedByApps="
841                        + mWifiLogProto.numNetworksAddedByApps);
842                pw.println("mWifiLogProto.numNonEmptyScanResults="
843                        + mWifiLogProto.numNonEmptyScanResults);
844                pw.println("mWifiLogProto.numEmptyScanResults="
845                        + mWifiLogProto.numEmptyScanResults);
846                pw.println("mWifiLogProto.numOneshotScans="
847                        + mWifiLogProto.numOneshotScans);
848                pw.println("mWifiLogProto.numBackgroundScans="
849                        + mWifiLogProto.numBackgroundScans);
850
851                pw.println("mScanReturnEntries:");
852                pw.println("  SCAN_UNKNOWN: " + getScanReturnEntry(
853                        WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
854                pw.println("  SCAN_SUCCESS: " + getScanReturnEntry(
855                        WifiMetricsProto.WifiLog.SCAN_SUCCESS));
856                pw.println("  SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
857                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
858                pw.println("  SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
859                        WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
860                pw.println("  FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
861                        WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
862
863                pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
864                pw.println("  WIFI_UNKNOWN       ON: "
865                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
866                pw.println("  WIFI_DISABLED      ON: "
867                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
868                pw.println("  WIFI_DISCONNECTED  ON: "
869                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
870                pw.println("  WIFI_ASSOCIATED    ON: "
871                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
872                pw.println("  WIFI_UNKNOWN      OFF: "
873                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
874                pw.println("  WIFI_DISABLED     OFF: "
875                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
876                pw.println("  WIFI_DISCONNECTED OFF: "
877                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
878                pw.println("  WIFI_ASSOCIATED   OFF: "
879                        + getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
880                pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
881                        + mWifiLogProto.numConnectivityWatchdogPnoGood);
882                pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
883                        + mWifiLogProto.numConnectivityWatchdogPnoBad);
884                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
885                        + mWifiLogProto.numConnectivityWatchdogBackgroundGood);
886                pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
887                        + mWifiLogProto.numConnectivityWatchdogBackgroundBad);
888                pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
889                        + mWifiLogProto.numLastResortWatchdogTriggers);
890                pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
891                        + mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
892                pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
893                        + mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
894                pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
895                        + mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
896                pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
897                        + mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
898                pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
899                        + mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
900                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
901                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
902                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
903                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
904                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
905                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
906                pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
907                        + mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
908                pw.println("mWifiLogProto.recordDurationSec="
909                        + ((mClock.elapsedRealtime() / 1000) - mRecordStartTimeSec));
910            }
911        }
912    }
913
914    /**
915     * append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
916     * respective lists within mWifiLogProto
917     *
918     * @param incremental Only include ConnectionEvents created since last automatic bug report
919     */
920    private void consolidateProto(boolean incremental) {
921        List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
922        synchronized (mLock) {
923            for (ConnectionEvent event : mConnectionEventList) {
924                // If this is not incremental, dump full ConnectionEvent list
925                // Else Dump all un-dumped events except for the current one
926                if (!incremental || ((mCurrentConnectionEvent != event)
927                        && !event.mConnectionEvent.automaticBugReportTaken)) {
928                    //Get all ConnectionEvents that haven not been dumped as a proto, also exclude
929                    //the current active un-ended connection event
930                    events.add(event.mConnectionEvent);
931                    if (incremental) {
932                        event.mConnectionEvent.automaticBugReportTaken = true;
933                    }
934                }
935            }
936            if (events.size() > 0) {
937                mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
938            }
939
940            //Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
941            mWifiLogProto.scanReturnEntries =
942                    new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
943            for (int i = 0; i < mScanReturnEntries.size(); i++) {
944                mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
945                mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
946                mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
947            }
948
949            // Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
950            // This one is slightly more complex, as the Sparse are indexed with:
951            //     key: wifiState * 2 + isScreenOn, value: wifiStateCount
952            mWifiLogProto.wifiSystemStateEntries =
953                    new WifiMetricsProto.WifiLog
954                    .WifiSystemStateEntry[mWifiSystemStateEntries.size()];
955            for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
956                mWifiLogProto.wifiSystemStateEntries[i] =
957                        new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
958                mWifiLogProto.wifiSystemStateEntries[i].wifiState =
959                        mWifiSystemStateEntries.keyAt(i) / 2;
960                mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
961                        mWifiSystemStateEntries.valueAt(i);
962                mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
963                        (mWifiSystemStateEntries.keyAt(i) % 2) > 0;
964            }
965            mWifiLogProto.recordDurationSec = (int) ((mClock.elapsedRealtime() / 1000)
966                    - mRecordStartTimeSec);
967        }
968    }
969
970    /**
971     * Clear all WifiMetrics, except for currentConnectionEvent.
972     */
973    private void clear() {
974        synchronized (mLock) {
975            mConnectionEventList.clear();
976            if (mCurrentConnectionEvent != null) {
977                mConnectionEventList.add(mCurrentConnectionEvent);
978            }
979            mScanReturnEntries.clear();
980            mWifiSystemStateEntries.clear();
981            mRecordStartTimeSec = mClock.elapsedRealtime() / 1000;
982            mWifiLogProto.clear();
983        }
984    }
985
986    /**
987     *  Set screen state (On/Off)
988     */
989    public void setScreenState(boolean screenOn) {
990        synchronized (mLock) {
991            mScreenOn = screenOn;
992        }
993    }
994
995    /**
996     *  Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
997     */
998    public void setWifiState(int wifiState) {
999        synchronized (mLock) {
1000            mWifiState = wifiState;
1001        }
1002    }
1003}
1004