/* * Copyright 2014 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.util.SparseArray; import java.util.Arrays; /** * A class representing link layer statistics collected over a Wifi Interface. */ /** * {@hide} */ public class WifiLinkLayerStats { public static final String V1_0 = "V1_0"; public static final String V1_3 = "V1_3"; public static final String V1_5 = "V1_5"; /** The version of hal StaLinkLayerStats **/ public String version; /** Number of beacons received from our own AP */ public int beacon_rx; /** RSSI of management frames */ public int rssi_mgmt; /* Packet counters and contention time stats */ /** WME Best Effort Access Category received mpdu */ public long rxmpdu_be; /** WME Best Effort Access Category transmitted mpdu */ public long txmpdu_be; /** WME Best Effort Access Category lost mpdu */ public long lostmpdu_be; /** WME Best Effort Access Category number of transmission retries */ public long retries_be; /** WME Best Effort Access Category data packet min contention time in microseconds */ public long contentionTimeMinBeInUsec; /** WME Best Effort Access Category data packet max contention time in microseconds */ public long contentionTimeMaxBeInUsec; /** WME Best Effort Access Category data packet average contention time in microseconds */ public long contentionTimeAvgBeInUsec; /** * WME Best Effort Access Category number of data packets used for deriving the min, the max, * and the average contention time */ public long contentionNumSamplesBe; /** WME Background Access Category received mpdu */ public long rxmpdu_bk; /** WME Background Access Category transmitted mpdu */ public long txmpdu_bk; /** WME Background Access Category lost mpdu */ public long lostmpdu_bk; /** WME Background Access Category number of transmission retries */ public long retries_bk; /** WME Background Access Category data packet min contention time in microseconds */ public long contentionTimeMinBkInUsec; /** WME Background Access Category data packet max contention time in microseconds */ public long contentionTimeMaxBkInUsec; /** WME Background Access Category data packet average contention time in microseconds */ public long contentionTimeAvgBkInUsec; /** * WME Background Access Category number of data packets used for deriving the min, the max, * and the average contention time */ public long contentionNumSamplesBk; /** WME Video Access Category received mpdu */ public long rxmpdu_vi; /** WME Video Access Category transmitted mpdu */ public long txmpdu_vi; /** WME Video Access Category lost mpdu */ public long lostmpdu_vi; /** WME Video Access Category number of transmission retries */ public long retries_vi; /** WME Video Access Category data packet min contention time in microseconds */ public long contentionTimeMinViInUsec; /** WME Video Access Category data packet max contention time in microseconds */ public long contentionTimeMaxViInUsec; /** WME Video Access Category data packet average contention time in microseconds */ public long contentionTimeAvgViInUsec; /** * WME Video Access Category number of data packets used for deriving the min, the max, and * the average contention time */ public long contentionNumSamplesVi; /** WME Voice Access Category received mpdu */ public long rxmpdu_vo; /** WME Voice Access Category transmitted mpdu */ public long txmpdu_vo; /** WME Voice Access Category lost mpdu */ public long lostmpdu_vo; /** WME Voice Access Category number of transmission retries */ public long retries_vo; /** WME Voice Access Category data packet min contention time in microseconds */ public long contentionTimeMinVoInUsec; /** WME Voice Access Category data packet max contention time in microseconds */ public long contentionTimeMaxVoInUsec; /** WME Voice Access Category data packet average contention time in microseconds */ public long contentionTimeAvgVoInUsec; /** * WME Voice Access Category number of data packets used for deriving the min, the max, and * the average contention time */ public long contentionNumSamplesVo; /** * Cumulative milliseconds when radio is awake */ public int on_time; /** * Cumulative milliseconds of active transmission */ public int tx_time; /** * Cumulative milliseconds per radio transmit power level of active transmission */ public int[] tx_time_per_level; /** * Cumulative milliseconds of active receive */ public int rx_time; /** * Cumulative milliseconds when radio is awake due to scan */ public int on_time_scan; /** * Cumulative milliseconds when radio is awake due to nan scan */ public int on_time_nan_scan; /** * Cumulative milliseconds when radio is awake due to background scan */ public int on_time_background_scan; /** * Cumulative milliseconds when radio is awake due to roam scan */ public int on_time_roam_scan; /** * Cumulative milliseconds when radio is awake due to pno scan */ public int on_time_pno_scan; /** * Cumulative milliseconds when radio is awake due to hotspot 2.0 scan amd GAS exchange */ public int on_time_hs20_scan; /** * channel stats */ public static class ChannelStats { /** * Channel frequency in MHz; */ public int frequency; /** * Cumulative milliseconds radio is awake on this channel */ public int radioOnTimeMs; /** * Cumulative milliseconds CCA is held busy on this channel */ public int ccaBusyTimeMs; } /** * Channel stats list */ public final SparseArray channelStatsMap = new SparseArray<>(); /** * numRadios - Number of radios used for coalescing above radio stats. */ public int numRadios; /** * TimeStamp - absolute milliseconds from boot when these stats were sampled. */ public long timeStampInMs; /** * Duty cycle of the iface. * if this iface is being served using time slicing on a radio with one or more ifaces * (i.e MCC), then the duty cycle assigned to this iface in %. * If not using time slicing (i.e SCC or DBS), set to 100. */ public short timeSliceDutyCycleInPercent = -1; /** * Per rate information and statistics. */ public static class RateStat { /** * Preamble information. 0: OFDM, 1:CCK, 2:HT 3:VHT 4:HE 5..7 reserved. */ public int preamble; /** * Number of spatial streams. 0:1x1, 1:2x2, 3:3x3, 4:4x4. */ public int nss; /** * Bandwidth information. 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz. */ public int bw; /** * MCS index. OFDM/CCK rate code would be as per IEEE std in the units of 0.5Mbps. * HT/VHT/HE: it would be MCS index. */ public int rateMcsIdx; /** * Bitrate in units of 100 Kbps. */ public int bitRateInKbps; /** * Number of successfully transmitted data packets (ACK received). */ public int txMpdu; /** * Number of received data packets. */ public int rxMpdu; /** * Number of data packet losses (no ACK). */ public int mpduLost; /** * Number of data packet retries. */ public int retries; } /** * Per peer statistics. */ public static class PeerInfo { /** * Station count. */ public short staCount; /** * Channel utilization. */ public short chanUtil; /** * Per rate statistics. */ public RateStat[] rateStats; } /** * Peer statistics. */ public PeerInfo[] peerInfo; /** * Radio stats */ public static class RadioStat { /** * Radio identifier */ public int radio_id; /** * Cumulative milliseconds when radio is awake from the last radio chip reset */ public int on_time; /** * Cumulative milliseconds of active transmission from the last radio chip reset */ public int tx_time; /** * Cumulative milliseconds of active receive from the last radio chip reset */ public int rx_time; /** * Cumulative milliseconds when radio is awake due to scan from the last radio chip reset */ public int on_time_scan; /** * Cumulative milliseconds when radio is awake due to nan scan from the last radio chip * reset */ public int on_time_nan_scan; /** * Cumulative milliseconds when radio is awake due to background scan from the last radio * chip reset */ public int on_time_background_scan; /** * Cumulative milliseconds when radio is awake due to roam scan from the last radio chip * reset */ public int on_time_roam_scan; /** * Cumulative milliseconds when radio is awake due to pno scan from the last radio chip * reset */ public int on_time_pno_scan; /** * Cumulative milliseconds when radio is awake due to hotspot 2.0 scan amd GAS exchange * from the last radio chip reset */ public int on_time_hs20_scan; /** * Channel stats list */ public final SparseArray channelStatsMap = new SparseArray<>(); } /** * Radio stats of all the radios. */ public RadioStat[] radioStats; @Override public String toString() { StringBuilder sbuf = new StringBuilder(); sbuf.append(" WifiLinkLayerStats: ").append('\n'); sbuf.append(" version of StaLinkLayerStats: ").append(version).append('\n'); sbuf.append(" my bss beacon rx: ").append(Integer.toString(this.beacon_rx)).append('\n'); sbuf.append(" RSSI mgmt: ").append(Integer.toString(this.rssi_mgmt)).append('\n'); sbuf.append(" BE : ").append(" rx=").append(Long.toString(this.rxmpdu_be)) .append(" tx=").append(Long.toString(this.txmpdu_be)) .append(" lost=").append(Long.toString(this.lostmpdu_be)) .append(" retries=").append(Long.toString(this.retries_be)).append('\n') .append(" contention_time_min") .append(Long.toString(this.contentionTimeMinBeInUsec)) .append(" contention_time_max") .append(Long.toString(this.contentionTimeMaxBeInUsec)).append('\n') .append(" contention_time_avg") .append(Long.toString(this.contentionTimeAvgBeInUsec)) .append(" contention_num_samples") .append(Long.toString(this.contentionNumSamplesBe)).append('\n'); sbuf.append(" BK : ").append(" rx=").append(Long.toString(this.rxmpdu_bk)) .append(" tx=").append(Long.toString(this.txmpdu_bk)) .append(" lost=").append(Long.toString(this.lostmpdu_bk)) .append(" retries=").append(Long.toString(this.retries_bk)).append('\n') .append(" contention_time_min") .append(Long.toString(this.contentionTimeMinBkInUsec)) .append(" contention_time_max") .append(Long.toString(this.contentionTimeMaxBkInUsec)).append('\n') .append(" contention_time_avg") .append(Long.toString(this.contentionTimeAvgBkInUsec)) .append(" contention_num_samples") .append(Long.toString(this.contentionNumSamplesBk)).append('\n'); sbuf.append(" VI : ").append(" rx=").append(Long.toString(this.rxmpdu_vi)) .append(" tx=").append(Long.toString(this.txmpdu_vi)) .append(" lost=").append(Long.toString(this.lostmpdu_vi)) .append(" retries=").append(Long.toString(this.retries_vi)).append('\n') .append(" contention_time_min") .append(Long.toString(this.contentionTimeMinViInUsec)) .append(" contention_time_max") .append(Long.toString(this.contentionTimeMaxViInUsec)).append('\n') .append(" contention_time_avg") .append(Long.toString(this.contentionTimeAvgViInUsec)) .append(" contention_num_samples") .append(Long.toString(this.contentionNumSamplesVi)).append('\n'); sbuf.append(" VO : ").append(" rx=").append(Long.toString(this.rxmpdu_vo)) .append(" tx=").append(Long.toString(this.txmpdu_vo)) .append(" lost=").append(Long.toString(this.lostmpdu_vo)) .append(" retries=").append(Long.toString(this.retries_vo)).append('\n') .append(" contention_time_min") .append(Long.toString(this.contentionTimeMinVoInUsec)) .append(" contention_time_max") .append(Long.toString(this.contentionTimeMaxVoInUsec)).append('\n') .append(" contention_time_avg") .append(Long.toString(this.contentionTimeAvgVoInUsec)) .append(" contention_num_samples") .append(Long.toString(this.contentionNumSamplesVo)).append('\n'); sbuf.append(" numRadios=" + numRadios) .append(" on_time= ").append(Integer.toString(this.on_time)) .append(" tx_time=").append(Integer.toString(this.tx_time)) .append(" rx_time=").append(Integer.toString(this.rx_time)) .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n') .append(" nan_scan_time=") .append(Integer.toString(this.on_time_nan_scan)).append('\n') .append(" g_scan_time=") .append(Integer.toString(this.on_time_background_scan)).append('\n') .append(" roam_scan_time=") .append(Integer.toString(this.on_time_roam_scan)).append('\n') .append(" pno_scan_time=") .append(Integer.toString(this.on_time_pno_scan)).append('\n') .append(" hs2.0_scan_time=") .append(Integer.toString(this.on_time_hs20_scan)).append('\n') .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level)).append('\n'); int numChanStats = this.channelStatsMap.size(); sbuf.append(" Number of channel stats=").append(numChanStats).append('\n'); for (int i = 0; i < numChanStats; ++i) { ChannelStats channelStatsEntry = this.channelStatsMap.valueAt(i); sbuf.append(" Frequency=").append(channelStatsEntry.frequency) .append(" radioOnTimeMs=").append(channelStatsEntry.radioOnTimeMs) .append(" ccaBusyTimeMs=").append(channelStatsEntry.ccaBusyTimeMs).append('\n'); } int numRadios = this.radioStats == null ? 0 : this.radioStats.length; sbuf.append(" Individual radio stats: numRadios=").append(numRadios).append('\n'); for (int i = 0; i < numRadios; i++) { RadioStat radio = this.radioStats[i]; sbuf.append(" radio_id=" + radio.radio_id) .append(" on_time=").append(Integer.toString(radio.on_time)) .append(" tx_time=").append(Integer.toString(radio.tx_time)) .append(" rx_time=").append(Integer.toString(radio.rx_time)) .append(" scan_time=").append(Integer.toString(radio.on_time_scan)).append('\n') .append(" nan_scan_time=") .append(Integer.toString(radio.on_time_nan_scan)).append('\n') .append(" g_scan_time=") .append(Integer.toString(radio.on_time_background_scan)).append('\n') .append(" roam_scan_time=") .append(Integer.toString(radio.on_time_roam_scan)).append('\n') .append(" pno_scan_time=") .append(Integer.toString(radio.on_time_pno_scan)).append('\n') .append(" hs2.0_scan_time=") .append(Integer.toString(radio.on_time_hs20_scan)).append('\n'); int numRadioChanStats = radio.channelStatsMap.size(); sbuf.append(" Number of channel stats=").append(numRadioChanStats).append('\n'); for (int j = 0; j < numRadioChanStats; ++j) { ChannelStats channelStatsEntry = radio.channelStatsMap.valueAt(j); sbuf.append(" Frequency=").append(channelStatsEntry.frequency) .append(" radioOnTimeMs=").append(channelStatsEntry.radioOnTimeMs) .append(" ccaBusyTimeMs=").append(channelStatsEntry.ccaBusyTimeMs) .append('\n'); } } sbuf.append(" ts=" + timeStampInMs); int numPeers = this.peerInfo == null ? 0 : this.peerInfo.length; sbuf.append(" Number of peers=").append(numPeers).append('\n'); for (int i = 0; i < numPeers; i++) { PeerInfo peer = this.peerInfo[i]; sbuf.append(" staCount=").append(peer.staCount) .append(" chanUtil=").append(peer.chanUtil).append('\n'); int numRateStats = peer.rateStats == null ? 0 : peer.rateStats.length; for (int j = 0; j < numRateStats; j++) { RateStat rateStat = peer.rateStats[j]; sbuf.append(" preamble=").append(rateStat.preamble) .append(" nss=").append(rateStat.nss) .append(" bw=").append(rateStat.bw) .append(" rateMcsIdx=").append(rateStat.rateMcsIdx) .append(" bitRateInKbps=").append(rateStat.bitRateInKbps).append('\n') .append(" txMpdu=").append(rateStat.txMpdu) .append(" rxMpdu=").append(rateStat.rxMpdu) .append(" mpduLost=").append(rateStat.mpduLost) .append(" retries=").append(rateStat.retries).append('\n'); } } return sbuf.toString(); } }