Home | History | Annotate | Download | only in wifi
      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 
     17 package com.android.server.wifi;
     18 
     19 import android.net.wifi.ScanResult;
     20 import android.net.wifi.WifiConfiguration;
     21 import android.util.Base64;
     22 import android.util.Log;
     23 import android.util.SparseIntArray;
     24 
     25 import com.android.server.wifi.hotspot2.NetworkDetail;
     26 import com.android.server.wifi.util.InformationElementUtil;
     27 
     28 import java.io.FileDescriptor;
     29 import java.io.PrintWriter;
     30 import java.util.ArrayList;
     31 import java.util.Calendar;
     32 import 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  */
     42 public 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