Discard unneeded IEs (#78)

This commit is contained in:
RockyMandayam2
2022-09-29 11:28:48 -07:00
committed by GitHub
parent 01a070c9b7
commit df21d07ec9
13 changed files with 563 additions and 19 deletions

View File

@@ -22,8 +22,8 @@ import com.facebook.openwifirrm.aggregators.Aggregator;
import com.facebook.openwifirrm.aggregators.MeanAggregator;
import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.operationelement.HTOperationElement;
import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
import com.facebook.openwifirrm.ucentral.informationelement.HTOperationElement;
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperationElement;
/**
* Modeler utilities.

View File

@@ -25,9 +25,9 @@ import com.facebook.openwifirrm.modules.Modeler.DataModel;
import com.facebook.openwifirrm.ucentral.UCentralConstants;
import com.facebook.openwifirrm.ucentral.UCentralUtils;
import com.facebook.openwifirrm.ucentral.WifiScanEntry;
import com.facebook.openwifirrm.ucentral.informationelement.HTOperationElement;
import com.facebook.openwifirrm.ucentral.informationelement.VHTOperationElement;
import com.facebook.openwifirrm.ucentral.models.State;
import com.facebook.openwifirrm.ucentral.operationelement.HTOperationElement;
import com.facebook.openwifirrm.ucentral.operationelement.VHTOperationElement;
/**
* Channel optimizer base class.

View File

@@ -24,6 +24,10 @@ import org.slf4j.LoggerFactory;
import com.facebook.openwifirrm.RRMConfig;
import com.facebook.openwifirrm.Utils;
import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
import com.facebook.openwifirrm.ucentral.informationelement.Country;
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
import com.facebook.openwifirrm.ucentral.models.State;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
@@ -37,6 +41,9 @@ public class UCentralUtils {
private static final Logger logger =
LoggerFactory.getLogger(UCentralUtils.class);
/** Information Element (IE) content field key */
private static final String IE_CONTENT_FIELD_KEY = "content";
/** The Gson instance. */
private static final Gson gson = new Gson();
@@ -79,15 +86,61 @@ public class UCentralUtils {
for (JsonElement e : scanInfo) {
WifiScanEntry entry = gson.fromJson(e, WifiScanEntry.class);
entry.unixTimeMs = timestampMs;
extractIEs(e, entry);
entries.add(entry);
}
} catch (Exception e) {
logger.debug("Exception when parsing wifiscan entries", e);
return null;
}
return entries;
}
/**
* Extract desired information elements (IEs) from the wifiscan entry.
* Modifies {@code entry} argument. Skips invalid IEs (IEs with missing
* fields).
*/
private static void extractIEs(
JsonElement entryJsonElement,
WifiScanEntry entry
) {
JsonElement iesJsonElement =
entryJsonElement.getAsJsonObject().get("ies");
if (iesJsonElement == null) {
logger.debug("Wifiscan entry does not contain 'ies' field.");
return;
}
JsonArray ies = iesJsonElement.getAsJsonArray();
for (JsonElement ie : ies) {
JsonElement typeElement = ie.getAsJsonObject().get("type");
if (typeElement == null) {
continue;
}
JsonObject contents =
ie.getAsJsonObject().get(IE_CONTENT_FIELD_KEY).getAsJsonObject();
try {
switch (typeElement.getAsInt()) {
case Country.TYPE:
entry.country = Country.parse(contents);
break;
case QbssLoad.TYPE:
entry.qbssLoad = QbssLoad.parse(contents);
break;
case LocalPowerConstraint.TYPE:
entry.localPowerConstraint =
LocalPowerConstraint.parse(contents);
break;
case TxPwrInfo.TYPE:
entry.txPwrInfo = TxPwrInfo.parse(contents);
break;
}
} catch (NullPointerException e) {
logger.debug("Skipping invalid IE.", e);
}
}
}
/**
* Set all radios config of an AP to a given value.
*

View File

@@ -10,6 +10,10 @@ package com.facebook.openwifirrm.ucentral;
import java.util.Objects;
import com.facebook.openwifirrm.ucentral.informationelement.Country;
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
import com.facebook.openwifirrm.ucentral.models.WifiScanEntryResult;
/**
@@ -22,6 +26,10 @@ public class WifiScanEntry extends WifiScanEntryResult {
* time reference.
*/
public long unixTimeMs;
public Country country;
public QbssLoad qbssLoad;
public LocalPowerConstraint localPowerConstraint;
public TxPwrInfo txPwrInfo;
/** Default Constructor. */
public WifiScanEntry() {}
@@ -30,13 +38,23 @@ public class WifiScanEntry extends WifiScanEntryResult {
public WifiScanEntry(WifiScanEntry o) {
super(o);
this.unixTimeMs = o.unixTimeMs;
this.country = o.country;
this.qbssLoad = o.qbssLoad;
this.localPowerConstraint = o.localPowerConstraint;
this.txPwrInfo = o.txPwrInfo;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + Objects.hash(unixTimeMs);
result = prime * result + Objects.hash(
country,
localPowerConstraint,
qbssLoad,
txPwrInfo,
unixTimeMs
);
return result;
}
@@ -52,7 +70,12 @@ public class WifiScanEntry extends WifiScanEntryResult {
return false;
}
WifiScanEntry other = (WifiScanEntry) obj;
return unixTimeMs == other.unixTimeMs;
return Objects.equals(country, other.country) && Objects.equals(
localPowerConstraint,
other.localPowerConstraint
) && Objects.equals(qbssLoad, other.qbssLoad) && Objects
.equals(txPwrInfo, other.txPwrInfo) &&
unixTimeMs == other.unixTimeMs;
}
@Override
@@ -64,4 +87,5 @@ public class WifiScanEntry extends WifiScanEntryResult {
unixTimeMs
);
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* This information element (IE) appears in wifiscan entries.
* Refer to the 802.11 specification for more details. Language in
* javadocs is taken from the specification.
*/
public class Country {
private static final Logger logger = LoggerFactory.getLogger(Country.class);
/** Defined in 802.11 */
public static final int TYPE = 7;
public static class CountryInfo {
/**
* The lowest channel number in the CountryInfo.
*/
public final int firstChannelNumber;
/**
* The maximum power, in dBm, allowed to be transmitted.
*/
public final int maximumTransmitPowerLevel;
/**
* Number of channels this CountryInfo applies to. E.g., if First
* Channel Number is 2 and Number of Channels is 4, this CountryInfo
* describes channels 2, 3, 4, and 5.
*/
public final int numberOfChannels;
/** Constructor. */
public CountryInfo(
int firstChannelNumber,
int maximumTransmitPowerLevel,
int numberOfChannels
) {
this.firstChannelNumber = firstChannelNumber;
this.maximumTransmitPowerLevel = maximumTransmitPowerLevel;
this.numberOfChannels = numberOfChannels;
}
/** Parse CountryInfo from the appropriate Json object. */
public static CountryInfo parse(JsonObject contents) {
final int firstChannelNumber =
contents.get("First Channel Number").getAsInt();
final int maximumTransmitPowerLevel = contents
.get("Maximum Transmit Power Level (in dBm)")
.getAsInt();
final int numberOfChannels =
contents.get("Number of Channels").getAsInt();
return new CountryInfo(
firstChannelNumber,
maximumTransmitPowerLevel,
numberOfChannels
);
}
@Override
public int hashCode() {
return Objects.hash(
firstChannelNumber,
maximumTransmitPowerLevel,
numberOfChannels
);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
CountryInfo other = (CountryInfo) obj;
return firstChannelNumber == other.firstChannelNumber &&
maximumTransmitPowerLevel == other.maximumTransmitPowerLevel &&
numberOfChannels == other.numberOfChannels;
}
@Override
public String toString() {
return "CountryInfo [firstChannelNumber=" + firstChannelNumber +
", maximumTransmitPowerLevel=" + maximumTransmitPowerLevel +
", numberOfChannels=" + numberOfChannels + "]";
}
}
/**
* Each constraint is a CountryInfo describing tx power constraints on
* one or more channels, for the current country.
*/
public final List<CountryInfo> constraints;
/** Constructor */
public Country(List<CountryInfo> countryInfos) {
this.constraints = Collections.unmodifiableList(countryInfos);
}
/** Parse Country IE from the appropriate Json object. */
public static Country parse(JsonObject contents) {
List<CountryInfo> constraints = new ArrayList<>();
JsonElement constraintsObject = contents.get("constraints");
if (constraintsObject != null) {
for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) {
try {
constraints
.add(CountryInfo.parse(jsonElement.getAsJsonObject()));
} catch (NullPointerException e) {
logger.debug(
"Skipping invalid constraint encountered in Country IE.",
e
);
}
}
}
return new Country(constraints);
}
@Override
public int hashCode() {
return Objects.hash(constraints);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
Country other = (Country) obj;
return Objects.equals(constraints, other.constraints);
}
@Override
public String toString() {
return "Country [constraints=" + constraints + "]";
}
}

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.operationelement;
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Arrays;
import java.util.Objects;

