blob: 67914ae03657ebddd1f79e1c6380f6ca4d0edabf [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.SystemClock;
import android.util.Base64;
import android.util.Log;
import android.util.SparseIntArray;
import com.android.server.wifi.hotspot2.NetworkDetail;
import com.android.server.wifi.util.InformationElementUtil;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
/**
* Provides storage for wireless connectivity metrics, as they are generated.
* Metrics logged by this class include:
* Aggregated connection stats (num of connections, num of failures, ...)
* Discrete connection event stats (time, duration, failure codes, ...)
* Router details (technology type, authentication type, ...)
* Scan stats
*/
public class WifiMetrics {
private static final String TAG = "WifiMetrics";
private static final boolean DBG = false;
private final Object mLock = new Object();
private static final int MAX_CONNECTION_EVENTS = 256;
private boolean mScreenOn;
private int mWifiState;
/**
* Metrics are stored within an instance of the WifiLog proto during runtime,
* The ConnectionEvent, SystemStateEntries & ScanReturnEntries metrics are stored during
* runtime in member lists of this WifiMetrics class, with the final WifiLog proto being pieced
* together at dump-time
*/
private final WifiMetricsProto.WifiLog mWifiLogProto;
/**
* Session information that gets logged for every Wifi connection attempt.
*/
private final List<ConnectionEvent> mConnectionEventList;
/**
* The latest started (but un-ended) connection attempt
*/
private ConnectionEvent mCurrentConnectionEvent;
/**
* Count of number of times each scan return code, indexed by WifiLog.ScanReturnCode
*/
private SparseIntArray mScanReturnEntries;
/**
* Mapping of system state to the counts of scans requested in that wifi state * screenOn
* combination. Indexed by WifiLog.WifiState * (1 + screenOn)
*/
private SparseIntArray mWifiSystemStateEntries;
class RouterFingerPrint {
private WifiMetricsProto.RouterFingerPrint mRouterFingerPrintProto;
RouterFingerPrint() {
mRouterFingerPrintProto = new WifiMetricsProto.RouterFingerPrint();
}
public String toString() {
StringBuilder sb = new StringBuilder();
synchronized (mLock) {
sb.append("mConnectionEvent.roamType=" + mRouterFingerPrintProto.roamType);
sb.append(", mChannelInfo=" + mRouterFingerPrintProto.channelInfo);
sb.append(", mDtim=" + mRouterFingerPrintProto.dtim);
sb.append(", mAuthentication=" + mRouterFingerPrintProto.authentication);
sb.append(", mHidden=" + mRouterFingerPrintProto.hidden);
sb.append(", mRouterTechnology=" + mRouterFingerPrintProto.routerTechnology);
sb.append(", mSupportsIpv6=" + mRouterFingerPrintProto.supportsIpv6);
}
return sb.toString();
}
public void updateFromWifiConfiguration(WifiConfiguration config) {
synchronized (mLock) {
if (config != null) {
// Is this a hidden network
mRouterFingerPrintProto.hidden = config.hiddenSSID;
// Config may not have a valid dtimInterval set yet, in which case dtim will be zero
// (These are only populated from beacon frame scan results, which are returned as
// scan results from the chip far less frequently than Probe-responses)
if (config.dtimInterval > 0) {
mRouterFingerPrintProto.dtim = config.dtimInterval;
}
mCurrentConnectionEvent.mConfigSsid = config.SSID;
// Get AuthType information from config (We do this again from ScanResult after
// associating with BSSID)
if (config.allowedKeyManagement != null
&& config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
.authentication = WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
} else if (config.isEnterprise()) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
.authentication = WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
} else {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
.authentication = WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
}
// If there's a ScanResult candidate associated with this config already, get it and
// log (more accurate) metrics from it
ScanResult candidate = config.getNetworkSelectionStatus().getCandidate();
if (candidate != null) {
updateMetricsFromScanResult(candidate);
}
}
}
}
}
/**
* Log event, tracking the start time, end time and result of a wireless connection attempt.
*/
class ConnectionEvent {
WifiMetricsProto.ConnectionEvent mConnectionEvent;
//<TODO> Move these constants into a wifi.proto Enum, and create a new Failure Type field
//covering more than just l2 failures. see b/27652362
/**
* Failure codes, used for the 'level_2_failure_code' Connection event field (covers a lot
* more failures than just l2 though, since the proto does not have a place to log
* framework failures)
*/
// Failure is unknown
public static final int FAILURE_UNKNOWN = 0;
// NONE
public static final int FAILURE_NONE = 1;
// ASSOCIATION_REJECTION_EVENT
public static final int FAILURE_ASSOCIATION_REJECTION = 2;
// AUTHENTICATION_FAILURE_EVENT
public static final int FAILURE_AUTHENTICATION_FAILURE = 3;
// SSID_TEMP_DISABLED (Also Auth failure)
public static final int FAILURE_SSID_TEMP_DISABLED = 4;
// reconnect() or reassociate() call to WifiNative failed
public static final int FAILURE_CONNECT_NETWORK_FAILED = 5;
// NETWORK_DISCONNECTION_EVENT
public static final int FAILURE_NETWORK_DISCONNECTION = 6;
// NEW_CONNECTION_ATTEMPT before previous finished
public static final int FAILURE_NEW_CONNECTION_ATTEMPT = 7;
// New connection attempt to the same network & bssid
public static final int FAILURE_REDUNDANT_CONNECTION_ATTEMPT = 8;
// Roam Watchdog timer triggered (Roaming timed out)
public static final int FAILURE_ROAM_TIMEOUT = 9;
// DHCP failure
public static final int FAILURE_DHCP = 10;
RouterFingerPrint mRouterFingerPrint;
private long mRealStartTime;
private long mRealEndTime;
private String mConfigSsid;
private String mConfigBssid;
private int mWifiState;
private boolean mScreenOn;
private ConnectionEvent() {
mConnectionEvent = new WifiMetricsProto.ConnectionEvent();
mRealEndTime = 0;
mRealStartTime = 0;
mRouterFingerPrint = new RouterFingerPrint();
mConnectionEvent.routerFingerprint = mRouterFingerPrint.mRouterFingerPrintProto;
mConfigSsid = "<NULL>";
mConfigBssid = "<NULL>";
mWifiState = WifiMetricsProto.WifiLog.WIFI_UNKNOWN;
mScreenOn = false;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("startTime=");
Calendar c = Calendar.getInstance();
synchronized (mLock) {
c.setTimeInMillis(mConnectionEvent.startTimeMillis);
sb.append(mConnectionEvent.startTimeMillis == 0 ? " <null>" :
String.format("%tm-%td %tH:%tM:%tS.%tL", c, c, c, c, c, c));
sb.append(", SSID=");
sb.append(mConfigSsid);
sb.append(", BSSID=");
sb.append(mConfigBssid);
sb.append(", durationMillis=");
sb.append(mConnectionEvent.durationTakenToConnectMillis);
sb.append(", roamType=");
switch(mConnectionEvent.roamType) {
case 1:
sb.append("ROAM_NONE");
break;
case 2:
sb.append("ROAM_DBDC");
break;
case 3:
sb.append("ROAM_ENTERPRISE");
break;
case 4:
sb.append("ROAM_USER_SELECTED");
break;
case 5:
sb.append("ROAM_UNRELATED");
break;
default:
sb.append("ROAM_UNKNOWN");
}
sb.append(", connectionResult=");
sb.append(mConnectionEvent.connectionResult);
sb.append(", level2FailureCode=");
switch(mConnectionEvent.level2FailureCode) {
case FAILURE_NONE:
sb.append("NONE");
break;
case FAILURE_ASSOCIATION_REJECTION:
sb.append("ASSOCIATION_REJECTION");
break;
case FAILURE_AUTHENTICATION_FAILURE:
sb.append("AUTHENTICATION_FAILURE");
break;
case FAILURE_SSID_TEMP_DISABLED:
sb.append("SSID_TEMP_DISABLED");
break;
case FAILURE_CONNECT_NETWORK_FAILED:
sb.append("CONNECT_NETWORK_FAILED");
break;
case FAILURE_NETWORK_DISCONNECTION:
sb.append("NETWORK_DISCONNECTION");
break;
case FAILURE_NEW_CONNECTION_ATTEMPT:
sb.append("NEW_CONNECTION_ATTEMPT");
break;
case FAILURE_REDUNDANT_CONNECTION_ATTEMPT:
sb.append("REDUNDANT_CONNECTION_ATTEMPT");
break;
case FAILURE_ROAM_TIMEOUT:
sb.append("ROAM_TIMEOUT");
break;
case FAILURE_DHCP:
sb.append("DHCP");
default:
sb.append("UNKNOWN");
break;
}
sb.append(", connectivityLevelFailureCode=");
switch(mConnectionEvent.connectivityLevelFailureCode) {
case WifiMetricsProto.ConnectionEvent.HLF_NONE:
sb.append("NONE");
break;
case WifiMetricsProto.ConnectionEvent.HLF_DHCP:
sb.append("DHCP");
break;
case WifiMetricsProto.ConnectionEvent.HLF_NO_INTERNET:
sb.append("NO_INTERNET");
break;
case WifiMetricsProto.ConnectionEvent.HLF_UNWANTED:
sb.append("UNWANTED");
break;
default:
sb.append("UNKNOWN");
break;
}
sb.append(", signalStrength=");
sb.append(mConnectionEvent.signalStrength);
sb.append(", wifiState=");
switch(mWifiState) {
case WifiMetricsProto.WifiLog.WIFI_DISABLED:
sb.append("WIFI_DISABLED");
break;
case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
sb.append("WIFI_DISCONNECTED");
break;
case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
sb.append("WIFI_ASSOCIATED");
break;
default:
sb.append("WIFI_UNKNOWN");
break;
}
sb.append(", screenOn=");
sb.append(mScreenOn);
sb.append(". mRouterFingerprint: ");
sb.append(mRouterFingerPrint.toString());
}
return sb.toString();
}
}
public WifiMetrics() {
mWifiLogProto = new WifiMetricsProto.WifiLog();
mConnectionEventList = new ArrayList<>();
mScanReturnEntries = new SparseIntArray();
mWifiSystemStateEntries = new SparseIntArray();
mCurrentConnectionEvent = null;
mScreenOn = true;
mWifiState = WifiMetricsProto.WifiLog.WIFI_DISABLED;
}
// Values used for indexing SystemStateEntries
private static final int SCREEN_ON = 1;
private static final int SCREEN_OFF = 0;
/**
* Create a new connection event. Call when wifi attempts to make a new network connection
* If there is a current 'un-ended' connection event, it will be ended with UNKNOWN connectivity
* failure code.
* Gathers and sets the RouterFingerPrint data as well
*
* @param config WifiConfiguration of the config used for the current connection attempt
* @param roamType Roam type that caused connection attempt, see WifiMetricsProto.WifiLog.ROAM_X
*/
public void startConnectionEvent(WifiConfiguration config, String targetBSSID, int roamType) {
synchronized (mLock) {
// Check if this is overlapping another current connection event
if (mCurrentConnectionEvent != null) {
//Is this new Connection Event the same as the current one
if (mCurrentConnectionEvent.mConfigSsid != null
&& mCurrentConnectionEvent.mConfigBssid != null
&& config != null
&& mCurrentConnectionEvent.mConfigSsid.equals(config.SSID)
&& (mCurrentConnectionEvent.mConfigBssid.equals("any")
|| mCurrentConnectionEvent.mConfigBssid.equals(targetBSSID))) {
mCurrentConnectionEvent.mConfigBssid = targetBSSID;
// End Connection Event due to new connection attempt to the same network
endConnectionEvent(ConnectionEvent.FAILURE_REDUNDANT_CONNECTION_ATTEMPT,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
} else {
// End Connection Event due to new connection attempt to different network
endConnectionEvent(ConnectionEvent.FAILURE_NEW_CONNECTION_ATTEMPT,
WifiMetricsProto.ConnectionEvent.HLF_NONE);
}
}
//If past maximum connection events, start removing the oldest
while(mConnectionEventList.size() >= MAX_CONNECTION_EVENTS) {
mConnectionEventList.remove(0);
}
mCurrentConnectionEvent = new ConnectionEvent();
mCurrentConnectionEvent.mConnectionEvent.startTimeMillis =
System.currentTimeMillis();
mCurrentConnectionEvent.mConfigBssid = targetBSSID;
mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
mCurrentConnectionEvent.mRouterFingerPrint.updateFromWifiConfiguration(config);
mCurrentConnectionEvent.mConfigBssid = "any";
mCurrentConnectionEvent.mRealStartTime = SystemClock.elapsedRealtime();
mCurrentConnectionEvent.mWifiState = mWifiState;
mCurrentConnectionEvent.mScreenOn = mScreenOn;
mConnectionEventList.add(mCurrentConnectionEvent);
}
}
/**
* set the RoamType of the current ConnectionEvent (if any)
*/
public void setConnectionEventRoamType(int roamType) {
synchronized (mLock) {
if (mCurrentConnectionEvent != null) {
mCurrentConnectionEvent.mConnectionEvent.roamType = roamType;
}
}
}
/**
* Set AP related metrics from ScanDetail
*/
public void setConnectionScanDetail(ScanDetail scanDetail) {
synchronized (mLock) {
if (mCurrentConnectionEvent != null && scanDetail != null) {
NetworkDetail networkDetail = scanDetail.getNetworkDetail();
ScanResult scanResult = scanDetail.getScanResult();
//Ensure that we have a networkDetail, and that it corresponds to the currently
//tracked connection attempt
if (networkDetail != null && scanResult != null
&& mCurrentConnectionEvent.mConfigSsid != null
&& mCurrentConnectionEvent.mConfigSsid
.equals("\"" + networkDetail.getSSID() + "\"")) {
updateMetricsFromNetworkDetail(networkDetail);
updateMetricsFromScanResult(scanResult);
}
}
}
}
/**
* End a Connection event record. Call when wifi connection attempt succeeds or fails.
* If a Connection event has not been started and is active when .end is called, a new one is
* created with zero duration.
*
* @param level2FailureCode Level 2 failure code returned by supplicant
* @param connectivityFailureCode WifiMetricsProto.ConnectionEvent.HLF_X
*/
public void endConnectionEvent(int level2FailureCode, int connectivityFailureCode) {
synchronized (mLock) {
if (mCurrentConnectionEvent != null) {
boolean result = (level2FailureCode == 1)
&& (connectivityFailureCode == WifiMetricsProto.ConnectionEvent.HLF_NONE);
mCurrentConnectionEvent.mConnectionEvent.connectionResult = result ? 1 : 0;
mCurrentConnectionEvent.mRealEndTime = SystemClock.elapsedRealtime();
mCurrentConnectionEvent.mConnectionEvent.durationTakenToConnectMillis = (int)
(mCurrentConnectionEvent.mRealEndTime
- mCurrentConnectionEvent.mRealStartTime);
mCurrentConnectionEvent.mConnectionEvent.level2FailureCode = level2FailureCode;
mCurrentConnectionEvent.mConnectionEvent.connectivityLevelFailureCode =
connectivityFailureCode;
// ConnectionEvent already added to ConnectionEvents List. Safe to null current here
mCurrentConnectionEvent = null;
}
}
}
/**
* Set ConnectionEvent DTIM Interval (if set), and 802.11 Connection mode, from NetworkDetail
*/
private void updateMetricsFromNetworkDetail(NetworkDetail networkDetail) {
int dtimInterval = networkDetail.getDtimInterval();
if (dtimInterval > 0) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.dtim =
dtimInterval;
}
int connectionWifiMode;
switch (networkDetail.getWifiMode()) {
case InformationElementUtil.WifiMode.MODE_UNDEFINED:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_UNKNOWN;
break;
case InformationElementUtil.WifiMode.MODE_11A:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_A;
break;
case InformationElementUtil.WifiMode.MODE_11B:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_B;
break;
case InformationElementUtil.WifiMode.MODE_11G:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_G;
break;
case InformationElementUtil.WifiMode.MODE_11N:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_N;
break;
case InformationElementUtil.WifiMode.MODE_11AC :
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_AC;
break;
default:
connectionWifiMode = WifiMetricsProto.RouterFingerPrint.ROUTER_TECH_OTHER;
break;
}
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto
.routerTechnology = connectionWifiMode;
}
/**
* Set ConnectionEvent RSSI and authentication type from ScanResult
*/
private void updateMetricsFromScanResult(ScanResult scanResult) {
mCurrentConnectionEvent.mConnectionEvent.signalStrength = scanResult.level;
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_OPEN;
mCurrentConnectionEvent.mConfigBssid = scanResult.BSSID;
if (scanResult.capabilities != null) {
if (scanResult.capabilities.contains("WEP")) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
} else if (scanResult.capabilities.contains("PSK")) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_PERSONAL;
} else if (scanResult.capabilities.contains("EAP")) {
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.authentication =
WifiMetricsProto.RouterFingerPrint.AUTH_ENTERPRISE;
}
}
mCurrentConnectionEvent.mRouterFingerPrint.mRouterFingerPrintProto.channelInfo =
scanResult.frequency;
}
void setNumSavedNetworks(int num) {
synchronized (mLock) {
mWifiLogProto.numSavedNetworks = num;
}
}
void setNumOpenNetworks(int num) {
synchronized (mLock) {
mWifiLogProto.numOpenNetworks = num;
}
}
void setNumPersonalNetworks(int num) {
synchronized (mLock) {
mWifiLogProto.numPersonalNetworks = num;
}
}
void setNumEnterpriseNetworks(int num) {
synchronized (mLock) {
mWifiLogProto.numEnterpriseNetworks = num;
}
}
void setNumNetworksAddedByUser(int num) {
synchronized (mLock) {
mWifiLogProto.numNetworksAddedByUser = num;
}
}
void setNumNetworksAddedByApps(int num) {
synchronized (mLock) {
mWifiLogProto.numNetworksAddedByApps = num;
}
}
void setIsLocationEnabled(boolean enabled) {
synchronized (mLock) {
mWifiLogProto.isLocationEnabled = enabled;
}
}
void setIsScanningAlwaysEnabled(boolean enabled) {
synchronized (mLock) {
mWifiLogProto.isScanningAlwaysEnabled = enabled;
}
}
/**
* Increment Non Empty Scan Results count
*/
public void incrementNonEmptyScanResultCount() {
if (DBG) Log.v(TAG, "incrementNonEmptyScanResultCount");
synchronized (mLock) {
mWifiLogProto.numNonEmptyScanResults++;
}
}
/**
* Increment Empty Scan Results count
*/
public void incrementEmptyScanResultCount() {
if (DBG) Log.v(TAG, "incrementEmptyScanResultCount");
synchronized (mLock) {
mWifiLogProto.numEmptyScanResults++;
}
}
/**
* Increment background scan count
*/
public void incrementBackgroundScanCount() {
if (DBG) Log.v(TAG, "incrementBackgroundScanCount");
synchronized (mLock) {
mWifiLogProto.numBackgroundScans++;
}
}
/**
* Get Background scan count
*/
public int getBackgroundScanCount() {
synchronized (mLock) {
return mWifiLogProto.numBackgroundScans;
}
}
/**
* Increment oneshot scan count, and the associated WifiSystemScanStateCount entry
*/
public void incrementOneshotScanCount() {
synchronized (mLock) {
mWifiLogProto.numOneshotScans++;
}
incrementWifiSystemScanStateCount(mWifiState, mScreenOn);
}
/**
* Get oneshot scan count
*/
public int getOneshotScanCount() {
synchronized (mLock) {
return mWifiLogProto.numOneshotScans;
}
}
private String returnCodeToString(int scanReturnCode) {
switch(scanReturnCode){
case WifiMetricsProto.WifiLog.SCAN_UNKNOWN:
return "SCAN_UNKNOWN";
case WifiMetricsProto.WifiLog.SCAN_SUCCESS:
return "SCAN_SUCCESS";
case WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED:
return "SCAN_FAILURE_INTERRUPTED";
case WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION:
return "SCAN_FAILURE_INVALID_CONFIGURATION";
case WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED:
return "FAILURE_WIFI_DISABLED";
default:
return "<UNKNOWN>";
}
}
/**
* Increment count of scan return code occurrence
*
* @param scanReturnCode Return code from scan attempt WifiMetricsProto.WifiLog.SCAN_X
*/
public void incrementScanReturnEntry(int scanReturnCode, int countToAdd) {
synchronized (mLock) {
if (DBG) Log.v(TAG, "incrementScanReturnEntry " + returnCodeToString(scanReturnCode));
int entry = mScanReturnEntries.get(scanReturnCode);
entry += countToAdd;
mScanReturnEntries.put(scanReturnCode, entry);
}
}
/**
* Get the count of this scanReturnCode
* @param scanReturnCode that we are getting the count for
*/
public int getScanReturnEntry(int scanReturnCode) {
synchronized (mLock) {
return mScanReturnEntries.get(scanReturnCode);
}
}
private String wifiSystemStateToString(int state) {
switch(state){
case WifiMetricsProto.WifiLog.WIFI_UNKNOWN:
return "WIFI_UNKNOWN";
case WifiMetricsProto.WifiLog.WIFI_DISABLED:
return "WIFI_DISABLED";
case WifiMetricsProto.WifiLog.WIFI_DISCONNECTED:
return "WIFI_DISCONNECTED";
case WifiMetricsProto.WifiLog.WIFI_ASSOCIATED:
return "WIFI_ASSOCIATED";
default:
return "default";
}
}
/**
* Increments the count of scans initiated by each wifi state, accounts for screenOn/Off
*
* @param state State of the system when scan was initiated, see WifiMetricsProto.WifiLog.WIFI_X
* @param screenOn Is the screen on
*/
public void incrementWifiSystemScanStateCount(int state, boolean screenOn) {
synchronized (mLock) {
if (DBG) {
Log.v(TAG, "incrementWifiSystemScanStateCount " + wifiSystemStateToString(state)
+ " " + screenOn);
}
int index = (state * 2) + (screenOn ? SCREEN_ON : SCREEN_OFF);
int entry = mWifiSystemStateEntries.get(index);
entry++;
mWifiSystemStateEntries.put(index, entry);
}
}
/**
* Get the count of this system State Entry
*/
public int getSystemStateCount(int state, boolean screenOn) {
synchronized (mLock) {
int index = state * 2 + (screenOn ? SCREEN_ON : SCREEN_OFF);
return mWifiSystemStateEntries.get(index);
}
}
/**
* Increment number of times the Watchdog of Last Resort triggered, resetting the wifi stack
*/
public void incrementNumLastResortWatchdogTriggers() {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogTriggers++;
}
}
/**
* @param count number of networks over bad association threshold when watchdog triggered
*/
public void addCountToNumLastResortWatchdogBadAssociationNetworksTotal(int count) {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal += count;
}
}
/**
* @param count number of networks over bad authentication threshold when watchdog triggered
*/
public void addCountToNumLastResortWatchdogBadAuthenticationNetworksTotal(int count) {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal += count;
}
}
/**
* @param count number of networks over bad dhcp threshold when watchdog triggered
*/
public void addCountToNumLastResortWatchdogBadDhcpNetworksTotal(int count) {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal += count;
}
}
/**
* @param count number of networks over bad other threshold when watchdog triggered
*/
public void addCountToNumLastResortWatchdogBadOtherNetworksTotal(int count) {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal += count;
}
}
/**
* @param count number of networks seen when watchdog triggered
*/
public void addCountToNumLastResortWatchdogAvailableNetworksTotal(int count) {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal += count;
}
}
/**
* Increment count of triggers with atleast one bad association network
*/
public void incrementNumLastResortWatchdogTriggersWithBadAssociation() {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation++;
}
}
/**
* Increment count of triggers with atleast one bad authentication network
*/
public void incrementNumLastResortWatchdogTriggersWithBadAuthentication() {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication++;
}
}
/**
* Increment count of triggers with atleast one bad dhcp network
*/
public void incrementNumLastResortWatchdogTriggersWithBadDhcp() {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp++;
}
}
/**
* Increment count of triggers with atleast one bad other network
*/
public void incrementNumLastResortWatchdogTriggersWithBadOther() {
synchronized (mLock) {
mWifiLogProto.numLastResortWatchdogTriggersWithBadOther++;
}
}
/**
* Increment number of times connectivity watchdog confirmed pno is working
*/
public void incrementNumConnectivityWatchdogPnoGood() {
synchronized (mLock) {
mWifiLogProto.numConnectivityWatchdogPnoGood++;
}
}
/**
* Increment number of times connectivity watchdog found pno not working
*/
public void incrementNumConnectivityWatchdogPnoBad() {
synchronized (mLock) {
mWifiLogProto.numConnectivityWatchdogPnoBad++;
}
}
/**
* Increment number of times connectivity watchdog confirmed background scan is working
*/
public void incrementNumConnectivityWatchdogBackgroundGood() {
synchronized (mLock) {
mWifiLogProto.numConnectivityWatchdogBackgroundGood++;
}
}
/**
* Increment number of times connectivity watchdog found background scan not working
*/
public void incrementNumConnectivityWatchdogBackgroundBad() {
synchronized (mLock) {
mWifiLogProto.numConnectivityWatchdogBackgroundBad++;
}
}
public static final String PROTO_DUMP_ARG = "wifiMetricsProto";
/**
* Dump all WifiMetrics. Collects some metrics from ConfigStore, Settings and WifiManager
* at this time
*
* @param fd unused
* @param pw PrintWriter for writing dump to
* @param args unused
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
synchronized (mLock) {
pw.println("WifiMetrics:");
if (args.length > 0 && PROTO_DUMP_ARG.equals(args[0])) {
//Dump serialized WifiLog proto
consolidateProto(true);
for (ConnectionEvent event : mConnectionEventList) {
if (mCurrentConnectionEvent != event) {
//indicate that automatic bug report has been taken for all valid
//connection events
event.mConnectionEvent.automaticBugReportTaken = true;
}
}
byte[] wifiMetricsProto = WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
String metricsProtoDump = Base64.encodeToString(wifiMetricsProto, Base64.DEFAULT);
pw.println(metricsProtoDump);
pw.println("EndWifiMetrics");
clear();
} else {
pw.println("mConnectionEvents:");
for (ConnectionEvent event : mConnectionEventList) {
String eventLine = event.toString();
if (event == mCurrentConnectionEvent) {
eventLine += "CURRENTLY OPEN EVENT";
}
pw.println(eventLine);
}
pw.println("mWifiLogProto.numSavedNetworks=" + mWifiLogProto.numSavedNetworks);
pw.println("mWifiLogProto.numOpenNetworks=" + mWifiLogProto.numOpenNetworks);
pw.println("mWifiLogProto.numPersonalNetworks="
+ mWifiLogProto.numPersonalNetworks);
pw.println("mWifiLogProto.numEnterpriseNetworks="
+ mWifiLogProto.numEnterpriseNetworks);
pw.println("mWifiLogProto.isLocationEnabled=" + mWifiLogProto.isLocationEnabled);
pw.println("mWifiLogProto.isScanningAlwaysEnabled="
+ mWifiLogProto.isScanningAlwaysEnabled);
pw.println("mWifiLogProto.numNetworksAddedByUser="
+ mWifiLogProto.numNetworksAddedByUser);
pw.println("mWifiLogProto.numNetworksAddedByApps="
+ mWifiLogProto.numNetworksAddedByApps);
pw.println("mWifiLogProto.numNonEmptyScanResults="
+ mWifiLogProto.numNonEmptyScanResults);
pw.println("mWifiLogProto.numEmptyScanResults="
+ mWifiLogProto.numEmptyScanResults);
pw.println("mWifiLogProto.numOneshotScans="
+ mWifiLogProto.numOneshotScans);
pw.println("mWifiLogProto.numBackgroundScans="
+ mWifiLogProto.numBackgroundScans);
pw.println("mScanReturnEntries:");
pw.println(" SCAN_UNKNOWN: " + getScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_UNKNOWN));
pw.println(" SCAN_SUCCESS: " + getScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_SUCCESS));
pw.println(" SCAN_FAILURE_INTERRUPTED: " + getScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_FAILURE_INTERRUPTED));
pw.println(" SCAN_FAILURE_INVALID_CONFIGURATION: " + getScanReturnEntry(
WifiMetricsProto.WifiLog.SCAN_FAILURE_INVALID_CONFIGURATION));
pw.println(" FAILURE_WIFI_DISABLED: " + getScanReturnEntry(
WifiMetricsProto.WifiLog.FAILURE_WIFI_DISABLED));
pw.println("mSystemStateEntries: <state><screenOn> : <scansInitiated>");
pw.println(" WIFI_UNKNOWN ON: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, true));
pw.println(" WIFI_DISABLED ON: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, true));
pw.println(" WIFI_DISCONNECTED ON: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, true));
pw.println(" WIFI_ASSOCIATED ON: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, true));
pw.println(" WIFI_UNKNOWN OFF: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_UNKNOWN, false));
pw.println(" WIFI_DISABLED OFF: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISABLED, false));
pw.println(" WIFI_DISCONNECTED OFF: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_DISCONNECTED, false));
pw.println(" WIFI_ASSOCIATED OFF: "
+ getSystemStateCount(WifiMetricsProto.WifiLog.WIFI_ASSOCIATED, false));
pw.println("mWifiLogProto.numConnectivityWatchdogPnoGood="
+ mWifiLogProto.numConnectivityWatchdogPnoGood);
pw.println("mWifiLogProto.numConnectivityWatchdogPnoBad="
+ mWifiLogProto.numConnectivityWatchdogPnoBad);
pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundGood="
+ mWifiLogProto.numConnectivityWatchdogBackgroundGood);
pw.println("mWifiLogProto.numConnectivityWatchdogBackgroundBad="
+ mWifiLogProto.numConnectivityWatchdogBackgroundBad);
pw.println("mWifiLogProto.numLastResortWatchdogTriggers="
+ mWifiLogProto.numLastResortWatchdogTriggers);
pw.println("mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal="
+ mWifiLogProto.numLastResortWatchdogBadAssociationNetworksTotal);
pw.println("mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal="
+ mWifiLogProto.numLastResortWatchdogBadAuthenticationNetworksTotal);
pw.println("mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal="
+ mWifiLogProto.numLastResortWatchdogBadDhcpNetworksTotal);
pw.println("mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal="
+ mWifiLogProto.numLastResortWatchdogBadOtherNetworksTotal);
pw.println("mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal="
+ mWifiLogProto.numLastResortWatchdogAvailableNetworksTotal);
pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation="
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadAssociation);
pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication="
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadAuthentication);
pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp="
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadDhcp);
pw.println("mWifiLogProto.numLastResortWatchdogTriggersWithBadOther="
+ mWifiLogProto.numLastResortWatchdogTriggersWithBadOther);
}
}
}
/**
* append the separate ConnectionEvent, SystemStateEntry and ScanReturnCode collections to their
* respective lists within mWifiLogProto
*
* @param incremental Only include ConnectionEvents created since last automatic bug report
*/
private void consolidateProto(boolean incremental) {
List<WifiMetricsProto.ConnectionEvent> events = new ArrayList<>();
synchronized (mLock) {
for (ConnectionEvent event : mConnectionEventList) {
// If this is not incremental, dump full ConnectionEvent list
// Else Dump all un-dumped events except for the current one
if (!incremental || ((mCurrentConnectionEvent != event)
&& !event.mConnectionEvent.automaticBugReportTaken)) {
//Get all ConnectionEvents that haven not been dumped as a proto, also exclude
//the current active un-ended connection event
events.add(event.mConnectionEvent);
if (incremental) {
event.mConnectionEvent.automaticBugReportTaken = true;
}
}
}
if (events.size() > 0) {
mWifiLogProto.connectionEvent = events.toArray(mWifiLogProto.connectionEvent);
}
//Convert the SparseIntArray of scanReturnEntry integers into ScanReturnEntry proto list
mWifiLogProto.scanReturnEntries =
new WifiMetricsProto.WifiLog.ScanReturnEntry[mScanReturnEntries.size()];
for (int i = 0; i < mScanReturnEntries.size(); i++) {
mWifiLogProto.scanReturnEntries[i] = new WifiMetricsProto.WifiLog.ScanReturnEntry();
mWifiLogProto.scanReturnEntries[i].scanReturnCode = mScanReturnEntries.keyAt(i);
mWifiLogProto.scanReturnEntries[i].scanResultsCount = mScanReturnEntries.valueAt(i);
}
// Convert the SparseIntArray of systemStateEntry into WifiSystemStateEntry proto list
// This one is slightly more complex, as the Sparse are indexed with:
// key: wifiState * 2 + isScreenOn, value: wifiStateCount
mWifiLogProto.wifiSystemStateEntries =
new WifiMetricsProto.WifiLog
.WifiSystemStateEntry[mWifiSystemStateEntries.size()];
for (int i = 0; i < mWifiSystemStateEntries.size(); i++) {
mWifiLogProto.wifiSystemStateEntries[i] =
new WifiMetricsProto.WifiLog.WifiSystemStateEntry();
mWifiLogProto.wifiSystemStateEntries[i].wifiState =
mWifiSystemStateEntries.keyAt(i) / 2;
mWifiLogProto.wifiSystemStateEntries[i].wifiStateCount =
mWifiSystemStateEntries.valueAt(i);
mWifiLogProto.wifiSystemStateEntries[i].isScreenOn =
(mWifiSystemStateEntries.keyAt(i) % 2) > 0;
}
}
}
/**
* Serializes all of WifiMetrics to WifiLog proto, and returns the byte array.
* Does not count as taking an automatic bug report
*
* @return byte array of the deserialized & consolidated Proto
*/
public byte[] toByteArray() {
synchronized (mLock) {
consolidateProto(false);
return WifiMetricsProto.WifiLog.toByteArray(mWifiLogProto);
}
}
/**
* Clear all WifiMetrics, except for currentConnectionEvent.
*/
private void clear() {
synchronized (mLock) {
mConnectionEventList.clear();
if (mCurrentConnectionEvent != null) {
mConnectionEventList.add(mCurrentConnectionEvent);
}
mScanReturnEntries.clear();
mWifiSystemStateEntries.clear();
mWifiLogProto.clear();
}
}
/**
* Set screen state (On/Off)
*/
public void setScreenState(boolean screenOn) {
synchronized (mLock) {
mScreenOn = screenOn;
}
}
/**
* Set wifi state (WIFI_UNKNOWN, WIFI_DISABLED, WIFI_DISCONNECTED, WIFI_ASSOCIATED)
*/
public void setWifiState(int wifiState) {
synchronized (mLock) {
mWifiState = wifiState;
}
}
}