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