View File

@@ -0,0 +1,72 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects;
import com.google.gson.JsonObject;
/**
* This information element (IE) appears in wifiscan entries. It is called
* "Local Power Constraint" in these entries, and just "Power Constraint" in
* the 802.11 specification. Refer to the specification for more details.
* Language in javadocs is taken from the specification.
*/
public class LocalPowerConstraint {
/** Defined in 802.11 */
public static final int TYPE = 32;
/**
* Units are dB.
* <p>
* The local maximum transmit power for a channel is defined as the maximum
* transmit power level specified for the channel in the Country IE minus
* this variable for the given channel.
*/
public final int localPowerConstraint;
/** Constructor */
public LocalPowerConstraint(int localPowerConstraint) {
this.localPowerConstraint = localPowerConstraint;
}
/** Parse LocalPowerConstraint IE from appropriate Json object. */
public static LocalPowerConstraint parse(JsonObject contents) {
final int localPowerConstraint =
contents.get("Local Power Constraint").getAsInt();
return new LocalPowerConstraint(localPowerConstraint);
}
@Override
public int hashCode() {
return Objects.hash(localPowerConstraint);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
LocalPowerConstraint other = (LocalPowerConstraint) obj;
return localPowerConstraint == other.localPowerConstraint;
}
@Override
public String toString() {
return "LocalPowerConstraint [localPowerConstraint=" +
localPowerConstraint + "]";
}
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects;
import com.google.gson.JsonObject;
/**
* This information element (IE) appears in wifiscan entries. It is called
* "QBSS Load" in these entries, and just "BSS Load" in the 802.11
* specification. Refer to the specification for more details. Language in
* javadocs is taken from the specification.
*/
public class QbssLoad {
/** Defined in 802.11 */
public static final int TYPE = 11;
/**
* The total number of STAs currently associated with the BSS.
*/
public final int stationCount;
/**
* The Channel Utilization field is defined as the percentage of time,
* linearly scaled with 255 representing 100%, that the AP sensed the
* medium was busy, as indicated by either the physical or virtual carrier
* sense (CS) mechanism. When more than one channel is in use for the BSS,
* the Channel Utilization field value is calculated only for the primary
* channel. This percentage is computed using the following formula:
* <p>
* floor(255 * channelBusyTime /
* (dot11ChannelUtilizationBeaconIntervals * dot11BeaconPeriod * 1024)
* )
*/
public final int channelUtilization;
/**
* The Available Admission Capacity field contains an unsigned integer that
* specifies the remaining amount of medium time available via explicit
* admission control, in units of 32 miscrosecond/second. The field is
* helpful for roaming STAs to select an AP that is likely to accept future
* admission control requests, but it does not represent an assurance that
* the HC admits these requests.
*/
public final int availableAdmissionCapacity;
/** Constructor */
public QbssLoad(
int stationCount,
int channelUtilization,
int availableAdmissionCapacity
) {
this.stationCount = stationCount;
this.channelUtilization = channelUtilization;
this.availableAdmissionCapacity = availableAdmissionCapacity;
}
/** Parse QbssLoad IE from appropriate Json object. */
public static QbssLoad parse(JsonObject contents) {
contents = contents.get("802.11e CCA Version").getAsJsonObject();
final int stationCount = contents.get("Station Count").getAsInt();
final int channelUtilization =
contents.get("Channel Utilization").getAsInt();
final int availableAdmissionCapacity =
contents.get("Available Admission Capabilities").getAsInt();
return new QbssLoad(
stationCount,
channelUtilization,
availableAdmissionCapacity
);
}
@Override
public int hashCode() {
return Objects.hash(
availableAdmissionCapacity,
channelUtilization,
stationCount
);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
QbssLoad other = (QbssLoad) obj;
return availableAdmissionCapacity == other.availableAdmissionCapacity &&
channelUtilization == other.channelUtilization &&
stationCount == other.stationCount;
}
@Override
public String toString() {
return "QbssLoad [stationCount=" + stationCount +
", channelUtilization=" + channelUtilization +
", availableAdmissionCapacity=" + availableAdmissionCapacity + "]";
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Objects;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
/**
* This information element (IE) appears in wifiscan entries. It is called
* "Tx Pwr Info" in these entries, and "Transmit Power Envelope" in the 802.11
* specification. Refer to the specification for more details. Language in
* javadocs is taken from the specification.
*/
public class TxPwrInfo {
/** Defined in 802.11 */
public static final int TYPE = 195;
/** Local maximum transmit power for 20 MHz. Required field. */
public final Integer localMaxTxPwrConstraint20MHz;
/** Local maximum transmit power for 40 MHz. Optional field. */
public final Integer localMaxTxPwrConstraint40MHz;
/** Local maximum transmit power for 80 MHz. Optional field. */
public final Integer localMaxTxPwrConstraint80MHz;
/** Local maximum transmit power for both 160 MHz and 80+80 MHz. Optional field. */
public final Integer localMaxTxPwrConstraint160MHz;
/** Constructor */
public TxPwrInfo(
int localMaxTxPwrConstraint20MHz,
int localMaxTxPwrConstraint40MHz,
int localMaxTxPwrConstraint80MHz,
int localMaxTxPwrConstraint160MHz
) {
this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz;
this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz;
this.localMaxTxPwrConstraint80MHz = localMaxTxPwrConstraint80MHz;
this.localMaxTxPwrConstraint160MHz = localMaxTxPwrConstraint160MHz;
}
/** Parse TxPwrInfo IE from appropriate Json object. */
public static TxPwrInfo parse(JsonObject contents) {
// required field
int localMaxTxPwrConstraint20MHz =
contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
// optional field
Integer localMaxTxPwrConstraint40MHz =
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint80MHz =
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
Integer localMaxTxPwrConstraint160MHz =
parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
return new TxPwrInfo(
localMaxTxPwrConstraint20MHz,
localMaxTxPwrConstraint40MHz,
localMaxTxPwrConstraint80MHz,
localMaxTxPwrConstraint160MHz
);
}
private static Integer parseOptionalField(
JsonObject contents,
String fieldName
) {
JsonElement element = contents.get(fieldName);
if (element == null) {
return null;
}
return element.getAsInt();
}
@Override
public int hashCode() {
return Objects.hash(
localMaxTxPwrConstraint160MHz,
localMaxTxPwrConstraint20MHz,
localMaxTxPwrConstraint40MHz,
localMaxTxPwrConstraint80MHz
);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TxPwrInfo other = (TxPwrInfo) obj;
return localMaxTxPwrConstraint160MHz ==
other.localMaxTxPwrConstraint160MHz &&
localMaxTxPwrConstraint20MHz ==
other.localMaxTxPwrConstraint20MHz &&
localMaxTxPwrConstraint40MHz ==
other.localMaxTxPwrConstraint40MHz &&
localMaxTxPwrConstraint80MHz == other.localMaxTxPwrConstraint80MHz;
}
@Override
public String toString() {
return "TxPwrInfo [localMaxTxPwrConstraint20MHz=" +
localMaxTxPwrConstraint20MHz + ", localMaxTxPwrConstraint40MHz=" +
localMaxTxPwrConstraint40MHz + ", localMaxTxPwrConstraint80MHz=" +
localMaxTxPwrConstraint80MHz + ", localMaxTxPwrConstraint160MHz=" +
localMaxTxPwrConstraint160MHz + "]";
}
}

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.operationelement;
package com.facebook.openwifirrm.ucentral.informationelement;
import java.util.Arrays;
import java.util.Objects;

View File

@@ -10,9 +10,11 @@ package com.facebook.openwifirrm.ucentral.models;
import java.util.Objects;
import com.google.gson.JsonArray;
/** Represents a single entry in wifi scan results. */
/**
* Represents a single entry in wifi scan results.
* ies[] array is not stored directly, but parsed into WifiScanEntry fields
*
*/
public class WifiScanEntryResult {
public int channel;
public long last_seen;
@@ -50,8 +52,6 @@ public class WifiScanEntryResult {
public String vht_oper;
public int capability;
public int frequency;
/** IE = information element */
public JsonArray ies;
/** Default Constructor. */
public WifiScanEntryResult() {}
@@ -68,7 +68,6 @@ public class WifiScanEntryResult {
this.vht_oper = o.vht_oper;
this.capability = o.capability;
this.frequency = o.frequency;
this.ies = o.ies;
}
@Override
@@ -79,7 +78,6 @@ public class WifiScanEntryResult {
channel,
frequency,
ht_oper,
ies,
last_seen,
signal,
ssid,
@@ -104,7 +102,9 @@ public class WifiScanEntryResult {
capability == other.capability && channel == other.channel &&
frequency == other.frequency && Objects
.equals(ht_oper, other.ht_oper) &&
Objects.equals(ies, other.ies) && last_seen == other.last_seen && signal == other.signal && Objects.equals(ssid, other.ssid) && tsf == other.tsf && Objects.equals(vht_oper, other.vht_oper);
last_seen == other.last_seen &&
Objects.equals(ssid, other.ssid) && tsf == other.tsf &&
Objects.equals(vht_oper, other.vht_oper);
}
@Override

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.operationelement;
package com.facebook.openwifirrm.ucentral.informationelement;
import static org.junit.jupiter.api.Assertions.assertEquals;

View File

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.openwifirrm.ucentral.operationelement;
package com.facebook.openwifirrm.ucentral.informationelement;
import static org.junit.jupiter.api.Assertions.assertEquals;