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