mirror of
https://github.com/Telecominfraproject/wlan-cloud-rrm.git
synced 2025-10-28 17:22:30 +00:00
Compare commits
48 Commits
v2.7.0-RC1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e22796758 | ||
|
|
f1c488eac8 | ||
|
|
066c523df5 | ||
|
|
ba8c156e72 | ||
|
|
d73eb23920 | ||
|
|
ac5a1c8887 | ||
|
|
ea3a13e98c | ||
|
|
c1511e8e91 | ||
|
|
05c36a535f | ||
|
|
c94c31cb63 | ||
|
|
404934eda9 | ||
|
|
80626388c8 | ||
|
|
a79359c69d | ||
|
|
a638d70fd6 | ||
|
|
e3705699b4 | ||
|
|
35eddf73cf | ||
|
|
f031027684 | ||
|
|
a54f9a48be | ||
|
|
c22ebeea31 | ||
|
|
e1b9052ecc | ||
|
|
c635af6c1d | ||
|
|
37a904087d | ||
|
|
6df81b7fef | ||
|
|
8171cc74ae | ||
|
|
215d73fee5 | ||
|
|
ecbf8fa644 | ||
|
|
19928e0286 | ||
|
|
da978611d0 | ||
|
|
e42eaa747b | ||
|
|
264f114be2 | ||
|
|
dd2b485b00 | ||
|
|
033d93beff | ||
|
|
e5d5f7d5c0 | ||
|
|
0b4fd49627 | ||
|
|
d81df03637 | ||
|
|
594fd9fa91 | ||
|
|
8c48a8901b | ||
|
|
0ac189f493 | ||
|
|
df21d07ec9 | ||
|
|
01a070c9b7 | ||
|
|
5211eae7c6 | ||
|
|
fafbda0bd8 | ||
|
|
43c9aaafb2 | ||
|
|
89e637cfeb | ||
|
|
0a64fb4963 | ||
|
|
4191bc1a70 | ||
|
|
3b6e83d103 | ||
|
|
27c36ff444 |
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
DOCKER_REGISTRY_USERNAME: ucentral
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
- docker
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
|
||||
2
.github/workflows/deploy-gh-pages.yml
vendored
2
.github/workflows/deploy-gh-pages.yml
vendored
@@ -18,7 +18,7 @@ jobs:
|
||||
distribution: 'adopt'
|
||||
cache: maven
|
||||
- name: Build with Maven
|
||||
run: mvn javadoc:javadoc
|
||||
run: mvn javadoc:aggregate
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
|
||||
2
.github/workflows/enforce-jira-issue-key.yml
vendored
2
.github/workflows/enforce-jira-issue-key.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
HELM_REPO_USERNAME: ucentral
|
||||
steps:
|
||||
- name: Checkout uCentral assembly chart repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: wlan-cloud-rrm
|
||||
|
||||
|
||||
22
.gitignore
vendored
22
.gitignore
vendored
@@ -1,12 +1,15 @@
|
||||
/target
|
||||
/*.log*
|
||||
/device_config.json
|
||||
/settings.json
|
||||
/topology.json
|
||||
/target/
|
||||
*/target/
|
||||
|
||||
# owrrm specific
|
||||
*.log*
|
||||
device_config.json
|
||||
settings.json
|
||||
topology.json
|
||||
|
||||
# Eclipse
|
||||
/.settings/
|
||||
/bin/
|
||||
.settings/
|
||||
bin/
|
||||
.metadata
|
||||
.classpath
|
||||
.project
|
||||
@@ -17,3 +20,8 @@
|
||||
*.iml
|
||||
*.iws
|
||||
*.ipr
|
||||
|
||||
# Miscellaneous files thzt should not be checked in
|
||||
temp/
|
||||
|
||||
.DS_Store
|
||||
|
||||
28
Dockerfile
28
Dockerfile
@@ -1,20 +1,22 @@
|
||||
FROM maven:3-jdk-11 as build
|
||||
FROM maven:3-eclipse-temurin-11 as build
|
||||
WORKDIR /usr/src/java
|
||||
COPY . .
|
||||
RUN mvn clean package -DappendVersionString="$(./scripts/get_build_version.sh)"
|
||||
RUN mvn clean package -pl owrrm -am -DappendVersionString="$(./scripts/get_build_version.sh)"
|
||||
|
||||
FROM adoptopenjdk/openjdk11-openj9:latest
|
||||
RUN apt-get update && apt-get install -y gettext-base wget
|
||||
RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
|
||||
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
|
||||
FROM ibm-semeru-runtimes:open-11-jre
|
||||
RUN apt-get update && apt-get install -y gettext-base
|
||||
ADD https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
|
||||
/usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
|
||||
RUN mkdir /owrrm-data
|
||||
WORKDIR /usr/src/java
|
||||
COPY docker-entrypoint.sh /
|
||||
COPY --from=build /usr/src/java/target/openwifi-rrm.jar /usr/local/bin/
|
||||
EXPOSE 16789
|
||||
COPY runner.sh /
|
||||
COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/
|
||||
EXPOSE 16789 16790
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
CMD ["java", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", \
|
||||
"-jar", "/usr/local/bin/openwifi-rrm.jar", \
|
||||
"run", "--config-env", \
|
||||
"-t", "/owrrm-data/topology.json", \
|
||||
"-d", "/owrrm-data/device_config.json"]
|
||||
ENV JVM_IMPL=openj9
|
||||
CMD ["/runner.sh", "java", "/usr/local/bin/openwifi-rrm.jar", \
|
||||
"run", \
|
||||
"--config-env", \
|
||||
"-t", "/owrrm-data/topology.json", \
|
||||
"-d", "/owrrm-data/device_config.json"]
|
||||
|
||||
28
Dockerfile-hotspot
Normal file
28
Dockerfile-hotspot
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM maven:3-eclipse-temurin-11 as build
|
||||
WORKDIR /usr/src/java
|
||||
COPY . .
|
||||
RUN mvn clean package -pl owrrm -am -DappendVersionString="$(./scripts/get_build_version.sh)"
|
||||
|
||||
FROM eclipse-temurin:11-jre-jammy
|
||||
RUN apt-get update && apt-get install -y gettext-base
|
||||
ADD https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
|
||||
/usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
|
||||
RUN mkdir /owrrm-data
|
||||
WORKDIR /usr/src/java
|
||||
COPY docker-entrypoint.sh /
|
||||
COPY runner.sh /
|
||||
COPY --from=build /usr/src/java/owrrm/target/openwifi-rrm.jar /usr/local/bin/
|
||||
|
||||
# generate Application CDS
|
||||
RUN java -Xshare:off -XX:DumpLoadedClassList=static-cds.lst -jar /usr/local/bin/openwifi-rrm.jar --help && \
|
||||
java -Xshare:dump -XX:SharedClassListFile=static-cds.lst -XX:SharedArchiveFile=static-cds.jsa -jar /usr/local/bin/openwifi-rrm.jar
|
||||
|
||||
EXPOSE 16789 16790
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
ENV JVM_IMPL=hotspot
|
||||
ENV EXTRA_JVM_FLAGS="-XX:SharedArchiveFile=static-cds.jsa -Xshare:auto"
|
||||
CMD ["/runner.sh", "java", "/usr/local/bin/openwifi-rrm.jar", \
|
||||
"run", \
|
||||
"--config-env", \
|
||||
"-t", "/owrrm-data/topology.json", \
|
||||
"-d", "/owrrm-data/device_config.json"]
|
||||
43
README.md
43
README.md
@@ -1,12 +1,10 @@
|
||||
# OpenWiFi RRM Service
|
||||
OpenWiFi uCentral-based radio resource management (RRM) service, providing a
|
||||
cloud-based Wi-Fi RRM layer for APs running the OpenWiFi SDK.
|
||||
[See here](owrrm/README.md) for details.
|
||||
|
||||
This service collects data from OpenWiFi APs (e.g. Wi-Fi scans, stats,
|
||||
capabilities) via the uCentral Gateway and Kafka, and integrates with the
|
||||
OpenWiFi Provisioning service to perform optimization across configured
|
||||
"venues". It pushes new device configuration parameters to APs after RRM
|
||||
algorithms are run (manually or periodically).
|
||||
## Project Structure
|
||||
This is an [Apache Maven] project with the following modules:
|
||||
* `lib-cloudsdk` - OpenWiFi CloudSDK Java Library
|
||||
* `owrrm` - OpenWiFi RRM Service
|
||||
|
||||
## Requirements
|
||||
* **Running:** JRE 11.
|
||||
@@ -16,7 +14,7 @@ algorithms are run (manually or periodically).
|
||||
```
|
||||
$ mvn package [-DskipTests]
|
||||
```
|
||||
This will build a runnable JAR located at `target/openwifi-rrm.jar`.
|
||||
This will build a runnable JAR located at `owrrm/target/openwifi-rrm.jar`.
|
||||
|
||||
Alternatively, Docker builds can be launched using the provided
|
||||
[Dockerfile](Dockerfile).
|
||||
@@ -27,34 +25,7 @@ $ mvn test
|
||||
```
|
||||
Unit tests are written using [JUnit 5].
|
||||
|
||||
## Usage
|
||||
```
|
||||
$ java -jar openwifi-rrm.jar [-h]
|
||||
```
|
||||
|
||||
To start the service, use the `run` command while providing configuration via
|
||||
either environment variables (`--config-env`) or a static JSON file
|
||||
(`--config-file`, default `settings.json`). The following data is *required*:
|
||||
* Service configuration
|
||||
* Env: `SERVICECONFIG_PRIVATEENDPOINT`, `SERVICECONFIG_PUBLICENDPOINT`
|
||||
* JSON: `serviceConfig` structure
|
||||
* Kafka broker URL
|
||||
* Env: `KAFKACONFIG_BOOTSTRAPSERVER`
|
||||
* JSON: `kafkaConfig` structure
|
||||
* MySQL database credentials
|
||||
* Env: `DATABASECONFIG_SERVER`, `DATABASECONFIG_USER`, `DATABASECONFIG_PASSWORD`
|
||||
* JSON: `databaseConfig` structure
|
||||
|
||||
## OpenAPI
|
||||
This service provides an OpenAPI HTTP interface on the port specified in the
|
||||
service configuration (`moduleConfig.apiServerParams`). An auto-generated
|
||||
OpenAPI 3.0 document is hosted at the endpoints `/openapi.{yaml,json}` and is
|
||||
written to [openapi.yaml](openapi.yaml) during the Maven "compile" phase.
|
||||
|
||||
## For Developers
|
||||
See [IMPLEMENTATION.md](IMPLEMENTATION.md) for service architecture details and
|
||||
[ALGORITHMS.md](ALGORITHMS.md) for descriptions of the RRM algorithms.
|
||||
|
||||
## Code Style
|
||||
Code is auto-formatted using [Spotless] with a custom Eclipse style config (see
|
||||
[spotless/eclipse-java-formatter.xml](spotless/eclipse-java-formatter.xml)).
|
||||
This can be applied via Maven (but is *not* enforced at build time):
|
||||
|
||||
@@ -29,8 +29,8 @@ services:
|
||||
targetPort: 16789
|
||||
protocol: TCP
|
||||
restapiinternal:
|
||||
servicePort: 17007
|
||||
targetPort: 17007
|
||||
servicePort: 16790
|
||||
targetPort: 16790
|
||||
protocol: TCP
|
||||
|
||||
checks:
|
||||
|
||||
3
lib-cloudsdk/README.md
Normal file
3
lib-cloudsdk/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# OpenWiFi CloudSDK Java Library
|
||||
A Java library providing clients and models for the OpenWiFi uCentral-based
|
||||
CloudSDK.
|
||||
80
lib-cloudsdk/pom.xml
Normal file
80
lib-cloudsdk/pom.xml
Normal file
@@ -0,0 +1,80 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>openwifi-cloudsdk</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<parent>
|
||||
<groupId>com.facebook</groupId>
|
||||
<artifactId>openwifi-base</artifactId>
|
||||
<version>2.7.0</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<!-- Hack for static files located in root project -->
|
||||
<myproject.root>${project.basedir}/..</myproject.root>
|
||||
</properties>
|
||||
<build>
|
||||
<finalName>openwifi-cloudsdk</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.konghq</groupId>
|
||||
<artifactId>unirest-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.kafka</groupId>
|
||||
<artifactId>kafka-clients</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.State;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.Counters;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.State.Interface.SSID.Association;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Aggregation model for State aggregation. Only contains info useful for
|
||||
* analysis.
|
||||
*/
|
||||
public class AggregatedState {
|
||||
|
||||
/**
|
||||
* Radio information with channel, channel_width and tx_power.
|
||||
*/
|
||||
public static class RadioConfig {
|
||||
public int channel;
|
||||
public int channelWidth;
|
||||
public int txPower;
|
||||
public String phy;
|
||||
|
||||
/** Default constructor with no args */
|
||||
private RadioConfig() {}
|
||||
|
||||
/** Constructor with args */
|
||||
public RadioConfig(JsonObject radio) {
|
||||
this.channel = radio.get("channel").getAsInt();
|
||||
this.channelWidth = radio.get("channel_width").getAsInt();
|
||||
this.txPower = radio.get("tx_power").getAsInt();
|
||||
this.phy = radio.get("phy").getAsString();
|
||||
}
|
||||
|
||||
public RadioConfig(int channel, int channelWidth, int txPower) {
|
||||
this.channel = channel;
|
||||
this.channelWidth = channelWidth;
|
||||
this.txPower = txPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(channel, channelWidth, txPower);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RadioConfig other = (RadioConfig) obj;
|
||||
return channel == other.channel &&
|
||||
channelWidth == other.channelWidth && txPower == other.txPower;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data model to keep raw data from {@link State.Interface.SSID.Association},
|
||||
* {@link State.Radio} and {@link State.Interface.Counters}.
|
||||
*/
|
||||
public static class AssociationInfo {
|
||||
/** Rate information with aggregated fields. */
|
||||
public static class Rate {
|
||||
/**
|
||||
* Aggregated fields bitRate
|
||||
*/
|
||||
public long bitRate;
|
||||
|
||||
/**
|
||||
* Aggregated fields chWidth
|
||||
*/
|
||||
public int chWidth;
|
||||
|
||||
/**
|
||||
* Aggregated fields mcs
|
||||
*/
|
||||
public int mcs;
|
||||
|
||||
/** Constructor with no args */
|
||||
private Rate() {}
|
||||
|
||||
/** Constructor with args */
|
||||
private Rate(long bitRate, int chWidth, int mcs) {
|
||||
this.bitRate = bitRate;
|
||||
this.chWidth = chWidth;
|
||||
this.mcs = mcs;
|
||||
}
|
||||
}
|
||||
|
||||
public long connected;
|
||||
public long inactive;
|
||||
public int rssi;
|
||||
public long rxBytes;
|
||||
public long rxPackets;
|
||||
public Rate rxRate;
|
||||
public long txBytes;
|
||||
public long txDuration;
|
||||
public long txFailed;
|
||||
public long txPackets;
|
||||
public Rate txRate;
|
||||
public long txRetries;
|
||||
public int ackSignal;
|
||||
public int ackSignalAvg;
|
||||
public long txPacketsCounters;
|
||||
public long txErrorsCounters;
|
||||
public long txDroppedCounters;
|
||||
public long activeMsRadio;
|
||||
public long busyMsRadio;
|
||||
public long noiseRadio;
|
||||
public long receiveMsRadio;
|
||||
public long transmitMsRadio;
|
||||
|
||||
/** unix time in ms */
|
||||
public long timestamp;
|
||||
|
||||
/** Default Constructor. */
|
||||
public AssociationInfo() {}
|
||||
|
||||
/** Constructor with only rssi(for test purpose) */
|
||||
public AssociationInfo(int rssi) {
|
||||
this.rssi = rssi;
|
||||
}
|
||||
|
||||
/** Constructor with args */
|
||||
public AssociationInfo(
|
||||
Association association,
|
||||
Counters counters,
|
||||
JsonObject radio,
|
||||
long timestamp
|
||||
) {
|
||||
// Association info
|
||||
connected = association.connected;
|
||||
inactive = association.inactive;
|
||||
rssi = association.rssi;
|
||||
rxBytes = association.rx_bytes;
|
||||
rxPackets = association.rx_packets;
|
||||
if (association.rx_rate != null) {
|
||||
rxRate = new Rate(
|
||||
association.rx_rate.bitrate,
|
||||
association.rx_rate.chwidth,
|
||||
association.rx_rate.mcs
|
||||
);
|
||||
} else {
|
||||
rxRate = new Rate();
|
||||
}
|
||||
|
||||
txBytes = association.tx_bytes;
|
||||
txPackets = association.tx_packets;
|
||||
|
||||
if (association.tx_rate != null) {
|
||||
txRate = new Rate(
|
||||
association.tx_rate.bitrate,
|
||||
association.tx_rate.chwidth,
|
||||
association.tx_rate.mcs
|
||||
);
|
||||
} else {
|
||||
txRate = new Rate();
|
||||
}
|
||||
txRetries = association.tx_retries;
|
||||
ackSignal = association.ack_signal;
|
||||
ackSignalAvg = association.ack_signal_avg;
|
||||
|
||||
//Counters info
|
||||
txPacketsCounters = counters.tx_packets;
|
||||
txErrorsCounters = counters.tx_errors;
|
||||
txDroppedCounters = counters.tx_dropped;
|
||||
|
||||
// Radio info
|
||||
activeMsRadio = radio.get("active_ms").getAsLong();
|
||||
busyMsRadio = radio.get("busy_ms").getAsLong();
|
||||
transmitMsRadio = radio.get("transmit_ms").getAsLong();
|
||||
receiveMsRadio = radio.get("receive_ms").getAsLong();
|
||||
noiseRadio = radio.get("noise").getAsLong();
|
||||
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate AssociationInfo over bssid, station and RadioConfig.
|
||||
public String bssid;
|
||||
public String station;
|
||||
public RadioConfig radioConfig;
|
||||
|
||||
// Store a list of AssociationInfo of the same link and radio configuration. */
|
||||
public List<AssociationInfo> associationInfoList;
|
||||
|
||||
/** Constructor with no args. For test purpose. */
|
||||
public AggregatedState() {
|
||||
this.associationInfoList = new ArrayList<>();
|
||||
this.radioConfig = new RadioConfig();
|
||||
}
|
||||
|
||||
/** Construct from Association, Counters, Radio and time stamp */
|
||||
public AggregatedState(
|
||||
Association association,
|
||||
Counters counters,
|
||||
JsonObject radio,
|
||||
long timestamp
|
||||
) {
|
||||
this.bssid = association.bssid;
|
||||
this.station = association.station;
|
||||
this.associationInfoList = new ArrayList<>();
|
||||
associationInfoList
|
||||
.add(new AssociationInfo(association, counters, radio, timestamp));
|
||||
this.radioConfig = new RadioConfig(radio);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the passed-in AggregatedState and this one match for aggregation.
|
||||
* If the two match in bssid, station and radio. Then they could be aggregated.
|
||||
*
|
||||
* @param state the reference AggregatedState with which to check with.
|
||||
* @return boolean return true if the two matches for aggregation.
|
||||
*/
|
||||
public boolean matchesForAggregation(AggregatedState state) {
|
||||
return bssid == state.bssid && station == state.station &&
|
||||
Objects.equals(radioConfig, state.radioConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an AggregatedState to this AggregatedState. Succeed only when the two
|
||||
* match for aggregation.
|
||||
*
|
||||
* @param state input AggregatedState
|
||||
* @return boolean true if the two match in bssid, station, channel,
|
||||
* channel_width and tx_power
|
||||
*/
|
||||
public boolean add(AggregatedState state) {
|
||||
if (matchesForAggregation(state)) {
|
||||
associationInfoList.addAll(state.associationInfoList);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* Utility functions for dealing with IEs
|
||||
*/
|
||||
public abstract class IEUtils {
|
||||
/**
|
||||
* Try to get a json object as a byte
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a byte or null
|
||||
*/
|
||||
public static Byte parseOptionalByteField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return element.getAsByte();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a json object as a short
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a short or null
|
||||
*/
|
||||
public static Short parseOptionalShortField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return element.getAsShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a json object as a int
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a int or null
|
||||
*/
|
||||
public static Integer parseOptionalIntField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return element.getAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a json object as a int
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a int (0 if key not present)
|
||||
*/
|
||||
public static Integer parseIntField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return 0;
|
||||
}
|
||||
return element.getAsInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a json object as a string
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a string or null
|
||||
*/
|
||||
public static String parseOptionalStringField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return null;
|
||||
}
|
||||
return element.getAsString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a json object as a boolean when represented as a number (0, 1)
|
||||
*
|
||||
* @param contents the JSON object to try to parse
|
||||
* @param fieldName the field name
|
||||
* @return the field as a boolean (false if key not present)
|
||||
*/
|
||||
public static boolean parseBooleanNumberField(
|
||||
JsonObject contents,
|
||||
String fieldName
|
||||
) {
|
||||
JsonElement element = contents.get(fieldName);
|
||||
if (element == null) {
|
||||
return false;
|
||||
}
|
||||
return element.getAsInt() > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.ies.Country;
|
||||
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint;
|
||||
import com.facebook.openwifi.cloudsdk.ies.QbssLoad;
|
||||
import com.facebook.openwifi.cloudsdk.ies.RMEnabledCapabilities;
|
||||
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo;
|
||||
|
||||
/** Wrapper class containing information elements */
|
||||
public final class InformationElements {
|
||||
public Country country;
|
||||
public QbssLoad qbssLoad;
|
||||
public LocalPowerConstraint localPowerConstraint;
|
||||
public TxPwrInfo txPwrInfo;
|
||||
public RMEnabledCapabilities rmEnabledCapabilities;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
country,
|
||||
localPowerConstraint,
|
||||
qbssLoad,
|
||||
rmEnabledCapabilities,
|
||||
txPwrInfo
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
InformationElements other = (InformationElements) obj;
|
||||
return Objects.equals(country, other.country) &&
|
||||
Objects.equals(localPowerConstraint, other.localPowerConstraint) &&
|
||||
Objects.equals(qbssLoad, other.qbssLoad) &&
|
||||
Objects
|
||||
.equals(rmEnabledCapabilities, other.rmEnabledCapabilities) &&
|
||||
Objects.equals(txPwrInfo, other.txPwrInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.State;
|
||||
|
||||
public class StateInfo extends State {
|
||||
/**
|
||||
* Unix time in milliseconds (ms). This is added it because State.unit.localtime is an unknown
|
||||
* time reference.
|
||||
*/
|
||||
public long timestamp;
|
||||
}
|
||||
@@ -6,15 +6,20 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Wrapper around uCentral AP configuration.
|
||||
@@ -51,33 +56,33 @@ public class UCentralApConfiguration {
|
||||
return config.getAsJsonArray("radios").size();
|
||||
}
|
||||
|
||||
/** Return all info in the radio config (or an empty array if none). */
|
||||
public JsonArray getRadioConfigList() {
|
||||
if (!config.has("radios") || !config.get("radios").isJsonArray()) {
|
||||
return new JsonArray();
|
||||
/** Return all info in the radio config (or an empty list if none). */
|
||||
public List<UCentralSchema.Radio> getRadioConfigList() {
|
||||
if (config.has("radios") && config.get("radios").isJsonArray()) {
|
||||
List<UCentralSchema.Radio> radios = new Gson().fromJson(
|
||||
config.getAsJsonArray("radios"),
|
||||
new TypeToken<ArrayList<UCentralSchema.Radio>>() {}.getType()
|
||||
);
|
||||
if (radios != null) {
|
||||
return radios;
|
||||
}
|
||||
}
|
||||
return config.getAsJsonArray("radios");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/** Return all the operational bands of an AP (from the radio config) */
|
||||
public Set<String> getRadioBandsSet(JsonArray radioConfigList) {
|
||||
public Set<String> getRadioBandsSet(
|
||||
List<UCentralSchema.Radio> radioConfigList
|
||||
) {
|
||||
Set<String> radioBandsSet = new HashSet<>();
|
||||
if (radioConfigList == null) {
|
||||
return radioBandsSet;
|
||||
}
|
||||
for (
|
||||
int radioIndex = 0; radioIndex < radioConfigList.size();
|
||||
radioIndex++
|
||||
) {
|
||||
JsonElement e = radioConfigList.get(radioIndex);
|
||||
if (!e.isJsonObject()) {
|
||||
for (UCentralSchema.Radio radio : radioConfigList) {
|
||||
if (radio == null || radio.band == null) {
|
||||
continue;
|
||||
}
|
||||
JsonObject radioObject = e.getAsJsonObject();
|
||||
if (!radioObject.has("band")) {
|
||||
continue;
|
||||
}
|
||||
radioBandsSet.add(radioObject.get("band").getAsString());
|
||||
radioBandsSet.add(radio.band);
|
||||
}
|
||||
return radioBandsSet;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -20,22 +20,22 @@ import org.json.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.facebook.openwifirrm.RRMConfig.UCentralConfig.UCentralSocketParams;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.CommandInfo;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.DeviceCapabilities;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.DeviceConfigureRequest;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.DeviceListWithStatus;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.DeviceWithStatus;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.StatisticsRecords;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.SystemInfoResults;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.TokenValidationResult;
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.WifiScanRequest;
|
||||
import com.facebook.openwifirrm.ucentral.prov.models.EntityList;
|
||||
import com.facebook.openwifirrm.ucentral.prov.models.InventoryTagList;
|
||||
import com.facebook.openwifirrm.ucentral.prov.models.RRMDetails;
|
||||
import com.facebook.openwifirrm.ucentral.prov.models.SerialNumberList;
|
||||
import com.facebook.openwifirrm.ucentral.prov.models.VenueList;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.DeviceCapabilities;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.DeviceConfigureRequest;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.DeviceListWithStatus;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.DeviceWithStatus;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.ScriptRequest;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.StatisticsRecords;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.SystemInfoResults;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.TokenValidationResult;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.WifiScanRequest;
|
||||
import com.facebook.openwifi.cloudsdk.models.prov.EntityList;
|
||||
import com.facebook.openwifi.cloudsdk.models.prov.InventoryTagList;
|
||||
import com.facebook.openwifi.cloudsdk.models.prov.RRMDetails;
|
||||
import com.facebook.openwifi.cloudsdk.models.prov.SerialNumberList;
|
||||
import com.facebook.openwifi.cloudsdk.models.prov.VenueList;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
@@ -127,8 +127,14 @@ public class UCentralClient {
|
||||
/** uCentral password */
|
||||
private final String password;
|
||||
|
||||
/** Socket parameters */
|
||||
private final UCentralSocketParams socketParams;
|
||||
/** Connection timeout for all requests, in ms */
|
||||
private final int connectTimeoutMs;
|
||||
|
||||
/** Socket timeout for all requests, in ms */
|
||||
private final int socketTimeoutMs;
|
||||
|
||||
/** Socket timeout for wifi scan requests, in ms */
|
||||
private final int wifiScanTimeoutMs;
|
||||
|
||||
/** The learned service endpoints. */
|
||||
private final Map<String, ServiceEvent> serviceEndpoints = new HashMap<>();
|
||||
@@ -147,7 +153,9 @@ public class UCentralClient {
|
||||
* (if needed)
|
||||
* @param username uCentral username (for public endpoints only)
|
||||
* @param password uCentral password (for public endpoints only)
|
||||
* @param socketParams Socket parameters
|
||||
* @param connectTimeoutMs connection timeout for all requests, in ms
|
||||
* @param socketTimeoutMs socket timeout for all requests, in ms
|
||||
* @param wifiScanTimeoutMs socket timeout for wifi scan requests, in ms
|
||||
*/
|
||||
public UCentralClient(
|
||||
String rrmEndpoint,
|
||||
@@ -155,13 +163,17 @@ public class UCentralClient {
|
||||
String uCentralSecPublicEndpoint,
|
||||
String username,
|
||||
String password,
|
||||
UCentralSocketParams socketParams
|
||||
int connectTimeoutMs,
|
||||
int socketTimeoutMs,
|
||||
int wifiScanTimeoutMs
|
||||
) {
|
||||
this.rrmEndpoint = rrmEndpoint;
|
||||
this.usePublicEndpoints = usePublicEndpoints;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.socketParams = socketParams;
|
||||
this.connectTimeoutMs = connectTimeoutMs;
|
||||
this.socketTimeoutMs = socketTimeoutMs;
|
||||
this.wifiScanTimeoutMs = wifiScanTimeoutMs;
|
||||
|
||||
if (usePublicEndpoints) {
|
||||
setServicePublicEndpoint(OWSEC_SERVICE, uCentralSecPublicEndpoint);
|
||||
@@ -305,8 +317,8 @@ public class UCentralClient {
|
||||
endpoint,
|
||||
service,
|
||||
parameters,
|
||||
socketParams.connectTimeoutMs,
|
||||
socketParams.socketTimeoutMs
|
||||
connectTimeoutMs,
|
||||
socketTimeoutMs
|
||||
);
|
||||
}
|
||||
|
||||
@@ -349,8 +361,8 @@ public class UCentralClient {
|
||||
endpoint,
|
||||
service,
|
||||
body,
|
||||
socketParams.connectTimeoutMs,
|
||||
socketParams.socketTimeoutMs
|
||||
connectTimeoutMs,
|
||||
socketTimeoutMs
|
||||
);
|
||||
}
|
||||
|
||||
@@ -454,8 +466,8 @@ public class UCentralClient {
|
||||
String.format("device/%s/wifiscan", serialNumber),
|
||||
OWGW_SERVICE,
|
||||
req,
|
||||
socketParams.connectTimeoutMs,
|
||||
socketParams.wifiScanTimeoutMs
|
||||
connectTimeoutMs,
|
||||
wifiScanTimeoutMs
|
||||
);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
@@ -551,6 +563,71 @@ public class UCentralClient {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a shell script on a device and return the result, or null upon error.
|
||||
*
|
||||
* @see #runScript(String, String, int)
|
||||
*/
|
||||
public CommandInfo runScript(String serialNumber, String script) {
|
||||
return runScript(serialNumber, script, 30);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a shell script on a device and return the result, or null upon error.
|
||||
*
|
||||
* @see #runScript(String, String, int, String)
|
||||
*/
|
||||
public CommandInfo runScript(
|
||||
String serialNumber,
|
||||
String script,
|
||||
int timeoutSec
|
||||
) {
|
||||
return runScript(serialNumber, script, timeoutSec, "shell");
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a script on a device and return the result, or null upon error.
|
||||
*
|
||||
* @param serialNumber the device
|
||||
* @param script the script contents
|
||||
* @param timeoutSec the timeout in seconds
|
||||
* @param type the script type (either "shell" or "ucode")
|
||||
*
|
||||
* @see UCentralUtils#getScriptOutput(CommandInfo)
|
||||
*/
|
||||
public CommandInfo runScript(
|
||||
String serialNumber,
|
||||
String script,
|
||||
int timeoutSec,
|
||||
String type
|
||||
) {
|
||||
ScriptRequest req = new ScriptRequest();
|
||||
req.serialNumber = serialNumber;
|
||||
req.timeout = timeoutSec;
|
||||
req.type = type;
|
||||
req.script = script;
|
||||
req.scriptId = "1"; // ??
|
||||
HttpResponse<String> response = httpPost(
|
||||
String.format("device/%s/script", serialNumber),
|
||||
OWGW_SERVICE,
|
||||
req
|
||||
);
|
||||
if (!response.isSuccess()) {
|
||||
logger.error("Error: {}", response.getBody());
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return gson.fromJson(response.getBody(), CommandInfo.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to CommandInfo: %s",
|
||||
response.getBody()
|
||||
);
|
||||
logger.error(errMsg, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** Retrieve a list of inventory from owprov. */
|
||||
public InventoryTagList getProvInventory() {
|
||||
HttpResponse<String> response = httpGet("inventory", OWPROV_SERVICE);
|
||||
@@ -613,6 +690,19 @@ public class UCentralClient {
|
||||
try {
|
||||
return gson.fromJson(response.getBody(), RRMDetails.class);
|
||||
} catch (JsonSyntaxException e) {
|
||||
// catch strings like "no", "inherit", "invalid" (???)
|
||||
JSONObject respBody;
|
||||
try {
|
||||
respBody = new JSONObject(response.getBody());
|
||||
respBody.getString("rrm");
|
||||
logger.error(
|
||||
"RRMDetails returned unexpected string body: {}",
|
||||
respBody
|
||||
);
|
||||
return null;
|
||||
} catch (JSONException e2) { /* ignore and fall through */}
|
||||
|
||||
// otherwise, log as a deserialization error
|
||||
String errMsg = String.format(
|
||||
"Failed to deserialize to RRMDetails: %s",
|
||||
response.getBody()
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -22,7 +22,7 @@ public final class UCentralConstants {
|
||||
public static final String BAND_2G = "2G";
|
||||
/** String of the 5 GHz band */
|
||||
public static final String BAND_5G = "5G";
|
||||
/** List of all bands */
|
||||
/** List of all bands ordered from lowest to highest */
|
||||
public static final List<String> BANDS = Collections
|
||||
.unmodifiableList(Arrays.asList(BAND_2G, BAND_5G));
|
||||
|
||||
@@ -0,0 +1,590 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.ies.Country;
|
||||
import com.facebook.openwifi.cloudsdk.ies.LocalPowerConstraint;
|
||||
import com.facebook.openwifi.cloudsdk.ies.QbssLoad;
|
||||
import com.facebook.openwifi.cloudsdk.ies.RMEnabledCapabilities;
|
||||
import com.facebook.openwifi.cloudsdk.ies.TxPwrInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.Capabilities;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.State;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.UCentralSchema;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.CommandInfo;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* uCentral utility methods/structures.
|
||||
*/
|
||||
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();
|
||||
|
||||
/** Map from band to ordered (increasing) list of available channels */
|
||||
public static final Map<String, List<Integer>> AVAILABLE_CHANNELS_BAND =
|
||||
Collections
|
||||
.unmodifiableMap(buildBandToChannelsMap());
|
||||
|
||||
// This class should not be instantiated.
|
||||
private UCentralUtils() {}
|
||||
|
||||
/**
|
||||
* Builds map from band to ordered (increasing) list of available channels.
|
||||
*/
|
||||
private static Map<String, List<Integer>> buildBandToChannelsMap() {
|
||||
Map<String, List<Integer>> bandToChannelsMap = new HashMap<>();
|
||||
bandToChannelsMap.put(
|
||||
UCentralConstants.BAND_5G,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(36, 40, 44, 48, 149, 153, 157, 161, 165)
|
||||
)
|
||||
);
|
||||
// NOTE: later, we may want to support channels 12, 13, and/or 14, if
|
||||
// the AP supports it and OWF vendors will use them
|
||||
bandToChannelsMap.put(
|
||||
UCentralConstants.BAND_2G,
|
||||
Collections.unmodifiableList(
|
||||
Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
|
||||
)
|
||||
);
|
||||
return bandToChannelsMap;
|
||||
}
|
||||
|
||||
/** Return the lowest available channel for the given band */
|
||||
public static int getLowerChannelLimit(String band) {
|
||||
return AVAILABLE_CHANNELS_BAND.get(band).get(0);
|
||||
}
|
||||
|
||||
/** Return the lowest available channel for the given band */
|
||||
public static int getUpperChannelLimit(String band) {
|
||||
List<Integer> channels = AVAILABLE_CHANNELS_BAND.get(band);
|
||||
return channels.get(channels.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a JSON wifi scan result into a list of WifiScanEntry objects.
|
||||
*
|
||||
* @param result result of the wifiscan
|
||||
* @param timestampMs Unix time in ms
|
||||
* @return list of wifiscan entries, or null if any parsing/deserialization
|
||||
* error occurred.
|
||||
*/
|
||||
public static List<WifiScanEntry> parseWifiScanEntries(
|
||||
JsonObject result,
|
||||
long timestampMs
|
||||
) {
|
||||
List<WifiScanEntry> entries = new ArrayList<>();
|
||||
try {
|
||||
JsonArray scanInfo = result
|
||||
.getAsJsonObject("status")
|
||||
.getAsJsonArray("scan");
|
||||
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 iesJsonArray = iesJsonElement.getAsJsonArray();
|
||||
InformationElements ieContainer = new InformationElements();
|
||||
for (JsonElement ieJsonElement : iesJsonArray) {
|
||||
JsonElement typeElement =
|
||||
ieJsonElement.getAsJsonObject().get("type");
|
||||
if (typeElement == null) { // shouldn't happen
|
||||
continue;
|
||||
}
|
||||
if (!ieJsonElement.isJsonObject()) {
|
||||
// the IEs we are interested in are Json objects
|
||||
continue;
|
||||
}
|
||||
JsonObject ie = ieJsonElement.getAsJsonObject();
|
||||
JsonElement contentsJsonElement = ie.get(IE_CONTENT_FIELD_KEY);
|
||||
if (
|
||||
contentsJsonElement == null ||
|
||||
!contentsJsonElement.isJsonObject()
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
JsonObject contents = contentsJsonElement.getAsJsonObject();
|
||||
try {
|
||||
switch (typeElement.getAsInt()) {
|
||||
case Country.TYPE:
|
||||
ieContainer.country = Country.parse(contents);
|
||||
break;
|
||||
case QbssLoad.TYPE:
|
||||
ieContainer.qbssLoad = QbssLoad.parse(contents);
|
||||
break;
|
||||
case LocalPowerConstraint.TYPE:
|
||||
ieContainer.localPowerConstraint =
|
||||
LocalPowerConstraint.parse(contents);
|
||||
break;
|
||||
case TxPwrInfo.TYPE:
|
||||
ieContainer.txPwrInfo = TxPwrInfo.parse(contents);
|
||||
break;
|
||||
case RMEnabledCapabilities.TYPE:
|
||||
ieContainer.rmEnabledCapabilities =
|
||||
RMEnabledCapabilities.parse(contents);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(String.format("Skipping invalid IE %s", ie), e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
entry.ieContainer = ieContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all radios config of an AP to a given value.
|
||||
*
|
||||
* Returns true if changed, or false if unchanged for any reason.
|
||||
*/
|
||||
public static boolean setRadioConfigField(
|
||||
String serialNumber,
|
||||
UCentralApConfiguration config,
|
||||
String fieldName,
|
||||
Map<String, Integer> newValueList
|
||||
) {
|
||||
boolean wasModified = false;
|
||||
int radioCount = config.getRadioCount();
|
||||
|
||||
// Iterate all the radios of an AP to find the corresponding band
|
||||
for (int radioIndex = 0; radioIndex < radioCount; radioIndex++) {
|
||||
JsonObject radioConfig = config.getRadioConfig(radioIndex);
|
||||
if (radioConfig == null) {
|
||||
continue;
|
||||
}
|
||||
String operationalBand = radioConfig.get("band").getAsString();
|
||||
if (!newValueList.containsKey(operationalBand)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the field doesn't exist in config, we generate the fieldName and
|
||||
// assign the new value to it.
|
||||
int newValue = newValueList.get(operationalBand);
|
||||
if (!radioConfig.has(fieldName)) {
|
||||
radioConfig.addProperty(fieldName, newValue);
|
||||
config.setRadioConfig(radioIndex, radioConfig);
|
||||
logger.info(
|
||||
"Device {}: setting {} {} to {} (was empty)",
|
||||
serialNumber,
|
||||
operationalBand,
|
||||
fieldName,
|
||||
newValue
|
||||
);
|
||||
wasModified = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare vs. existing value.
|
||||
// not all values are int so override those values
|
||||
Integer currentValue = null;
|
||||
JsonElement fieldValue = radioConfig.get(fieldName);
|
||||
if (
|
||||
fieldValue.isJsonPrimitive() &&
|
||||
fieldValue.getAsJsonPrimitive().isNumber()
|
||||
) {
|
||||
currentValue = fieldValue.getAsInt();
|
||||
} else {
|
||||
logger.debug(
|
||||
"Unable to get field '{}' as int, value was {}",
|
||||
fieldName,
|
||||
fieldValue.toString()
|
||||
);
|
||||
}
|
||||
|
||||
if (currentValue != null && currentValue == newValue) {
|
||||
logger.info(
|
||||
"Device {}: {} {} is already {}",
|
||||
serialNumber,
|
||||
operationalBand,
|
||||
fieldName,
|
||||
newValue
|
||||
);
|
||||
} else {
|
||||
// Update to new value
|
||||
radioConfig.addProperty(fieldName, newValue);
|
||||
config.setRadioConfig(radioIndex, radioConfig);
|
||||
logger.info(
|
||||
"Device {}: setting {} {} to {} (was {})",
|
||||
serialNumber,
|
||||
operationalBand,
|
||||
fieldName,
|
||||
newValue,
|
||||
currentValue != null ? currentValue : fieldValue.toString()
|
||||
);
|
||||
wasModified = true;
|
||||
}
|
||||
}
|
||||
return wasModified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the APs on a band who participate in an optimization algorithm.
|
||||
* Get the info from the configuration field in deviceStatus
|
||||
* (Since the State doesn't explicitly show the "band" info)
|
||||
*
|
||||
* Returns the results map
|
||||
*/
|
||||
public static Map<String, List<String>> getBandsMap(
|
||||
Map<String, List<UCentralSchema.Radio>> deviceStatus
|
||||
) {
|
||||
Map<String, List<String>> bandsMap = new HashMap<>();
|
||||
for (
|
||||
Map.Entry<String, List<UCentralSchema.Radio>> entry : deviceStatus
|
||||
.entrySet()
|
||||
) {
|
||||
String serialNumber = entry.getKey();
|
||||
for (UCentralSchema.Radio radio : entry.getValue()) {
|
||||
bandsMap
|
||||
.computeIfAbsent(radio.band, k -> new ArrayList<>())
|
||||
.add(serialNumber);
|
||||
}
|
||||
}
|
||||
return bandsMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the capabilities of the APs who participate in an optimization algorithm.
|
||||
*
|
||||
* @param deviceStatus map of {device, status}
|
||||
* @param deviceCapabilities map of {device, capabilities info}
|
||||
* @param defaultAvailableChannels map of {band, list of available channels}
|
||||
*
|
||||
* @return the results map of {band, {device, list of available channels}}
|
||||
*/
|
||||
public static Map<String, Map<String, List<Integer>>> getDeviceAvailableChannels(
|
||||
Map<String, List<UCentralSchema.Radio>> deviceStatus,
|
||||
Map<String, Map<String, Capabilities.Phy>> deviceCapabilities,
|
||||
Map<String, List<Integer>> defaultAvailableChannels
|
||||
) {
|
||||
Map<String, Map<String, List<Integer>>> deviceAvailableChannels =
|
||||
new HashMap<>();
|
||||
|
||||
for (
|
||||
Map.Entry<String, List<UCentralSchema.Radio>> entry : deviceStatus
|
||||
.entrySet()
|
||||
) {
|
||||
String serialNumber = entry.getKey();
|
||||
for (UCentralSchema.Radio radio : entry.getValue()) {
|
||||
Map<String, Capabilities.Phy> capabilitiesPhyMap =
|
||||
deviceCapabilities.get(serialNumber);
|
||||
List<Integer> availableChannels = new ArrayList<>();
|
||||
if (capabilitiesPhyMap == null) {
|
||||
availableChannels
|
||||
.addAll(defaultAvailableChannels.get(radio.band));
|
||||
} else {
|
||||
Set<Entry<String, Capabilities.Phy>> entrySet =
|
||||
capabilitiesPhyMap
|
||||
.entrySet();
|
||||
for (Map.Entry<String, Capabilities.Phy> f : entrySet) {
|
||||
Capabilities.Phy phy = f.getValue();
|
||||
String bandInsideObject = phy.band.toString();
|
||||
if (bandInsideObject.equals(radio.band)) {
|
||||
// (TODO) Remove the following dfsChannels code block
|
||||
// when the DFS channels are available
|
||||
Set<Integer> dfsChannels = new HashSet<>();
|
||||
try {
|
||||
int[] channelInfo = phy.dfs_channels;
|
||||
for (int d : channelInfo) {
|
||||
dfsChannels.add(d);
|
||||
}
|
||||
} catch (Exception d) {}
|
||||
try {
|
||||
int[] channelInfo = phy.channels;
|
||||
for (int channel : channelInfo) {
|
||||
if (!dfsChannels.contains(channel)) {
|
||||
availableChannels.add(channel);
|
||||
}
|
||||
}
|
||||
} catch (Exception c) {
|
||||
availableChannels
|
||||
.addAll(
|
||||
defaultAvailableChannels.get(radio.band)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviceAvailableChannels.computeIfAbsent(
|
||||
radio.band,
|
||||
k -> new HashMap<>()
|
||||
)
|
||||
.put(
|
||||
serialNumber,
|
||||
availableChannels
|
||||
);
|
||||
}
|
||||
}
|
||||
return deviceAvailableChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mapping between bssids and APs.
|
||||
* Get the info from the State data
|
||||
*
|
||||
* Returns the results map
|
||||
*/
|
||||
public static Map<String, String> getBssidsMap(
|
||||
Map<String, ? extends State> latestState
|
||||
) {
|
||||
Map<String, String> bssidMap = new HashMap<>();
|
||||
for (Entry<String, ? extends State> e : latestState.entrySet()) {
|
||||
State state = e.getValue();
|
||||
for (
|
||||
int interfaceIndex = 0;
|
||||
interfaceIndex < state.interfaces.length;
|
||||
interfaceIndex++
|
||||
) {
|
||||
if (state.interfaces[interfaceIndex].ssids == null) {
|
||||
continue;
|
||||
}
|
||||
for (
|
||||
int ssidIndex = 0;
|
||||
ssidIndex < state.interfaces[interfaceIndex].ssids.length;
|
||||
ssidIndex++
|
||||
) {
|
||||
bssidMap.put(
|
||||
state.interfaces[interfaceIndex].ssids[ssidIndex].bssid,
|
||||
e.getKey()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bssidMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given channel is in the given band.
|
||||
*
|
||||
* @param channel channel number
|
||||
* @param band "2G" or "5G"
|
||||
* @return true if the given channel is in the given band; false otherwise
|
||||
*/
|
||||
public static boolean isChannelInBand(int channel, String band) {
|
||||
return AVAILABLE_CHANNELS_BAND.get(band).contains(channel);
|
||||
}
|
||||
|
||||
/** Return which band contains the given frequency (MHz). */
|
||||
public static String freqToBand(int freqMHz) {
|
||||
if (2412 <= freqMHz && freqMHz <= 2484) {
|
||||
return "2G";
|
||||
} else {
|
||||
return "5G";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse channel width, if it encounters an error it will return null.
|
||||
* It can handle 80p80 in two ways. First it can just treat it as 160. Second,
|
||||
* it can just apply to the first 80 channel and ignore the second. This is
|
||||
* controlled by treatSeparate.
|
||||
*
|
||||
* @param channelWidthStr the channel width
|
||||
* @param treatSeparate treats each band separately
|
||||
* @return channel width in MHz
|
||||
*/
|
||||
public static Integer parseChannelWidth(
|
||||
String channelWidthStr,
|
||||
boolean treatSeparate
|
||||
) {
|
||||
// 80p80 is the only case where it can't be parsed into an integer
|
||||
if (channelWidthStr.equals("80p80")) {
|
||||
return treatSeparate ? 80 : 160;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(channelWidthStr);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse the index from the reference string in the JSON returned from
|
||||
* other services. Note that this only returns the index, the caller is
|
||||
* responsible for making sure that the correct field is passed in and the
|
||||
* index is used in the correct fields. If there's an error parsing, it will
|
||||
* return null.
|
||||
*
|
||||
* @param reference The reference string, keyed under "$ref"
|
||||
* @return the index of the reference or null if an error occurred.
|
||||
*/
|
||||
public static Integer parseReferenceIndex(String reference) {
|
||||
try {
|
||||
return Integer.parseInt(
|
||||
reference,
|
||||
reference.lastIndexOf("/") + 1,
|
||||
reference.length(),
|
||||
10
|
||||
);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a map of Wi-Fi client (STA) MAC addresses to the Client structure
|
||||
* found for that interface. This does NOT support clients connected on
|
||||
* multiple interfaces simultaneously.
|
||||
*/
|
||||
public static Map<String, State.Interface.Client> getWifiClientInfo(
|
||||
State state
|
||||
) {
|
||||
Map<String, State.Interface.Client> ret = new HashMap<>();
|
||||
|
||||
// Aggregate over all interfaces
|
||||
for (State.Interface iface : state.interfaces) {
|
||||
if (iface.ssids == null || iface.clients == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert client array to map (for faster lookups)
|
||||
Map<String, State.Interface.Client> ifaceMap = new HashMap<>();
|
||||
for (State.Interface.Client client : iface.clients) {
|
||||
ifaceMap.put(client.mac, client);
|
||||
}
|
||||
|
||||
// Loop over all SSIDs and connected clients
|
||||
for (State.Interface.SSID ssid : iface.ssids) {
|
||||
if (ssid.associations == null) {
|
||||
continue;
|
||||
}
|
||||
for (
|
||||
State.Interface.SSID.Association association : ssid.associations
|
||||
) {
|
||||
State.Interface.Client client =
|
||||
ifaceMap.get(association.station);
|
||||
if (client != null) {
|
||||
ret.put(association.station, client);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress (inflate) a UTF-8 string using ZLIB.
|
||||
*
|
||||
* @param compressed the compressed string
|
||||
* @param uncompressedSize the uncompressed size (must be known)
|
||||
*/
|
||||
private static String inflate(String compressed, int uncompressedSize)
|
||||
throws DataFormatException {
|
||||
if (compressed == null) {
|
||||
throw new NullPointerException("Null compressed string");
|
||||
}
|
||||
if (uncompressedSize < 0) {
|
||||
throw new IllegalArgumentException("Invalid size");
|
||||
}
|
||||
|
||||
byte[] input = compressed.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] output = new byte[uncompressedSize];
|
||||
|
||||
Inflater inflater = new Inflater();
|
||||
inflater.setInput(input, 0, input.length);
|
||||
inflater.inflate(output);
|
||||
inflater.end();
|
||||
|
||||
return new String(output, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the result of the "script" API, return the actual script output
|
||||
* (decoded/decompressed if needed), or null if the script returned an
|
||||
* error.
|
||||
*
|
||||
* @see UCentralClient#runScript(String, String, int, String)
|
||||
*/
|
||||
public static String getScriptOutput(CommandInfo info) {
|
||||
if (info == null || info.results == null) {
|
||||
return null;
|
||||
}
|
||||
if (!info.results.has("status")) {
|
||||
return null;
|
||||
}
|
||||
JsonObject status = info.results.get("status").getAsJsonObject();
|
||||
if (!status.has("error")) {
|
||||
return null;
|
||||
}
|
||||
int errorCode = status.get("error").getAsInt();
|
||||
if (errorCode != 0) {
|
||||
logger.error("Script failed with code {}", errorCode);
|
||||
return null;
|
||||
}
|
||||
if (status.has("result")) {
|
||||
// Raw result
|
||||
return status.get("result").getAsString();
|
||||
} else if (status.has("result_64") && status.has("result_sz")) {
|
||||
// Base64+compressed result
|
||||
// NOTE: untested, not actually implemented on ucentral-client?
|
||||
try {
|
||||
String encoded = status.get("result_64").getAsString();
|
||||
int uncompressedSize = status.get("result_sz").getAsInt();
|
||||
String decoded = new String(
|
||||
Base64.getDecoder().decode(encoded),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
return inflate(decoded, uncompressedSize);
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to decode or inflate script result", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.models.WifiScanEntryResult;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.WifiScanEntryResult;
|
||||
|
||||
/**
|
||||
* Extends {@link WifiScanEntryResult} to track the response time of the entry.
|
||||
@@ -22,6 +22,8 @@ public class WifiScanEntry extends WifiScanEntryResult {
|
||||
* time reference.
|
||||
*/
|
||||
public long unixTimeMs;
|
||||
/** Stores Information Elements (IEs) from the wifiscan entry. */
|
||||
public InformationElements ieContainer;
|
||||
|
||||
/** Default Constructor. */
|
||||
public WifiScanEntry() {}
|
||||
@@ -30,13 +32,14 @@ public class WifiScanEntry extends WifiScanEntryResult {
|
||||
public WifiScanEntry(WifiScanEntry o) {
|
||||
super(o);
|
||||
this.unixTimeMs = o.unixTimeMs;
|
||||
this.ieContainer = o.ieContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = super.hashCode();
|
||||
result = prime * result + Objects.hash(unixTimeMs);
|
||||
result = prime * result + Objects.hash(ieContainer, unixTimeMs);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -52,7 +55,8 @@ public class WifiScanEntry extends WifiScanEntryResult {
|
||||
return false;
|
||||
}
|
||||
WifiScanEntry other = (WifiScanEntry) obj;
|
||||
return unixTimeMs == other.unixTimeMs;
|
||||
return Objects.equals(ieContainer, other.ieContainer) &&
|
||||
unixTimeMs == other.unixTimeMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "BSS AC Access Delay" in
|
||||
* 802.11 specs (section 9.4.2.43). Refer to the specification for more details.
|
||||
* Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class BssAcAccessDelay {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 68;
|
||||
|
||||
/**
|
||||
* Subfield that goes into Access Category Access Delay field in BSS AC
|
||||
* Access Delay. For information on what the values mean, check section
|
||||
* 9.4.2.43
|
||||
*/
|
||||
public static class AccessCategoryAccessDelay {
|
||||
/**
|
||||
* Unsigned 8 bits that represents a scaled representation of best effort AC
|
||||
* access delay
|
||||
*/
|
||||
public final short averageAccessDelayForBestEffort;
|
||||
/**
|
||||
* Unsigned 8 bits that represents a scaled representation of background AC
|
||||
* access delay
|
||||
*/
|
||||
public final short averageAccessDelayForBackground;
|
||||
/**
|
||||
* Unsigned 8 bits that represents a scaled representation of video AC access
|
||||
* delay
|
||||
*/
|
||||
public final short averageAccessDelayForVideo;
|
||||
/**
|
||||
* Unsigned 8 bits that represents a scaled representation of voice AC access
|
||||
* delay
|
||||
*/
|
||||
public final short averageAccessDelayForVoice;
|
||||
|
||||
/** Constructor */
|
||||
public AccessCategoryAccessDelay(
|
||||
short averageAccessDelayForBestEffort,
|
||||
short averageAccessDelayForBackground,
|
||||
short averageAccessDelayForVideo,
|
||||
short averageAccessDelayForVoice
|
||||
) {
|
||||
this.averageAccessDelayForBestEffort =
|
||||
averageAccessDelayForBestEffort;
|
||||
this.averageAccessDelayForBackground =
|
||||
averageAccessDelayForBackground;
|
||||
this.averageAccessDelayForVideo = averageAccessDelayForVideo;
|
||||
this.averageAccessDelayForVoice = averageAccessDelayForVoice;
|
||||
}
|
||||
|
||||
/** Parse AccessCategoryAccessDelay from JSON object */
|
||||
// TODO rename fields as necessary - we don't know how the data format yet
|
||||
public static AccessCategoryAccessDelay parse(JsonObject contents) {
|
||||
return new AccessCategoryAccessDelay(
|
||||
contents.get("Average Access Delay For Best Effort").getAsShort(),
|
||||
contents.get("Average Access Delay For Background").getAsShort(),
|
||||
contents.get("Average Access Delay For Video").getAsShort(),
|
||||
contents.get("Average Access Delay For Voice").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
averageAccessDelayForBestEffort,
|
||||
averageAccessDelayForBestEffort,
|
||||
averageAccessDelayForVideo,
|
||||
averageAccessDelayForVoice
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessCategoryAccessDelay other = (AccessCategoryAccessDelay) obj;
|
||||
return averageAccessDelayForBestEffort ==
|
||||
other.averageAccessDelayForBestEffort &&
|
||||
averageAccessDelayForBackground ==
|
||||
other.averageAccessDelayForBackground &&
|
||||
averageAccessDelayForVideo ==
|
||||
other.averageAccessDelayForVideo &&
|
||||
averageAccessDelayForVoice == other.averageAccessDelayForVoice;
|
||||
}
|
||||
}
|
||||
|
||||
/** 32 bits - Holds AccessCategoryAccessDelay subfield */
|
||||
public final AccessCategoryAccessDelay accessCategoryAccessDelay;
|
||||
|
||||
/** Constructor */
|
||||
public BssAcAccessDelay(
|
||||
AccessCategoryAccessDelay accessCategoryAccessDelay
|
||||
) {
|
||||
this.accessCategoryAccessDelay = accessCategoryAccessDelay;
|
||||
}
|
||||
|
||||
/** Parse BssAcAccessDelay from JSON object */
|
||||
// TODO rename fields as necessary - we don't know how the data format yet
|
||||
public static BssAcAccessDelay parse(JsonObject contents) {
|
||||
return new BssAcAccessDelay(
|
||||
AccessCategoryAccessDelay.parse(
|
||||
contents.get("AP Average Access Delay").getAsJsonObject()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(accessCategoryAccessDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BssAcAccessDelay other = (BssAcAccessDelay) obj;
|
||||
return accessCategoryAccessDelay == other.accessCategoryAccessDelay;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "BSS Average Access Delay" in
|
||||
* 802.11 specs (section 9.4.2.38). Refer to the specification for more details.
|
||||
* Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class BssAvgAccessDelay {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 63;
|
||||
|
||||
/**
|
||||
* Unsigned 8 bits representing a scaled average medium access delay for all DCF
|
||||
* and EDCAF frames transmitted, measured from the time it's ready for
|
||||
* transmission to actual transmission start time.
|
||||
*/
|
||||
public final short apAvgAccessDelay;
|
||||
|
||||
/** Constructor */
|
||||
public BssAvgAccessDelay(short apAvgAccessDelay) {
|
||||
this.apAvgAccessDelay = apAvgAccessDelay;
|
||||
}
|
||||
|
||||
/** Parse BssAvgAccessDelay from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static BssAvgAccessDelay parse(JsonObject contents) {
|
||||
return new BssAvgAccessDelay(
|
||||
contents.get("AP Average Access Delay").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(apAvgAccessDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BssAvgAccessDelay other = (BssAvgAccessDelay) obj;
|
||||
return apAvgAccessDelay == other.apAvgAccessDelay;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "20/40
|
||||
* BSS Intolerant Channel Report" in 802.11 specs (section 9.4.2.57). Refer to
|
||||
* the specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class BssIntolerantChannelReport {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 73;
|
||||
|
||||
/**
|
||||
* Unsigned 8 bits representing the operating class in which the channel list
|
||||
* is valid
|
||||
*/
|
||||
public final short operatingClass;
|
||||
/** List of unsigned 8 bits, representing the channel numbers */
|
||||
public final List<Short> channelList;
|
||||
|
||||
/** Constructor */
|
||||
public BssIntolerantChannelReport(
|
||||
short operatingClass,
|
||||
List<Short> channelList
|
||||
) {
|
||||
this.operatingClass = operatingClass;
|
||||
this.channelList = Collections.unmodifiableList(channelList);
|
||||
}
|
||||
|
||||
/** Parse BssIntolerantChannelReport from JSON object */
|
||||
// TODO rename fields as necessary - we don't know how the data format yet
|
||||
public static BssIntolerantChannelReport parse(JsonObject contents) {
|
||||
List<Short> channelList = new ArrayList<>();
|
||||
JsonElement channelListJson = contents.get("Channel List");
|
||||
if (channelListJson != null) {
|
||||
for (JsonElement elem : channelListJson.getAsJsonArray()) {
|
||||
channelList.add(elem.getAsShort());
|
||||
}
|
||||
}
|
||||
|
||||
return new BssIntolerantChannelReport(
|
||||
contents.get("Operating Class").getAsShort(),
|
||||
channelList
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(operatingClass, channelList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BssIntolerantChannelReport other = (BssIntolerantChannelReport) obj;
|
||||
return operatingClass == other.operatingClass &&
|
||||
channelList.equals(other.channelList);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called
|
||||
* "Collocated Interference Report" in 802.11 specs (section 9.4.2.84). Refer to
|
||||
* the specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class CollocatedInterferenceReport {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 96;
|
||||
|
||||
public static class InterferenceAccuracyAndIndex {
|
||||
/**
|
||||
* Unsigned int (4 bits) representing expected accuracy of the estimate of
|
||||
* interference in dB with 95% confidence interval
|
||||
*/
|
||||
public final byte expectedAccuracy;
|
||||
/**
|
||||
* Unsigned int (4 bits) indicating the interference index that is unique for
|
||||
* each type of interference source
|
||||
*/
|
||||
public final byte interferenceIndex;
|
||||
|
||||
/** Constructor */
|
||||
public InterferenceAccuracyAndIndex(
|
||||
byte expectedAccuracy,
|
||||
byte interferenceIndex
|
||||
) {
|
||||
this.expectedAccuracy = expectedAccuracy;
|
||||
this.interferenceIndex = interferenceIndex;
|
||||
}
|
||||
|
||||
/** Parse InterferenceAccuracyAndIndex from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static InterferenceAccuracyAndIndex parse(JsonObject contents) {
|
||||
return new InterferenceAccuracyAndIndex(
|
||||
contents.get("Expected Accuracy").getAsByte(),
|
||||
contents.get("Interference Index").getAsByte()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(expectedAccuracy, interferenceIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
InterferenceAccuracyAndIndex other =
|
||||
(InterferenceAccuracyAndIndex) obj;
|
||||
return expectedAccuracy == other.expectedAccuracy &&
|
||||
interferenceIndex == other.interferenceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/** Unsigned 8 bits representing when the report is generated */
|
||||
public final short reportPeriod;
|
||||
/**
|
||||
* signed 8 bits representing the maximum level of the collocated
|
||||
* interference power in units of dBm over all receive chains averaged over a
|
||||
* 4 microsecond period during an interference period and across interference
|
||||
* bandwidth
|
||||
*/
|
||||
public final byte interferenceLevel;
|
||||
/** Subfield for interference level accuracy and index - 8 bits */
|
||||
public final InterferenceAccuracyAndIndex interferenceAccuracyAndIndex;
|
||||
/**
|
||||
* Unsigned 32 bits representing the interval between two successibe periods
|
||||
* of interference in microseconds
|
||||
*/
|
||||
public final long interferenceInterval;
|
||||
/**
|
||||
* Unsigned 32 bits representing the duration of each period of interference in
|
||||
* microseconds
|
||||
*/
|
||||
public final long interferenceBurstLength;
|
||||
/**
|
||||
* Unsigned 32 bits contains the least significant 4 octets (i.e., B0–B31) of
|
||||
* the TSF timer at the start of the interference burst. When either the
|
||||
* Interference Interval or the Interference Burst Length fields are set to
|
||||
* 2^32 – 1, this field indicates the average duty cycle
|
||||
*/
|
||||
public final long interferenceStartTimeDutyCycle;
|
||||
/**
|
||||
* Unsigned 32 bits representing indicates the center frequency of interference
|
||||
* in units of 5 kHz
|
||||
*/
|
||||
public final long interferenceCenterFrequency;
|
||||
/**
|
||||
* Unsigned 16 bits representing the bandwidth in units of 5 kHz at the –3 dB
|
||||
* roll-off point of the interference signal
|
||||
*/
|
||||
public final short interferenceBandwidth;
|
||||
|
||||
/** Constructor */
|
||||
public CollocatedInterferenceReport(
|
||||
short reportPeriod,
|
||||
byte interferenceLevel,
|
||||
InterferenceAccuracyAndIndex interferenceAccuracyAndIndex,
|
||||
long interferenceInterval,
|
||||
long interferenceBurstLength,
|
||||
long interferenceStartTimeDutyCycle,
|
||||
long interferenceCenterFrequency,
|
||||
short interferenceBandwidth
|
||||
) {
|
||||
this.reportPeriod = reportPeriod;
|
||||
this.interferenceLevel = interferenceLevel;
|
||||
this.interferenceAccuracyAndIndex = interferenceAccuracyAndIndex;
|
||||
this.interferenceInterval = interferenceInterval;
|
||||
this.interferenceBurstLength = interferenceBurstLength;
|
||||
this.interferenceStartTimeDutyCycle = interferenceStartTimeDutyCycle;
|
||||
this.interferenceCenterFrequency = interferenceCenterFrequency;
|
||||
this.interferenceBandwidth = interferenceBandwidth;
|
||||
}
|
||||
|
||||
/** Parse CollocatedInterferenceReport from JSON object */
|
||||
// TODO rename fields as necessary - we don't know how the data format yet
|
||||
public static CollocatedInterferenceReport parse(JsonObject contents) {
|
||||
return new CollocatedInterferenceReport(
|
||||
contents.get("Report Period").getAsShort(),
|
||||
contents.get("Intereference Level").getAsByte(),
|
||||
InterferenceAccuracyAndIndex
|
||||
.parse(
|
||||
contents.get("Interference Level Accuracy/Inteference Index").getAsJsonObject()
|
||||
),
|
||||
contents.get("Interference Interval").getAsLong(),
|
||||
contents.get("Interference Burst Length").getAsLong(),
|
||||
contents.get("Interference Start Time/Duty Cycle").getAsLong(),
|
||||
contents.get("Interference Center Frequency").getAsLong(),
|
||||
contents.get("Interference Bandwidth").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
reportPeriod,
|
||||
interferenceLevel,
|
||||
interferenceAccuracyAndIndex,
|
||||
interferenceInterval,
|
||||
interferenceBurstLength,
|
||||
interferenceStartTimeDutyCycle,
|
||||
interferenceCenterFrequency,
|
||||
interferenceBandwidth
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CollocatedInterferenceReport other = (CollocatedInterferenceReport) obj;
|
||||
return reportPeriod == other.reportPeriod &&
|
||||
interferenceLevel == other.interferenceLevel &&
|
||||
interferenceAccuracyAndIndex
|
||||
.equals(other.interferenceAccuracyAndIndex) &&
|
||||
interferenceInterval == other.interferenceInterval &&
|
||||
interferenceBurstLength == other.interferenceBurstLength &&
|
||||
interferenceStartTimeDutyCycle ==
|
||||
other.interferenceStartTimeDutyCycle &&
|
||||
interferenceCenterFrequency == other.interferenceCenterFrequency &&
|
||||
interferenceBandwidth == other.interferenceBandwidth;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries.
|
||||
* Refer to the 802.11 specification (section 9.4.2.8) for more details.
|
||||
* Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class Country {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 7;
|
||||
|
||||
/** Constraints for a subset of channels in the AP's country */
|
||||
public static class CountryInfo {
|
||||
/**
|
||||
* 8 bits unsigned - the lowest channel number in the CountryInfo.
|
||||
*/
|
||||
public final short firstChannelNumber;
|
||||
/**
|
||||
* 8 bits unsigned - The maximum power, in dBm, allowed to be transmitted.
|
||||
*/
|
||||
public final short maximumTransmitPowerLevel;
|
||||
/**
|
||||
* 8 bits unsigned - 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 short numberOfChannels;
|
||||
|
||||
/** Constructor. */
|
||||
public CountryInfo(
|
||||
short firstChannelNumber,
|
||||
short maximumTransmitPowerLevel,
|
||||
short numberOfChannels
|
||||
) {
|
||||
this.firstChannelNumber = firstChannelNumber;
|
||||
this.maximumTransmitPowerLevel = maximumTransmitPowerLevel;
|
||||
this.numberOfChannels = numberOfChannels;
|
||||
}
|
||||
|
||||
/** Parse CountryInfo from the appropriate Json object. */
|
||||
public static CountryInfo parse(JsonObject contents) {
|
||||
final short firstChannelNumber =
|
||||
contents.get("First Channel Number").getAsShort();
|
||||
final short maximumTransmitPowerLevel = contents
|
||||
.get("Maximum Transmit Power Level (in dBm)")
|
||||
.getAsShort();
|
||||
final short numberOfChannels =
|
||||
contents.get("Number of Channels").getAsShort();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/** Country */
|
||||
public final String country;
|
||||
/**
|
||||
* 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(
|
||||
String country,
|
||||
List<CountryInfo> countryInfos
|
||||
) {
|
||||
this.country = country;
|
||||
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()) {
|
||||
JsonObject innerElem = jsonElement.getAsJsonObject();
|
||||
CountryInfo countryInfo =
|
||||
CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject());
|
||||
constraints.add(countryInfo);
|
||||
}
|
||||
}
|
||||
return new Country(
|
||||
contents.get("Code").getAsString(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.operationelement;
|
||||
package com.facebook.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
@@ -15,9 +15,12 @@ import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* High Throughput (HT) Operation Element, which is potentially present in
|
||||
* wifiscan entries. Introduced in 802.11n (2009).
|
||||
* wifiscan entries. Introduced in 802.11n (2009). Refer to the 802.11
|
||||
* specification (section 9.4.2.56)).
|
||||
*/
|
||||
public class HTOperationElement {
|
||||
public class HTOperation {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 61;
|
||||
|
||||
/** Channel number of the primary channel. */
|
||||
public final byte primaryChannel;
|
||||
@@ -78,7 +81,7 @@ public class HTOperationElement {
|
||||
* For details about the parameters, see the javadocs for the corresponding
|
||||
* member variables.
|
||||
*/
|
||||
public HTOperationElement(
|
||||
public HTOperation(
|
||||
byte primaryChannel,
|
||||
byte secondaryChannelOffset,
|
||||
boolean staChannelWidth,
|
||||
@@ -114,7 +117,7 @@ public class HTOperationElement {
|
||||
}
|
||||
|
||||
/** Constructor with the most used parameters. */
|
||||
public HTOperationElement(
|
||||
public HTOperation(
|
||||
byte primaryChannel,
|
||||
byte secondaryChannelOffset,
|
||||
boolean staChannelWidth,
|
||||
@@ -141,7 +144,7 @@ public class HTOperationElement {
|
||||
* @param htOper a base64 encoded properly formatted HT operation element (see
|
||||
* 802.11)
|
||||
*/
|
||||
public HTOperationElement(String htOper) {
|
||||
public HTOperation(String htOper) {
|
||||
byte[] bytes = Base64.decodeBase64(htOper);
|
||||
/*
|
||||
* Note that the code here may seem to read "reversed" compared to 802.11. This
|
||||
@@ -182,7 +185,7 @@ public class HTOperationElement {
|
||||
* @return true if the the operation elements "match" for the purpose of
|
||||
* aggregating statistics; false otherwise.
|
||||
*/
|
||||
public boolean matchesForAggregation(HTOperationElement other) {
|
||||
public boolean matchesForAggregation(HTOperation other) {
|
||||
return other != null && primaryChannel == other.primaryChannel &&
|
||||
secondaryChannelOffset == other.secondaryChannelOffset &&
|
||||
staChannelWidth == other.staChannelWidth &&
|
||||
@@ -211,8 +214,8 @@ public class HTOperationElement {
|
||||
if (htOper1 == null || htOper2 == null) {
|
||||
return false; // false if exactly one is null
|
||||
}
|
||||
HTOperationElement htOperObj1 = new HTOperationElement(htOper1);
|
||||
HTOperationElement htOperObj2 = new HTOperationElement(htOper2);
|
||||
HTOperation htOperObj1 = new HTOperation(htOper1);
|
||||
HTOperation htOperObj2 = new HTOperation(htOper2);
|
||||
return htOperObj1.matchesForAggregation(htOperObj2);
|
||||
}
|
||||
|
||||
@@ -248,7 +251,7 @@ public class HTOperationElement {
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
HTOperationElement other = (HTOperationElement) obj;
|
||||
HTOperation other = (HTOperation) obj;
|
||||
return Arrays.equals(basicHtMcsSet, other.basicHtMcsSet) &&
|
||||
channelCenterFrequencySegment2 ==
|
||||
other.channelCenterFrequencySegment2 &&
|
||||
@@ -263,4 +266,4 @@ public class HTOperationElement {
|
||||
staChannelWidth == other.staChannelWidth &&
|
||||
stbcBeacon == other.stbcBeacon;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
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 (section 9.4.2.13). Refer to the specification for more details.
|
||||
* Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class LocalPowerConstraint {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 32;
|
||||
|
||||
/**
|
||||
* Unsigned 8 bits - 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 short localPowerConstraint;
|
||||
|
||||
/** Constructor */
|
||||
public LocalPowerConstraint(short localPowerConstraint) {
|
||||
this.localPowerConstraint = localPowerConstraint;
|
||||
}
|
||||
|
||||
/** Parse LocalPowerConstraint IE from appropriate Json object. */
|
||||
public static LocalPowerConstraint parse(JsonObject contents) {
|
||||
final short localPowerConstraint =
|
||||
contents.get("Local Power Constraint").getAsShort();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,318 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called
|
||||
* "Neighbor Report" in 802.11 specs (section 9.4.2.36). Refer to the
|
||||
* specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class NeighborReport {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 52;
|
||||
|
||||
/**
|
||||
* The BSSID Information field can be used to help determine neighbor service
|
||||
* set transition candidates
|
||||
*/
|
||||
public static class BssidInfo {
|
||||
/**
|
||||
* The capability subelement containing selected capability information for
|
||||
* the AP indicated by this BSSID.
|
||||
*/
|
||||
public static class Capabilities {
|
||||
/** dot11SpectrumManagementRequired */
|
||||
public final boolean spectrumManagement;
|
||||
/** dot11QosOptionImplemented */
|
||||
public final boolean qos;
|
||||
/** dot11APSDOptionImplemented */
|
||||
public final boolean apsd;
|
||||
/** dot11RadioMeasurementActivated */
|
||||
public final boolean radioMeasurement;
|
||||
|
||||
/** Constructor */
|
||||
public Capabilities(
|
||||
boolean spectrumManagement,
|
||||
boolean qos,
|
||||
boolean apsd,
|
||||
boolean radioMeasurement
|
||||
) {
|
||||
this.spectrumManagement = spectrumManagement;
|
||||
this.qos = qos;
|
||||
this.apsd = apsd;
|
||||
this.radioMeasurement = radioMeasurement;
|
||||
}
|
||||
|
||||
/** Parse Capabilities from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static Capabilities parse(JsonObject contents) {
|
||||
return new Capabilities(
|
||||
contents.get("Spectrum Management").getAsBoolean(),
|
||||
contents.get("QoS").getAsBoolean(),
|
||||
contents.get("APSD").getAsBoolean(),
|
||||
contents.get("Radio Management").getAsBoolean()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects
|
||||
.hash(spectrumManagement, qos, apsd, radioMeasurement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Capabilities other = (Capabilities) obj;
|
||||
return spectrumManagement == other.spectrumManagement &&
|
||||
qos == other.qos && apsd == other.apsd &&
|
||||
radioMeasurement == other.radioMeasurement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 2 unsigned bits - whether the AP identified by this BSSID is reachable by
|
||||
* the STA that requested the neighbor report
|
||||
*/
|
||||
public final byte apReachability;
|
||||
/**
|
||||
* If true, indicates that the AP identified by this BSSID supports the same
|
||||
* security provisioning as used by the STA in its current association. If the
|
||||
* bit is false, it indicates either that the AP does not support the same
|
||||
* security provisioning or that the security information is not available at
|
||||
* this time.
|
||||
*/
|
||||
public final boolean security;
|
||||
/**
|
||||
* Indicates the AP indicated by this BSSID has the same authenticator as
|
||||
* the AP sending the report. If this bit is false, it indicates a distinct
|
||||
* authenticator or the information is not available.
|
||||
*/
|
||||
public final boolean keyScope;
|
||||
/**
|
||||
* @see Capabilities
|
||||
*/
|
||||
public final Capabilities capabilities;
|
||||
/**
|
||||
* Set to true to indicate that the AP represented by this BSSID is including
|
||||
* an MDE in its Beacon frames and that the contents of that MDE are identical
|
||||
* to the MDE advertised by the AP sending the report
|
||||
*/
|
||||
public final boolean mobilityDomain;
|
||||
/**
|
||||
* High throughput or not, if true the contents of the HT Capabilities in the
|
||||
* Beacon frame should be identical to the HT Capabilities advertised by the
|
||||
* AP sending the report
|
||||
*/
|
||||
public final boolean highThroughput;
|
||||
/**
|
||||
* Very High throughput or not, if true the contents of the VHT Capabilities
|
||||
* in the Beacon frame should be identical to the VHT Capabilities advertised
|
||||
* by the AP sending the report
|
||||
*/
|
||||
public final boolean veryHighThroughput;
|
||||
/**
|
||||
* Indicate that the AP represented by this BSSID is an AP that has set the Fine
|
||||
* Timing Measurement Responder field of the Extended Capabilities element
|
||||
*/
|
||||
public final boolean ftm;
|
||||
|
||||
/** Constructor */
|
||||
public BssidInfo(
|
||||
byte apReachability,
|
||||
boolean security,
|
||||
boolean keyScope,
|
||||
Capabilities capabilities,
|
||||
boolean mobilityDomain,
|
||||
boolean highThroughput,
|
||||
boolean veryHighThroughput,
|
||||
boolean ftm
|
||||
) {
|
||||
this.apReachability = apReachability;
|
||||
this.security = security;
|
||||
this.keyScope = keyScope;
|
||||
this.capabilities = capabilities;
|
||||
this.mobilityDomain = mobilityDomain;
|
||||
this.highThroughput = highThroughput;
|
||||
this.veryHighThroughput = veryHighThroughput;
|
||||
this.ftm = ftm;
|
||||
}
|
||||
|
||||
/** Parse BssidInfo from JSON object */
|
||||
// TODO rename fields as necessary - we don't know how the data format yet
|
||||
public static BssidInfo parse(JsonObject contents) {
|
||||
JsonElement capabilitiesJson = contents.get("capabilities");
|
||||
if (capabilitiesJson == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Capabilities capabilities =
|
||||
Capabilities.parse(capabilitiesJson.getAsJsonObject());
|
||||
return new BssidInfo(
|
||||
contents.get("AP Reachability").getAsByte(),
|
||||
contents.get("Security").getAsBoolean(),
|
||||
contents.get("Key Scope").getAsBoolean(),
|
||||
capabilities,
|
||||
contents.get("Mobility Domain").getAsBoolean(),
|
||||
contents.get("High Throughput").getAsBoolean(),
|
||||
contents.get("Very High Throughput").getAsBoolean(),
|
||||
contents.get("FTM").getAsBoolean()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
apReachability,
|
||||
security,
|
||||
keyScope,
|
||||
mobilityDomain,
|
||||
highThroughput,
|
||||
veryHighThroughput,
|
||||
ftm
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BssidInfo other = (BssidInfo) obj;
|
||||
return apReachability == other.apReachability &&
|
||||
security == other.security && keyScope == other.keyScope &&
|
||||
capabilities == other.capabilities &&
|
||||
mobilityDomain == other.mobilityDomain &&
|
||||
highThroughput == other.highThroughput &&
|
||||
veryHighThroughput == other.veryHighThroughput &&
|
||||
ftm == other.ftm;
|
||||
}
|
||||
}
|
||||
|
||||
/** BSSID */
|
||||
public final String bssid;
|
||||
/**
|
||||
* @see BssidInfo
|
||||
*/
|
||||
public final BssidInfo bssidInfo;
|
||||
/**
|
||||
* Unsigned 8 bits - indicates the channel set of the AP indicated by this BSSID
|
||||
*/
|
||||
public final short operatingClass;
|
||||
/**
|
||||
* Unsigned 8 bits - channel number
|
||||
*/
|
||||
public final short channelNumber;
|
||||
/**
|
||||
* Unsigned 8 bits - PHY type
|
||||
*/
|
||||
public final short phyType;
|
||||
// TODO do we want to support the subelements?
|
||||
/**
|
||||
* Optional subelements
|
||||
*/
|
||||
public final List<JsonObject> subelements;
|
||||
|
||||
/** Constructor */
|
||||
public NeighborReport(
|
||||
String bssid,
|
||||
BssidInfo bssidInfo,
|
||||
short operatingClass,
|
||||
short channelNumber,
|
||||
short phyType,
|
||||
List<JsonObject> subelements
|
||||
) {
|
||||
this.bssid = bssid;
|
||||
this.bssidInfo = bssidInfo;
|
||||
this.operatingClass = operatingClass;
|
||||
this.channelNumber = channelNumber;
|
||||
this.phyType = phyType;
|
||||
this.subelements = Collections.unmodifiableList(subelements);
|
||||
}
|
||||
|
||||
/** Parse NeighborReport from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static NeighborReport parse(JsonObject contents) {
|
||||
List<JsonObject> subelements = null;
|
||||
JsonElement subelementsObj = contents.get("Subelements");
|
||||
if (subelementsObj != null) {
|
||||
subelements = new ArrayList<JsonObject>();
|
||||
for (JsonElement elem : subelementsObj.getAsJsonArray()) {
|
||||
subelements.add(elem.getAsJsonObject());
|
||||
}
|
||||
}
|
||||
|
||||
return new NeighborReport(
|
||||
contents.get("BSSID").getAsString(),
|
||||
BssidInfo.parse(contents.get("BSSID Info").getAsJsonObject()),
|
||||
contents.get("Operating Class").getAsShort(),
|
||||
contents.get("Channel Number").getAsShort(),
|
||||
contents.get("Phy Type").getAsShort(),
|
||||
subelements
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects
|
||||
.hash(bssid, bssidInfo, operatingClass, channelNumber, phyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NeighborReport other = (NeighborReport) obj;
|
||||
return bssid == other.bssid && bssidInfo == other.bssidInfo &&
|
||||
operatingClass == other.operatingClass &&
|
||||
channelNumber == other.channelNumber && phyType == other.phyType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "Power
|
||||
* Capability" in 802.11 specs (section 9.4.2.14). Refer to the specification
|
||||
* for more details. Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class PowerCapability {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 33;
|
||||
|
||||
/**
|
||||
* Signed 8 bits units of dB relative to 1mW - nominal minimum transmit power
|
||||
* with which the STA is capable of transmitting in the current channel, with a
|
||||
* tolerance ± 5 dB.
|
||||
*/
|
||||
public final byte minimumTxPowerCapability;
|
||||
/**
|
||||
* Signed 8 bits units of dB relative to 1mW - nominal maximum transmit power
|
||||
* with which the STA is capable of transmitting in the current channel, with a
|
||||
* tolerance ± 5 dB.
|
||||
*/
|
||||
public final byte maximumTxPowerCapability;
|
||||
|
||||
/** Constructor */
|
||||
public PowerCapability(
|
||||
byte minimumTxPowerCapability,
|
||||
byte maximumTxPowerCapability
|
||||
) {
|
||||
this.minimumTxPowerCapability = minimumTxPowerCapability;
|
||||
this.maximumTxPowerCapability = maximumTxPowerCapability;
|
||||
}
|
||||
|
||||
/** Parse PowerCapability from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static PowerCapability parse(JsonObject contents) {
|
||||
return new PowerCapability(
|
||||
contents.get("Minimum Tx Power Capability").getAsByte(),
|
||||
contents.get("Maximum Tx Power Capability").getAsByte()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(minimumTxPowerCapability, maximumTxPowerCapability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PowerCapability other = (PowerCapability) obj;
|
||||
return minimumTxPowerCapability == other.minimumTxPowerCapability &&
|
||||
maximumTxPowerCapability == other.maximumTxPowerCapability;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
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
|
||||
* "QBSS Load" in these entries, and just "BSS Load" in the 802.11 specification
|
||||
* (section 9.4.2.27). Refer to the specification for more details. Language in
|
||||
* javadocs is taken from the specification.
|
||||
*/
|
||||
public class QbssLoad {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 11;
|
||||
|
||||
/**
|
||||
* Unsigned 16 bits - The total number of STAs currently associated with the BSS.
|
||||
*/
|
||||
public final short stationCount;
|
||||
/**
|
||||
* Unsigned 8 bits - 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 short channelUtilization;
|
||||
/**
|
||||
* Unsigned 16 bits - 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 short availableAdmissionCapacity;
|
||||
|
||||
/** Constructor */
|
||||
public QbssLoad(
|
||||
short stationCount,
|
||||
short channelUtilization,
|
||||
short availableAdmissionCapacity
|
||||
) {
|
||||
this.stationCount = stationCount;
|
||||
this.channelUtilization = channelUtilization;
|
||||
this.availableAdmissionCapacity = availableAdmissionCapacity;
|
||||
}
|
||||
|
||||
/** Parse QbssLoad IE from appropriate Json object; return null if invalid. */
|
||||
public static QbssLoad parse(JsonObject contents) {
|
||||
// unclear why there is this additional nested layer
|
||||
JsonElement ccaContentJsonElement = contents.get("802.11e CCA Version");
|
||||
if (ccaContentJsonElement == null) {
|
||||
return null;
|
||||
}
|
||||
contents = ccaContentJsonElement.getAsJsonObject();
|
||||
final short stationCount = contents.get("Station Count").getAsShort();
|
||||
final short channelUtilization =
|
||||
contents.get("Channel Utilization").getAsShort();
|
||||
final short availableAdmissionCapacity =
|
||||
contents.get("Available Admission Capabilities").getAsShort();
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "RCPI"
|
||||
* in 802.11 specs (section 9.4.2.37). Refer to the specification for more
|
||||
* details. Language in javadocs is taken from the specification.
|
||||
*/
|
||||
public class RCPI {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 53;
|
||||
|
||||
/**
|
||||
* Unsigned 8 bits - indication of the received RF power in the selected
|
||||
* channel for a received frame
|
||||
*/
|
||||
public final short rcpi;
|
||||
|
||||
/** Constructor */
|
||||
public RCPI(short rcpi) {
|
||||
this.rcpi = rcpi;
|
||||
}
|
||||
|
||||
/** Parse RCPI from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static RCPI parse(JsonObject contents) {
|
||||
return new RCPI(
|
||||
contents.get("RCPI").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(rcpi);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RCPI other = (RCPI) obj;
|
||||
return rcpi == other.rcpi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.IEUtils;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called "RM
|
||||
* Enabled Capabilities" in 802.11 specs (section 9.4.2.45). Refer to the
|
||||
* specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class RMEnabledCapabilities {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 70;
|
||||
|
||||
// Bit fields
|
||||
// @formatter:off
|
||||
public final boolean linkMeasurementCapabilityEnabled;
|
||||
public final boolean neighborReportCapabilityEnabled;
|
||||
public final boolean parallelMeasurementsCapabilityEnabled;
|
||||
public final boolean repeatedMeasurementsCapabilityEnabled;
|
||||
public final boolean beaconPassiveMeasurementCapabilityEnabled;
|
||||
public final boolean beaconActiveMeasurementCapabilityEnabled;
|
||||
public final boolean beaconTableMeasurementCapabilityEnabled;
|
||||
public final boolean beaconMeasurementReportingConditionsCapabilityEnabled;
|
||||
public final boolean frameMeasurementCapabilityEnabled;
|
||||
public final boolean channelLoadMeasurementCapabilityEnabled;
|
||||
public final boolean noiseHistogramMeasurementCapabilityEnabled;
|
||||
public final boolean statisticsMeasurementCapabilityEnabled;
|
||||
public final boolean lciMeasurementCapabilityEnabled;
|
||||
public final boolean lciAzimuthCapabilityEnabled;
|
||||
public final boolean transmitStreamCategoryMeasurementCapabilityEnabled;
|
||||
public final boolean triggeredTransmitStreamCategoryMeasurementCapabilityEnabled;
|
||||
public final boolean apChannelReportCapabilityEnabled;
|
||||
public final boolean rmMibCapabilityEnabled;
|
||||
public final int operatingChannelMaxMeasurementDuration;
|
||||
public final int nonoperatingChannelMaxMeasurementDuration;
|
||||
public final int measurementPilotCapability;
|
||||
public final boolean measurementPilotTransmissionInformationCapabilityEnabled;
|
||||
public final boolean neighborReportTsfOffsetCapabilityEnabled;
|
||||
public final boolean rcpiMeasurementCapabilityEnabled;
|
||||
public final boolean rsniMeasurementCapabilityEnabled;
|
||||
public final boolean bssAverageAccessDelayCapabilityEnabled;
|
||||
public final boolean bssAvailableAdmissionCapacityCapabilityEnabled;
|
||||
public final boolean antennaCapabilityEnabled;
|
||||
public final boolean ftmRangeReportCapabilityEnabled;
|
||||
public final boolean civicLocationMeasurementCapabilityEnabled;
|
||||
// @formatter:on
|
||||
|
||||
/** Constructor */
|
||||
public RMEnabledCapabilities(
|
||||
boolean linkMeasurementCapabilityEnabled,
|
||||
boolean neighborReportCapabilityEnabled,
|
||||
boolean parallelMeasurementsCapabilityEnabled,
|
||||
boolean repeatedMeasurementsCapabilityEnabled,
|
||||
boolean beaconPassiveMeasurementCapabilityEnabled,
|
||||
boolean beaconActiveMeasurementCapabilityEnabled,
|
||||
boolean beaconTableMeasurementCapabilityEnabled,
|
||||
boolean beaconMeasurementReportingConditionsCapabilityEnabled,
|
||||
boolean frameMeasurementCapabilityEnabled,
|
||||
boolean channelLoadMeasurementCapabilityEnabled,
|
||||
boolean noiseHistogramMeasurementCapabilityEnabled,
|
||||
boolean statisticsMeasurementCapabilityEnabled,
|
||||
boolean lciMeasurementCapabilityEnabled,
|
||||
boolean lciAzimuthCapabilityEnabled,
|
||||
boolean transmitStreamCategoryMeasurementCapabilityEnabled,
|
||||
boolean triggeredTransmitStreamCategoryMeasurementCapabilityEnabled,
|
||||
boolean apChannelReportCapabilityEnabled,
|
||||
boolean rmMibCapabilityEnabled,
|
||||
int operatingChannelMaxMeasurementDuration,
|
||||
int nonoperatingChannelMaxMeasurementDuration,
|
||||
int measurementPilotCapability,
|
||||
boolean measurementPilotTransmissionInformationCapabilityEnabled,
|
||||
boolean neighborReportTsfOffsetCapabilityEnabled,
|
||||
boolean rcpiMeasurementCapabilityEnabled,
|
||||
boolean rsniMeasurementCapabilityEnabled,
|
||||
boolean bssAverageAccessDelayCapabilityEnabled,
|
||||
boolean bssAvailableAdmissionCapacityCapabilityEnabled,
|
||||
boolean antennaCapabilityEnabled,
|
||||
boolean ftmRangeReportCapabilityEnabled,
|
||||
boolean civicLocationMeasurementCapabilityEnabled
|
||||
) {
|
||||
// @formatter:off
|
||||
this.linkMeasurementCapabilityEnabled = linkMeasurementCapabilityEnabled;
|
||||
this.neighborReportCapabilityEnabled = neighborReportCapabilityEnabled;
|
||||
this.parallelMeasurementsCapabilityEnabled = parallelMeasurementsCapabilityEnabled;
|
||||
this.repeatedMeasurementsCapabilityEnabled = repeatedMeasurementsCapabilityEnabled;
|
||||
this.beaconPassiveMeasurementCapabilityEnabled = beaconPassiveMeasurementCapabilityEnabled;
|
||||
this.beaconActiveMeasurementCapabilityEnabled = beaconActiveMeasurementCapabilityEnabled;
|
||||
this.beaconTableMeasurementCapabilityEnabled = beaconTableMeasurementCapabilityEnabled;
|
||||
this.beaconMeasurementReportingConditionsCapabilityEnabled = beaconMeasurementReportingConditionsCapabilityEnabled;
|
||||
this.frameMeasurementCapabilityEnabled = frameMeasurementCapabilityEnabled;
|
||||
this.channelLoadMeasurementCapabilityEnabled = channelLoadMeasurementCapabilityEnabled;
|
||||
this.noiseHistogramMeasurementCapabilityEnabled = noiseHistogramMeasurementCapabilityEnabled;
|
||||
this.statisticsMeasurementCapabilityEnabled = statisticsMeasurementCapabilityEnabled;
|
||||
this.lciMeasurementCapabilityEnabled = lciMeasurementCapabilityEnabled;
|
||||
this.lciAzimuthCapabilityEnabled = lciAzimuthCapabilityEnabled;
|
||||
this.transmitStreamCategoryMeasurementCapabilityEnabled = transmitStreamCategoryMeasurementCapabilityEnabled;
|
||||
this.triggeredTransmitStreamCategoryMeasurementCapabilityEnabled = triggeredTransmitStreamCategoryMeasurementCapabilityEnabled;
|
||||
this.apChannelReportCapabilityEnabled = apChannelReportCapabilityEnabled;
|
||||
this.rmMibCapabilityEnabled = rmMibCapabilityEnabled;
|
||||
this.operatingChannelMaxMeasurementDuration = operatingChannelMaxMeasurementDuration;
|
||||
this.nonoperatingChannelMaxMeasurementDuration = nonoperatingChannelMaxMeasurementDuration;
|
||||
this.measurementPilotCapability = measurementPilotCapability;
|
||||
this.measurementPilotTransmissionInformationCapabilityEnabled = measurementPilotTransmissionInformationCapabilityEnabled;
|
||||
this.neighborReportTsfOffsetCapabilityEnabled = neighborReportTsfOffsetCapabilityEnabled;
|
||||
this.rcpiMeasurementCapabilityEnabled = rcpiMeasurementCapabilityEnabled;
|
||||
this.rsniMeasurementCapabilityEnabled = rsniMeasurementCapabilityEnabled;
|
||||
this.bssAverageAccessDelayCapabilityEnabled = bssAverageAccessDelayCapabilityEnabled;
|
||||
this.bssAvailableAdmissionCapacityCapabilityEnabled = bssAvailableAdmissionCapacityCapabilityEnabled;
|
||||
this.antennaCapabilityEnabled = antennaCapabilityEnabled;
|
||||
this.ftmRangeReportCapabilityEnabled = ftmRangeReportCapabilityEnabled;
|
||||
this.civicLocationMeasurementCapabilityEnabled = civicLocationMeasurementCapabilityEnabled;
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
/** Parse RMEnabledCapabilities IE from appropriate Json object. */
|
||||
public static RMEnabledCapabilities parse(JsonObject contents) {
|
||||
JsonObject o = contents.get("RM Capabilities").getAsJsonObject();
|
||||
// @formatter:off
|
||||
return new RMEnabledCapabilities(
|
||||
/* bits 0-17 */
|
||||
IEUtils.parseBooleanNumberField(o, "Link Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Neighbor Report"),
|
||||
IEUtils.parseBooleanNumberField(o, "Parallel Measurements"),
|
||||
IEUtils.parseBooleanNumberField(o, "Repeated Measurements"),
|
||||
IEUtils.parseBooleanNumberField(o, "Beacon Passive Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Beacon Active Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Beacon Table Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Beacon Measurement Reporting Conditions"),
|
||||
IEUtils.parseBooleanNumberField(o, "Frame Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Channel Load Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Noise Histogram Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Statistics Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "LCI Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "LCI Azimuth capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "Transmit Stream/Category Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "Triggered Transmit Stream/Category Measurement"),
|
||||
IEUtils.parseBooleanNumberField(o, "AP Channel Report capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "RM MIB capability"),
|
||||
/* bits 18-20 */
|
||||
IEUtils.parseIntField(o, "Operating Channel Max Measurement Duration"),
|
||||
/* bits 21-23 */
|
||||
IEUtils.parseIntField(o, "Nonoperating Channel Max Measurement Duration"),
|
||||
/* bits 24-26 */
|
||||
IEUtils.parseIntField(o, "Measurement Pilotcapability"),
|
||||
/* bits 27-35 */
|
||||
false /* TODO "Measurement Pilot Transmission Information Capability" */,
|
||||
IEUtils.parseBooleanNumberField(o, "Neighbor Report TSF Offset"),
|
||||
IEUtils.parseBooleanNumberField(o, "RCPI Measurement capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "RSNI Measurement capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "BSS Average Access Delay capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "BSS Available Admission Capacity capability"),
|
||||
IEUtils.parseBooleanNumberField(o, "Antenna capability"),
|
||||
false /* TODO "FTM Range Report Capability" */,
|
||||
false /* TODO "Civic Location Measurement Capability" */
|
||||
/* bits 36-39 reserved */
|
||||
);
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
antennaCapabilityEnabled,
|
||||
apChannelReportCapabilityEnabled,
|
||||
beaconActiveMeasurementCapabilityEnabled,
|
||||
beaconMeasurementReportingConditionsCapabilityEnabled,
|
||||
beaconPassiveMeasurementCapabilityEnabled,
|
||||
beaconTableMeasurementCapabilityEnabled,
|
||||
bssAvailableAdmissionCapacityCapabilityEnabled,
|
||||
bssAverageAccessDelayCapabilityEnabled,
|
||||
channelLoadMeasurementCapabilityEnabled,
|
||||
civicLocationMeasurementCapabilityEnabled,
|
||||
frameMeasurementCapabilityEnabled,
|
||||
ftmRangeReportCapabilityEnabled,
|
||||
lciAzimuthCapabilityEnabled,
|
||||
lciMeasurementCapabilityEnabled,
|
||||
linkMeasurementCapabilityEnabled,
|
||||
measurementPilotCapability,
|
||||
measurementPilotTransmissionInformationCapabilityEnabled,
|
||||
neighborReportCapabilityEnabled,
|
||||
neighborReportTsfOffsetCapabilityEnabled,
|
||||
noiseHistogramMeasurementCapabilityEnabled,
|
||||
nonoperatingChannelMaxMeasurementDuration,
|
||||
operatingChannelMaxMeasurementDuration,
|
||||
parallelMeasurementsCapabilityEnabled,
|
||||
rcpiMeasurementCapabilityEnabled,
|
||||
repeatedMeasurementsCapabilityEnabled,
|
||||
rmMibCapabilityEnabled,
|
||||
rsniMeasurementCapabilityEnabled,
|
||||
statisticsMeasurementCapabilityEnabled,
|
||||
transmitStreamCategoryMeasurementCapabilityEnabled,
|
||||
triggeredTransmitStreamCategoryMeasurementCapabilityEnabled
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
RMEnabledCapabilities other = (RMEnabledCapabilities) obj;
|
||||
return antennaCapabilityEnabled == other.antennaCapabilityEnabled &&
|
||||
apChannelReportCapabilityEnabled ==
|
||||
other.apChannelReportCapabilityEnabled &&
|
||||
beaconActiveMeasurementCapabilityEnabled ==
|
||||
other.beaconActiveMeasurementCapabilityEnabled &&
|
||||
beaconMeasurementReportingConditionsCapabilityEnabled ==
|
||||
other.beaconMeasurementReportingConditionsCapabilityEnabled &&
|
||||
beaconPassiveMeasurementCapabilityEnabled ==
|
||||
other.beaconPassiveMeasurementCapabilityEnabled &&
|
||||
beaconTableMeasurementCapabilityEnabled ==
|
||||
other.beaconTableMeasurementCapabilityEnabled &&
|
||||
bssAvailableAdmissionCapacityCapabilityEnabled ==
|
||||
other.bssAvailableAdmissionCapacityCapabilityEnabled &&
|
||||
bssAverageAccessDelayCapabilityEnabled ==
|
||||
other.bssAverageAccessDelayCapabilityEnabled &&
|
||||
channelLoadMeasurementCapabilityEnabled ==
|
||||
other.channelLoadMeasurementCapabilityEnabled &&
|
||||
civicLocationMeasurementCapabilityEnabled ==
|
||||
other.civicLocationMeasurementCapabilityEnabled &&
|
||||
frameMeasurementCapabilityEnabled ==
|
||||
other.frameMeasurementCapabilityEnabled &&
|
||||
ftmRangeReportCapabilityEnabled ==
|
||||
other.ftmRangeReportCapabilityEnabled &&
|
||||
lciAzimuthCapabilityEnabled == other.lciAzimuthCapabilityEnabled &&
|
||||
lciMeasurementCapabilityEnabled ==
|
||||
other.lciMeasurementCapabilityEnabled &&
|
||||
linkMeasurementCapabilityEnabled ==
|
||||
other.linkMeasurementCapabilityEnabled &&
|
||||
measurementPilotCapability == other.measurementPilotCapability &&
|
||||
measurementPilotTransmissionInformationCapabilityEnabled ==
|
||||
other.measurementPilotTransmissionInformationCapabilityEnabled &&
|
||||
neighborReportCapabilityEnabled ==
|
||||
other.neighborReportCapabilityEnabled &&
|
||||
neighborReportTsfOffsetCapabilityEnabled ==
|
||||
other.neighborReportTsfOffsetCapabilityEnabled &&
|
||||
noiseHistogramMeasurementCapabilityEnabled ==
|
||||
other.noiseHistogramMeasurementCapabilityEnabled &&
|
||||
nonoperatingChannelMaxMeasurementDuration ==
|
||||
other.nonoperatingChannelMaxMeasurementDuration &&
|
||||
operatingChannelMaxMeasurementDuration ==
|
||||
other.operatingChannelMaxMeasurementDuration &&
|
||||
parallelMeasurementsCapabilityEnabled ==
|
||||
other.parallelMeasurementsCapabilityEnabled &&
|
||||
rcpiMeasurementCapabilityEnabled ==
|
||||
other.rcpiMeasurementCapabilityEnabled &&
|
||||
repeatedMeasurementsCapabilityEnabled ==
|
||||
other.repeatedMeasurementsCapabilityEnabled &&
|
||||
rmMibCapabilityEnabled == other.rmMibCapabilityEnabled &&
|
||||
rsniMeasurementCapabilityEnabled ==
|
||||
other.rsniMeasurementCapabilityEnabled &&
|
||||
statisticsMeasurementCapabilityEnabled ==
|
||||
other.statisticsMeasurementCapabilityEnabled &&
|
||||
transmitStreamCategoryMeasurementCapabilityEnabled ==
|
||||
other.transmitStreamCategoryMeasurementCapabilityEnabled &&
|
||||
triggeredTransmitStreamCategoryMeasurementCapabilityEnabled ==
|
||||
other.triggeredTransmitStreamCategoryMeasurementCapabilityEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.IEUtils;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called
|
||||
* "Reduced Neighbor Report" in 802.11 specs (section 9.4.2.170). Refer to the
|
||||
* specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class ReducedNeighborReport {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 201;
|
||||
|
||||
/**
|
||||
* The Neighbor AP Information field specifies TBTT and other information
|
||||
* related to a group of neighbor APs on one channel.
|
||||
*/
|
||||
public static class NeighborApInformation {
|
||||
/**
|
||||
* Subfield for TBTT Information header
|
||||
*/
|
||||
public static class TbttInformationHeader {
|
||||
/**
|
||||
* Unsigned 2 bits - identifies, together with the TBTT Information Length
|
||||
* subfield, the format of the TBTT Information field
|
||||
*/
|
||||
public final byte tbttInformationType;
|
||||
/**
|
||||
* 1 bit - reserved except when the Reduced Neighbor Report element is
|
||||
* carried in a Probe Response frame transmitted by a TVHT AP
|
||||
*/
|
||||
public final boolean filteredNeighborAp;
|
||||
/**
|
||||
* Unsigned 4 bits - number of TBTT Information fields included in the TBTT
|
||||
* Information Set field of the Neighbor AP Information field, minus one
|
||||
*/
|
||||
public final byte tbttInformationCount;
|
||||
/**
|
||||
* Unsigned 8 bits - the length of each TBTT Information field included in
|
||||
* the TBTT Information Set field of the Neighbor AP Information field
|
||||
*/
|
||||
public final short tbttInformationLength;
|
||||
|
||||
/** Constructor */
|
||||
public TbttInformationHeader(
|
||||
byte tbttInformationType,
|
||||
boolean filteredNeighborAp,
|
||||
byte tbttInformationCount,
|
||||
short tbttInformationLength
|
||||
) {
|
||||
this.tbttInformationType = tbttInformationType;
|
||||
this.filteredNeighborAp = filteredNeighborAp;
|
||||
this.tbttInformationCount = tbttInformationCount;
|
||||
this.tbttInformationLength = tbttInformationLength;
|
||||
}
|
||||
|
||||
/** Parse TbttInformationHeader from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static TbttInformationHeader parse(JsonObject contents) {
|
||||
return new TbttInformationHeader(
|
||||
contents.get("TBTT Information Type").getAsByte(),
|
||||
contents.get("Filtered Neighbor Map").getAsBoolean(),
|
||||
contents.get("TBTT Information Count").getAsByte(),
|
||||
contents.get("TBTT Information Length").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
tbttInformationType,
|
||||
filteredNeighborAp,
|
||||
tbttInformationCount,
|
||||
tbttInformationLength
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TbttInformationHeader other = (TbttInformationHeader) obj;
|
||||
return tbttInformationType == other.tbttInformationType &&
|
||||
filteredNeighborAp == other.filteredNeighborAp &&
|
||||
tbttInformationCount == other.tbttInformationCount &&
|
||||
tbttInformationLength == other.tbttInformationLength;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subfield for TBTT Information
|
||||
*/
|
||||
public static class TbttInformation {
|
||||
/**
|
||||
* Unsigned 8 bits - offset in TUs, rounded down to nearest TU, to the next
|
||||
* TBTT of an AP’s BSS from the immediately prior TBTT of the AP that
|
||||
* transmits this element
|
||||
*/
|
||||
public final short neighborApTbttOffset;
|
||||
/** BSSID of neighbor, optional */
|
||||
public final String bssid;
|
||||
/** Short SSID of neighbor, optional */
|
||||
public final String shortSsid;
|
||||
|
||||
/** Constructor */
|
||||
public TbttInformation(
|
||||
short neighborApTbttOffset,
|
||||
String bssid,
|
||||
String shortSsid
|
||||
) {
|
||||
this.neighborApTbttOffset = neighborApTbttOffset;
|
||||
this.bssid = bssid;
|
||||
this.shortSsid = shortSsid;
|
||||
}
|
||||
|
||||
/** Parse TbttInformation from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static TbttInformation parse(JsonObject contents) {
|
||||
return new TbttInformation(
|
||||
contents.get("Neighbor AP TBTT Offset").getAsShort(),
|
||||
IEUtils.parseOptionalStringField(contents, "BSSID"),
|
||||
IEUtils.parseOptionalStringField(contents, "Short SSID")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(neighborApTbttOffset, bssid, shortSsid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TbttInformation other = (TbttInformation) obj;
|
||||
return neighborApTbttOffset ==
|
||||
other.neighborApTbttOffset && bssid.equals(other.bssid) &&
|
||||
Objects.equals(shortSsid, other.shortSsid);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see TbttInformationHeader
|
||||
*/
|
||||
public final TbttInformationHeader tbttInformationHeader;
|
||||
/**
|
||||
* Unsigned 8 bits - channel starting frequency that, together with the
|
||||
* Channel Number field, indicates the primary channel of the BSSs of the APs
|
||||
* in this Neighbor AP Information field
|
||||
*/
|
||||
public final short operatingClass;
|
||||
/**
|
||||
* Unsigned 8 bits - the last known primary channel of the APs in this
|
||||
* Neighbor AP Information field.
|
||||
*/
|
||||
public final short channelNumber;
|
||||
/**
|
||||
* @see TbttInformation
|
||||
*/
|
||||
public final TbttInformation tbttInformation;
|
||||
|
||||
/** Constructor */
|
||||
public NeighborApInformation(
|
||||
TbttInformationHeader tbttInformationHeader,
|
||||
short operatingClass,
|
||||
short channelNumber,
|
||||
TbttInformation tbttInformation
|
||||
) {
|
||||
this.tbttInformationHeader = tbttInformationHeader;
|
||||
this.operatingClass = operatingClass;
|
||||
this.channelNumber = channelNumber;
|
||||
this.tbttInformation = tbttInformation;
|
||||
}
|
||||
|
||||
/** Parse NeighborApInformation from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static NeighborApInformation parse(JsonObject contents) {
|
||||
return new NeighborApInformation(
|
||||
TbttInformationHeader.parse(
|
||||
contents.get("TBTT Information Header").getAsJsonObject()
|
||||
),
|
||||
contents.get("Operating Class").getAsShort(),
|
||||
contents.get("Channel Number").getAsShort(),
|
||||
TbttInformation.parse(contents.get("TBTT Information").getAsJsonObject())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
tbttInformationHeader,
|
||||
operatingClass,
|
||||
channelNumber,
|
||||
tbttInformation
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NeighborApInformation other = (NeighborApInformation) obj;
|
||||
return tbttInformationHeader.equals(other.tbttInformationHeader) &&
|
||||
operatingClass == other.operatingClass &&
|
||||
channelNumber == other.channelNumber &&
|
||||
Objects.equals(tbttInformation, other.tbttInformation);
|
||||
}
|
||||
}
|
||||
|
||||
/** number of channels in a subband of supported channels */
|
||||
public final List<NeighborApInformation> neighborApInformations;
|
||||
|
||||
/** Constructor */
|
||||
public ReducedNeighborReport(
|
||||
List<NeighborApInformation> neighborApInformations
|
||||
) {
|
||||
this.neighborApInformations =
|
||||
Collections.unmodifiableList(neighborApInformations);
|
||||
}
|
||||
|
||||
/** Parse ReducedNeighborReport from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static ReducedNeighborReport parse(JsonObject contents) {
|
||||
List<NeighborApInformation> neighborApInformations = new ArrayList<>();
|
||||
|
||||
JsonElement neighborApInformationsObject =
|
||||
contents.get("Neighbor AP Informations");
|
||||
if (neighborApInformationsObject != null) {
|
||||
for (
|
||||
JsonElement elem : neighborApInformationsObject.getAsJsonArray()
|
||||
) {
|
||||
neighborApInformations
|
||||
.add(NeighborApInformation.parse(elem.getAsJsonObject()));
|
||||
}
|
||||
}
|
||||
|
||||
return new ReducedNeighborReport(neighborApInformations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(neighborApInformations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ReducedNeighborReport other = (ReducedNeighborReport) obj;
|
||||
return neighborApInformations.equals(other.neighborApInformations);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
// NOTE: Not validated (not seen on test devices)
|
||||
/**
|
||||
* This information element (IE) appears in wifiscan entries. It's called
|
||||
* "Supported Channels" in 802.11 specs (section 9.4.2.17). Refer to the
|
||||
* specification for more details. Language in javadocs is taken from the
|
||||
* specification.
|
||||
*/
|
||||
public class SupportedChannels {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 36;
|
||||
|
||||
/** Unsigned 8 bits - first channel in a subband of supported channels */
|
||||
public final short firstChannelNumber;
|
||||
/** Unsigned 8 bits - number of channels in a subband of supported channels */
|
||||
public final short numberOfChannels;
|
||||
|
||||
/** Constructor */
|
||||
public SupportedChannels(short firstChannelNumber, short numberOfChannels) {
|
||||
this.firstChannelNumber = firstChannelNumber;
|
||||
this.numberOfChannels = numberOfChannels;
|
||||
}
|
||||
|
||||
/** Parse SupportedChannels from JSON object */
|
||||
// TODO modify this method as necessary - since the IE doesn't seem to be
|
||||
// present, we have no idea what the format looks like
|
||||
public static SupportedChannels parse(JsonObject contents) {
|
||||
return new SupportedChannels(
|
||||
contents.get("First Channel Number").getAsShort(),
|
||||
contents.get("Number of Channels").getAsShort()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(firstChannelNumber, numberOfChannels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SupportedChannels other = (SupportedChannels) obj;
|
||||
return firstChannelNumber == other.firstChannelNumber &&
|
||||
numberOfChannels == other.numberOfChannels;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.facebook.openwifi.cloudsdk.IEUtils;
|
||||
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 (section 9.4.2.161). Refer to the specification for more details. Language in
|
||||
* javadocs is taken from the specification.
|
||||
*/
|
||||
public class TxPwrInfo {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 195;
|
||||
|
||||
/**
|
||||
* Unsigned 8 bits - Local maximum transmit power for 20 MHz. Required field.
|
||||
*/
|
||||
public final Short localMaxTxPwrConstraint20MHz;
|
||||
/**
|
||||
* Unsigned 8 bits - Local maximum transmit power for 40 MHz. Optional field.
|
||||
*/
|
||||
public final Short localMaxTxPwrConstraint40MHz;
|
||||
/**
|
||||
* Unsigned 8 bits - Local maximum transmit power for 80 MHz. Optional field.
|
||||
*/
|
||||
public final Short localMaxTxPwrConstraint80MHz;
|
||||
/**
|
||||
* Unsigned 8 bits - Local maximum transmit power for both 160 MHz and 80+80 MHz. Optional field.
|
||||
*/
|
||||
public final Short localMaxTxPwrConstraint160MHz;
|
||||
|
||||
/** Constructor */
|
||||
public TxPwrInfo(
|
||||
short localMaxTxPwrConstraint20MHz,
|
||||
Short localMaxTxPwrConstraint40MHz,
|
||||
Short localMaxTxPwrConstraint80MHz,
|
||||
Short 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) {
|
||||
JsonObject innerObj = contents.get("Tx Pwr Info").getAsJsonObject();
|
||||
// required field
|
||||
short localMaxTxPwrConstraint20MHz =
|
||||
innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsShort();
|
||||
// optional field
|
||||
Short localMaxTxPwrConstraint40MHz =
|
||||
IEUtils.parseOptionalShortField(
|
||||
innerObj,
|
||||
"Local Max Tx Pwr Constraint 40MHz"
|
||||
);
|
||||
Short localMaxTxPwrConstraint80MHz =
|
||||
IEUtils.parseOptionalShortField(
|
||||
innerObj,
|
||||
"Local Max Tx Pwr Constraint 40MHz"
|
||||
);
|
||||
Short localMaxTxPwrConstraint160MHz =
|
||||
IEUtils.parseOptionalShortField(
|
||||
innerObj,
|
||||
"Local Max Tx Pwr Constraint 40MHz"
|
||||
);
|
||||
return new TxPwrInfo(
|
||||
localMaxTxPwrConstraint20MHz,
|
||||
localMaxTxPwrConstraint40MHz,
|
||||
localMaxTxPwrConstraint80MHz,
|
||||
localMaxTxPwrConstraint160MHz
|
||||
);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.operationelement;
|
||||
package com.facebook.openwifi.cloudsdk.ies;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
@@ -15,9 +15,12 @@ import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
/**
|
||||
* Very High Throughput (VHT) Operation Element, which is potentially present in
|
||||
* wifiscan entries. Introduced in 802.11ac (2013).
|
||||
* wifiscan entries. Introduced in 802.11ac (2013). Refer to the 802.11
|
||||
* specification (section 9.4.2.158)
|
||||
*/
|
||||
public class VHTOperationElement {
|
||||
public class VHTOperation {
|
||||
/** Defined in 802.11 table 9-92 */
|
||||
public static final int TYPE = 192;
|
||||
|
||||
/**
|
||||
* This field is 0 if the channel width is 20 MHz or 40 MHz, and 1 otherwise.
|
||||
@@ -30,15 +33,23 @@ public class VHTOperationElement {
|
||||
* 160 MHz wide channel, this parameter is the channel number of the 80MHz
|
||||
* channel that contains the primary channel. For a 80+80 MHz wide channel, this
|
||||
* parameter is the channel number of the primary channel.
|
||||
* <p>
|
||||
* This field is an unsigned byte in the specification (i.e., with values
|
||||
* between 0 and 255). But because Java only supports signed bytes, a short
|
||||
* data type is used to store the value.
|
||||
*/
|
||||
public final byte channel1;
|
||||
public final short channel1;
|
||||
/**
|
||||
* This should be zero unless the channel is 160MHz or 80+80 MHz wide. If the
|
||||
* channel is 160 MHz wide, this parameter is the channel number of the 160 MHz
|
||||
* wide channel. If the channel is 80+80 MHz wide, this parameter is the channel
|
||||
* index of the secondary 80 MHz wide channel.
|
||||
* <p>
|
||||
* This field is an unsigned byte in the specification (i.e., with values
|
||||
* between 0 and 255). But because Java only supports signed bytes, a short
|
||||
* data type is used to store the value.
|
||||
*/
|
||||
public final byte channel2;
|
||||
public final short channel2;
|
||||
/**
|
||||
* An 8-element array where each element is between 0 and 4 inclusive. MCS means
|
||||
* Modulation and Coding Scheme. NSS means Number of Spatial Streams. There can
|
||||
@@ -57,11 +68,11 @@ public class VHTOperationElement {
|
||||
* @param vhtOper a base64 encoded properly formatted VHT operation element (see
|
||||
* 802.11 standard)
|
||||
*/
|
||||
public VHTOperationElement(String vhtOper) {
|
||||
public VHTOperation(String vhtOper) {
|
||||
byte[] bytes = Base64.decodeBase64(vhtOper);
|
||||
this.channelWidth = bytes[0];
|
||||
this.channel1 = bytes[1];
|
||||
this.channel2 = bytes[2];
|
||||
this.channel1 = (short) (bytes[1] & 0xff); // read as unsigned value
|
||||
this.channel2 = (short) (bytes[2] & 0xff); // read as unsigned value
|
||||
byte[] vhtMcsForNss = new byte[8];
|
||||
vhtMcsForNss[0] = (byte) (bytes[3] >>> 6);
|
||||
vhtMcsForNss[1] = (byte) ((bytes[3] & 0b00110000) >>> 4);
|
||||
@@ -81,10 +92,10 @@ public class VHTOperationElement {
|
||||
* For details about the parameters, see the javadocs for the corresponding
|
||||
* member variables.
|
||||
*/
|
||||
public VHTOperationElement(
|
||||
public VHTOperation(
|
||||
byte channelWidth,
|
||||
byte channel1,
|
||||
byte channel2,
|
||||
short channel1,
|
||||
short channel2,
|
||||
byte[] vhtMcsForNss
|
||||
) {
|
||||
/*
|
||||
@@ -106,7 +117,7 @@ public class VHTOperationElement {
|
||||
* @return true if the the operation elements "match" for the purpose of
|
||||
* aggregating statistics; false otherwise.
|
||||
*/
|
||||
public boolean matchesForAggregation(VHTOperationElement other) {
|
||||
public boolean matchesForAggregation(VHTOperation other) {
|
||||
// check everything except vhtMcsForNss
|
||||
return other != null && channel1 == other.channel1 &&
|
||||
channel2 == other.channel2 && channelWidth == other.channelWidth;
|
||||
@@ -134,8 +145,8 @@ public class VHTOperationElement {
|
||||
if (vhtOper1 == null || vhtOper2 == null) {
|
||||
return false; // false if exactly one is null
|
||||
}
|
||||
VHTOperationElement vhtOperObj1 = new VHTOperationElement(vhtOper1);
|
||||
VHTOperationElement vhtOperObj2 = new VHTOperationElement(vhtOper2);
|
||||
VHTOperation vhtOperObj1 = new VHTOperation(vhtOper1);
|
||||
VHTOperation vhtOperObj2 = new VHTOperation(vhtOper2);
|
||||
return vhtOperObj1.matchesForAggregation(vhtOperObj2);
|
||||
}
|
||||
|
||||
@@ -160,9 +171,9 @@ public class VHTOperationElement {
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
VHTOperationElement other = (VHTOperationElement) obj;
|
||||
VHTOperation other = (VHTOperation) obj;
|
||||
return channel1 == other.channel1 && channel2 == other.channel2 &&
|
||||
channelWidth == other.channelWidth &&
|
||||
Arrays.equals(vhtMcsForNss, other.vhtMcsForNss);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information elements (IEs) defined in the 802.11 specifications.
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.ies;
|
||||
@@ -6,13 +6,13 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk.kafka;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.kafka.common.errors.WakeupException;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
|
||||
|
||||
/**
|
||||
* Kafka runner.
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk.kafka;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
@@ -29,7 +29,8 @@ import org.apache.kafka.common.serialization.StringDeserializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
|
||||
import com.facebook.openwifi.cloudsdk.UCentralClient;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@@ -69,7 +70,12 @@ public class UCentralKafkaConsumer {
|
||||
/** The state payload JSON. */
|
||||
public final JsonObject payload;
|
||||
|
||||
/** Unix time (ms). */
|
||||
/**
|
||||
* The record timestamp (Unix time, in ms).
|
||||
*
|
||||
* Depending on the broker configuration for "message.timestamp.type",
|
||||
* this may either be the "CreateTime" or "LogAppendTime".
|
||||
*/
|
||||
public final long timestampMs;
|
||||
|
||||
/** Constructor. */
|
||||
@@ -84,7 +90,12 @@ public class UCentralKafkaConsumer {
|
||||
}
|
||||
}
|
||||
|
||||
/** Kafka record listener interface. */
|
||||
/**
|
||||
* Kafka record listener interface.
|
||||
*
|
||||
* The inputs must NOT be mutated, as they may be passed to multiple
|
||||
* listeners and may result in ConcurrentModificationException.
|
||||
*/
|
||||
public interface KafkaListener {
|
||||
/** Handle a list of state records. */
|
||||
void handleStateRecords(List<KafkaRecord> records);
|
||||
@@ -270,7 +281,6 @@ public class UCentralKafkaConsumer {
|
||||
serialNumber,
|
||||
payload.toString()
|
||||
);
|
||||
// record.timestamp() is empirically confirmed to be Unix time (ms)
|
||||
KafkaRecord kafkaRecord =
|
||||
new KafkaRecord(serialNumber, payload, record.timestamp());
|
||||
if (record.topic().equals(stateTopic)) {
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral;
|
||||
package com.facebook.openwifi.cloudsdk.kafka;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.apache.kafka.common.serialization.StringSerializer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.ServiceEvent;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.ServiceEvent;
|
||||
import com.google.gson.Gson;
|
||||
|
||||
/**
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Kafka consumer and producer functionality required by the CloudSDK.
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.kafka;
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.models.ap;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* AP capabilities schema.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main/system/capabilities.uc">capabilities.uc</a>
|
||||
*/
|
||||
public class Capabilities {
|
||||
public String compatible;
|
||||
public String model;
|
||||
public String platform;
|
||||
public Map<String, List<String>> network;
|
||||
|
||||
public static class Switch {
|
||||
public boolean enable;
|
||||
public boolean reset;
|
||||
}
|
||||
|
||||
@SerializedName("switch") public Map<String, Switch> switch_;
|
||||
|
||||
public static class Phy {
|
||||
public int tx_ant;
|
||||
public int rx_ant;
|
||||
public int[] frequencies;
|
||||
public int[] channels;
|
||||
public int[] dfs_channels;
|
||||
public String[] htmode;
|
||||
public String[] band;
|
||||
public int ht_capa;
|
||||
public int vht_capa;
|
||||
public int[] he_phy_capa;
|
||||
public int[] he_mac_capa;
|
||||
public String country;
|
||||
public String dfs_region;
|
||||
public int temperature;
|
||||
}
|
||||
|
||||
public Map<String, Phy> wifi;
|
||||
// TODO The fields below were omitted
|
||||
// macaddr;
|
||||
// country_code;
|
||||
// label_macaddr;
|
||||
}
|
||||
@@ -6,14 +6,19 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.ap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* AP statistics/telemetry schema.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main/state/state.yml">state.yml</a>
|
||||
*/
|
||||
public class State {
|
||||
public class Interface {
|
||||
public class Client {
|
||||
public static class Interface {
|
||||
public static class Client {
|
||||
public String mac;
|
||||
public String[] ipv4_addresses;
|
||||
public String[] ipv6_addresses;
|
||||
@@ -21,9 +26,9 @@ public class State {
|
||||
// TODO last_seen
|
||||
}
|
||||
|
||||
public class SSID {
|
||||
public class Association {
|
||||
public class Rate {
|
||||
public static class SSID {
|
||||
public static class Association {
|
||||
public static class Rate {
|
||||
public long bitrate;
|
||||
public int chwidth;
|
||||
public boolean sgi;
|
||||
@@ -54,6 +59,8 @@ public class State {
|
||||
public int ack_signal;
|
||||
public int ack_signal_avg;
|
||||
public JsonObject[] tid_stats; // TODO: see cfg80211_tid_stats
|
||||
|
||||
// TODO ipaddr_v4 - either string or object (ip4leases), but duplicated in "clients"
|
||||
}
|
||||
|
||||
public Association[] associations;
|
||||
@@ -66,7 +73,7 @@ public class State {
|
||||
public JsonObject radio;
|
||||
}
|
||||
|
||||
public class Counters {
|
||||
public static class Counters {
|
||||
public long collisions;
|
||||
public long multicast;
|
||||
public long rx_bytes;
|
||||
@@ -96,8 +103,8 @@ public class State {
|
||||
|
||||
public Interface[] interfaces;
|
||||
|
||||
public class Unit {
|
||||
public class Memory {
|
||||
public static class Unit {
|
||||
public static class Memory {
|
||||
public long buffered;
|
||||
public long cached;
|
||||
public long free;
|
||||
@@ -112,8 +119,21 @@ public class State {
|
||||
|
||||
public Unit unit;
|
||||
|
||||
public static class Radio {
|
||||
public long active_ms;
|
||||
public long busy_ms;
|
||||
public int channel; // TODO might be int[] array??
|
||||
public String channel_width;
|
||||
public long noise;
|
||||
public String phy;
|
||||
public long receive_ms;
|
||||
public long transmit_ms;
|
||||
public int tx_power;
|
||||
}
|
||||
|
||||
public Radio[] radios;
|
||||
|
||||
// TODO
|
||||
public JsonObject[] radios;
|
||||
@SerializedName("link-state") public JsonObject linkState;
|
||||
public JsonObject gps;
|
||||
public JsonObject poe;
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.models.ap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* AP configuration schema.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main/schema/ucentral.yml">ucentral.yml</a>
|
||||
*/
|
||||
public class UCentralSchema {
|
||||
public static class Radio {
|
||||
public String band;
|
||||
public int bandwidth;
|
||||
public JsonPrimitive channel; // either "auto" or int
|
||||
@SerializedName("valid-channels") public int[] validChannels;
|
||||
public String country;
|
||||
@SerializedName("allow-dfs") public boolean allowDfs;
|
||||
@SerializedName("channel-mode") public String channelMode;
|
||||
@SerializedName("channel-width") public int channelWidth;
|
||||
@SerializedName("require-mode") public String requireMode;
|
||||
public String mimo;
|
||||
@SerializedName("tx-power") public int txPower;
|
||||
@SerializedName("legacy-rates") public boolean legacyRates;
|
||||
@SerializedName("beacon-interval") public int beaconInterval;
|
||||
@SerializedName("dtim-period") public int dtimPeriod;
|
||||
@SerializedName("maximum-clients") public int maximumClients;
|
||||
|
||||
public static class Rates {
|
||||
public int beacon;
|
||||
public int multicast;
|
||||
}
|
||||
|
||||
public Rates rates;
|
||||
|
||||
public static class HESettings {
|
||||
@SerializedName("multiple-bssid") public boolean multipleBssid;
|
||||
public boolean ema;
|
||||
@SerializedName("bss-color") public int bssColor;
|
||||
}
|
||||
|
||||
@SerializedName("he-settings") public HESettings heSettings;
|
||||
|
||||
@SerializedName("hostapd-iface-raw") public String[] hostapdIfaceRaw;
|
||||
}
|
||||
|
||||
public List<Radio> radios;
|
||||
|
||||
public static class Metrics {
|
||||
public static class Statistics {
|
||||
public int interval;
|
||||
public List<String> types;
|
||||
}
|
||||
|
||||
public Statistics statistics;
|
||||
|
||||
public static class Health {
|
||||
public int interval;
|
||||
}
|
||||
|
||||
public Health health;
|
||||
|
||||
public static class WifiFrames {
|
||||
public List<String> filters;
|
||||
}
|
||||
|
||||
@SerializedName("wifi-frames") public WifiFrames wifiFrames;
|
||||
|
||||
public static class DhcpSnooping {
|
||||
public List<String> filters;
|
||||
}
|
||||
|
||||
@SerializedName("dhcp-snooping") public DhcpSnooping dhcpSnooping;
|
||||
}
|
||||
|
||||
public Metrics metrics;
|
||||
|
||||
// TODO also add fields below as needed
|
||||
// unit
|
||||
// globals
|
||||
// definitions
|
||||
// ethernet
|
||||
// switch
|
||||
// interfaces
|
||||
// services
|
||||
}
|
||||
@@ -6,13 +6,20 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.ap;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
|
||||
|
||||
/** Represents a single entry in wifi scan results. */
|
||||
/**
|
||||
* Wi-Fi scan result schema.
|
||||
* <p>
|
||||
* Note that the {@code ies[]} array is not stored here, but parsed into
|
||||
* {@link WifiScanEntry#ieContainer}.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-ucentral-schema/blob/main/command/cmd_wifiscan.uc">cmd_wifiscan.uc</a>
|
||||
*/
|
||||
public class WifiScanEntryResult {
|
||||
public int channel;
|
||||
public long last_seen;
|
||||
@@ -50,8 +57,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 +73,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 +83,6 @@ public class WifiScanEntryResult {
|
||||
channel,
|
||||
frequency,
|
||||
ht_oper,
|
||||
ies,
|
||||
last_seen,
|
||||
signal,
|
||||
ssid,
|
||||
@@ -104,7 +107,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
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schemas originating from the AP-NOS (wlan-ap).
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-ucentral-schema">wlan-ucentral-schema</a>
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.models.ap;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class AclTemplate {
|
||||
public boolean Read;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.facebook.openwifi.cloudsdk.models.ap.Capabilities;
|
||||
|
||||
public class DeviceCapabilities {
|
||||
public JsonObject capabilities;
|
||||
public Capabilities capabilities;
|
||||
public long firstUpdate;
|
||||
public long lastUpdate;
|
||||
public String serialNumber;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class DeviceConfigureRequest {
|
||||
public String serialNumber;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,6 +6,6 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public enum DeviceType { AP, SWITCH, IOT, MESH }
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class MfaAuthInfo {
|
||||
public boolean enabled;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class MobilePhoneNumber {
|
||||
public String number;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class NoteInfo {
|
||||
public long created;
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class ScriptRequest {
|
||||
public String serialNumber;
|
||||
public long timeout = 30; // in seconds
|
||||
public String type; // "shell", "ucode", "uci"
|
||||
public String script;
|
||||
public String scriptId; // required but unused?
|
||||
public long when = 0;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class ServiceEvent {
|
||||
public static final String EVENT_JOIN = "join";
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class TokenValidationResult {
|
||||
public UserInfo userInfo;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public enum VerifiedCertificate {
|
||||
NO_CERTIFICATE, VALID_CERTIFICATE, MISMATCH_SERIAL, VERIFIED
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class WebTokenAclTemplate {
|
||||
public AclTemplate aclTemplate;
|
||||
@@ -6,13 +6,13 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
public class WebTokenResult {
|
||||
public String access_token;
|
||||
public String refresh_token;
|
||||
public String token_type;
|
||||
public int expires_in;
|
||||
public long expires_in;
|
||||
public int idle_timeout;
|
||||
public String username;
|
||||
public long created;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.gw.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schemas defined in the uCentral Gateway.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-cloud-ucentralgw">wlan-cloud-ucentralgw</a>
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.models.gw;
|
||||
@@ -6,14 +6,14 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
|
||||
|
||||
public class DeviceConfiguration {
|
||||
public class DeviceConfigurationElement {
|
||||
public static class DeviceConfigurationElement {
|
||||
public String name;
|
||||
public String description;
|
||||
public Integer weight;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
public class DeviceRules {
|
||||
public String rcOnly;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
public class DiGraphEntry {
|
||||
public String parent;
|
||||
@@ -6,11 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
|
||||
|
||||
public class Entity {
|
||||
// from ObjectInfo
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
|
||||
|
||||
public class InventoryTag {
|
||||
// from ObjectInfo
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
public class RRMAlgorithmDetails {
|
||||
public String name;
|
||||
@@ -6,12 +6,12 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RRMDetails {
|
||||
public class RRMDetailsImpl {
|
||||
public static class RRMDetailsImpl {
|
||||
public String vendor;
|
||||
public String schedule;
|
||||
public List<RRMAlgorithmDetails> algorithms;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.facebook.openwifirrm.ucentral.gw.models.NoteInfo;
|
||||
import com.facebook.openwifi.cloudsdk.models.gw.NoteInfo;
|
||||
|
||||
public class Venue {
|
||||
// from ObjectInfo
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schemas defined in the Provisioning service.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-cloud-owprov">wlan-cloud-owprov</a>
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.models.prov;
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.rrm.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov.rrm;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.prov.rrm.models;
|
||||
package com.facebook.openwifi.cloudsdk.models.prov.rrm;
|
||||
|
||||
public class Provider {
|
||||
public String vendor;
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Schemas defined in the Provisioning service specific to RRM.
|
||||
*
|
||||
* @see <a href="https://github.com/Telecominfraproject/wlan-cloud-owprov/blob/main/openapi/rrm_provider.yaml">rrm_provider.yaml</a>
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk.models.prov.rrm;
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Library providing clients and models for the OpenWiFi uCentral-based
|
||||
* CloudSDK.
|
||||
*/
|
||||
package com.facebook.openwifi.cloudsdk;
|
||||
6
lib-cloudsdk/src/main/resources/log4j.properties
Normal file
6
lib-cloudsdk/src/main/resources/log4j.properties
Normal file
@@ -0,0 +1,6 @@
|
||||
log4j.rootLogger=DEBUG, stdout
|
||||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd'T'HH:mm:ss.SSS} %-5p [%c{1}:%L] - %m%n
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.openwifi.cloudsdk;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class UCentralUtilsTest {
|
||||
@Test
|
||||
void test_placeholder() throws Exception {
|
||||
assertEquals(3, 1 + 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void test_setRadioConfigFieldChannel() throws Exception {
|
||||
final String serialNumber = "aaaaaaaaaaaa";
|
||||
final int expectedChannel = 1;
|
||||
final Map<String, Integer> newValueList = Collections
|
||||
.singletonMap(UCentralConstants.BAND_5G, expectedChannel);
|
||||
|
||||
// test case where channel value is a string and not an integer
|
||||
UCentralApConfiguration config = new UCentralApConfiguration(
|
||||
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": \"auto\"}]}"
|
||||
);
|
||||
boolean modified = UCentralUtils
|
||||
.setRadioConfigField(serialNumber, config, "channel", newValueList);
|
||||
assertTrue(modified);
|
||||
assertEquals(
|
||||
config.getRadioConfig(0).get("channel").getAsInt(),
|
||||
expectedChannel
|
||||
);
|
||||
|
||||
// field doesn't exist
|
||||
config = new UCentralApConfiguration(
|
||||
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\"}]}"
|
||||
);
|
||||
modified = UCentralUtils
|
||||
.setRadioConfigField(serialNumber, config, "channel", newValueList);
|
||||
assertTrue(modified);
|
||||
assertEquals(
|
||||
config.getRadioConfig(0).get("channel").getAsInt(),
|
||||
expectedChannel
|
||||
);
|
||||
|
||||
// normal field, not modified
|
||||
config = new UCentralApConfiguration(
|
||||
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": 1}]}"
|
||||
);
|
||||
modified = UCentralUtils
|
||||
.setRadioConfigField(serialNumber, config, "channel", newValueList);
|
||||
assertFalse(modified);
|
||||
assertEquals(
|
||||
config.getRadioConfig(0).get("channel").getAsInt(),
|
||||
expectedChannel
|
||||
);
|
||||
|
||||
// normal field, modified
|
||||
config = new UCentralApConfiguration(
|
||||
"{\"interfaces\": [], \"radios\": [{\"band\": \"5G\", \"channel\": 15}]}"
|
||||
);
|
||||
modified = UCentralUtils
|
||||
.setRadioConfigField(serialNumber, config, "channel", newValueList);
|
||||
assertTrue(modified);
|
||||
assertEquals(
|
||||
config.getRadioConfig(0).get("channel").getAsInt(),
|
||||
expectedChannel
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,17 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.operationelement;
|
||||
package com.facebook.openwifi.cloudsdk.ies;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class HTOperationElementTest {
|
||||
public class HTOperationTest {
|
||||
@Test
|
||||
void testGetHtOper() {
|
||||
String htOper = "AQAEAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
HTOperationElement htOperObj = new HTOperationElement(htOper);
|
||||
HTOperation htOperObj = new HTOperation(htOper);
|
||||
byte expectedPrimaryChannel = 1;
|
||||
byte expectedSecondaryChannelOffset = 0;
|
||||
boolean expectedStaChannelWidth = false;
|
||||
@@ -28,7 +28,7 @@ public class HTOperationElementTest {
|
||||
boolean expectedDualBeacon = false;
|
||||
boolean expectedDualCtsProtection = false;
|
||||
boolean expectedStbcBeacon = false;
|
||||
HTOperationElement expectedHtOperObj = new HTOperationElement(
|
||||
HTOperation expectedHtOperObj = new HTOperation(
|
||||
expectedPrimaryChannel,
|
||||
expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth,
|
||||
@@ -44,11 +44,11 @@ public class HTOperationElementTest {
|
||||
assertEquals(expectedHtOperObj, htOperObj);
|
||||
|
||||
htOper = "JAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==";
|
||||
htOperObj = new HTOperationElement(htOper);
|
||||
htOperObj = new HTOperation(htOper);
|
||||
// all fields except the primary channel and nongreenfield field are the same
|
||||
expectedPrimaryChannel = 36;
|
||||
expectedNongreenfieldHtStasPresent = false;
|
||||
expectedHtOperObj = new HTOperationElement(
|
||||
expectedHtOperObj = new HTOperation(
|
||||
expectedPrimaryChannel,
|
||||
expectedSecondaryChannelOffset,
|
||||
expectedStaChannelWidth,
|
||||
@@ -6,23 +6,23 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.openwifirrm.ucentral.operationelement;
|
||||
package com.facebook.openwifi.cloudsdk.ies;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class VHTOperationElementTest {
|
||||
public class VHTOperationTest {
|
||||
|
||||
@Test
|
||||
void testGetVhtOper() {
|
||||
String vhtOper = "ACQAAAA=";
|
||||
VHTOperationElement vhtOperObj = new VHTOperationElement(vhtOper);
|
||||
VHTOperation vhtOperObj = new VHTOperation(vhtOper);
|
||||
byte expectedChannelWidthIndicator = 0; // 20 MHz channel width
|
||||
byte expectedChannel1 = 36;
|
||||
byte expectedChannel2 = 0;
|
||||
short expectedChannel1 = 36;
|
||||
short expectedChannel2 = 0;
|
||||
byte[] expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
VHTOperationElement expectedVhtOperObj = new VHTOperationElement(
|
||||
VHTOperation expectedVhtOperObj = new VHTOperation(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
@@ -31,12 +31,12 @@ public class VHTOperationElementTest {
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
|
||||
vhtOper = "AToAUAE=";
|
||||
vhtOperObj = new VHTOperationElement(vhtOper);
|
||||
vhtOperObj = new VHTOperation(vhtOper);
|
||||
expectedChannelWidthIndicator = 1; // 80 MHz channel width
|
||||
expectedChannel1 = 58;
|
||||
// same channel2
|
||||
expectedVhtMcsForNss = new byte[] { 1, 1, 0, 0, 0, 0, 0, 1 };
|
||||
expectedVhtOperObj = new VHTOperationElement(
|
||||
expectedVhtOperObj = new VHTOperation(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
@@ -45,12 +45,27 @@ public class VHTOperationElementTest {
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
|
||||
vhtOper = "ASoyUAE=";
|
||||
vhtOperObj = new VHTOperationElement(vhtOper);
|
||||
vhtOperObj = new VHTOperation(vhtOper);
|
||||
// same channel width indicator (160 MHz channel width)
|
||||
expectedChannel1 = 42;
|
||||
expectedChannel2 = 50;
|
||||
// same vhtMcsForNss
|
||||
expectedVhtOperObj = new VHTOperationElement(
|
||||
expectedVhtOperObj = new VHTOperation(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
expectedVhtMcsForNss
|
||||
);
|
||||
assertEquals(expectedVhtOperObj, vhtOperObj);
|
||||
|
||||
// test with channel number >= 128 (channel fields should be unsigned)
|
||||
vhtOper = "AJUAAAA=";
|
||||
vhtOperObj = new VHTOperation(vhtOper);
|
||||
expectedChannelWidthIndicator = 0;
|
||||
expectedChannel1 = 149;
|
||||
expectedChannel2 = 0;
|
||||
expectedVhtMcsForNss = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
expectedVhtOperObj = new VHTOperation(
|
||||
expectedChannelWidthIndicator,
|
||||
expectedChannel1,
|
||||
expectedChannel2,
|
||||
3
lib-rca/README.md
Normal file
3
lib-rca/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Root Cause Analysis (RCA) Java Library
|
||||
A Java library which analyzes statistics and provides root cause analysis (RCA) for clients.
|
||||
This is a work in progress.
|
||||
72
lib-rca/pom.xml
Normal file
72
lib-rca/pom.xml
Normal file
@@ -0,0 +1,72 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>openwifi-librca</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<parent>
|
||||
<groupId>com.facebook</groupId>
|
||||
<artifactId>openwifi-base</artifactId>
|
||||
<version>2.7.0</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<!-- Hack for static files located in root project -->
|
||||
<myproject.root>${project.basedir}/..</myproject.root>
|
||||
</properties>
|
||||
<build>
|
||||
<finalName>openwifi-librca</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.diffplug.spotless</groupId>
|
||||
<artifactId>spotless-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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.openwifi.librca;
|
||||
|
||||
public class RootCauseAnalyzer {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* 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.openwifi.librca.inputs;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Define root cause analysis configuration parameters */
|
||||
public final class RCAParams {
|
||||
// Note: we expect to receive these parameters in json format, so for now
|
||||
// we do not include a constructor which takes in the member vars as inputs
|
||||
|
||||
/** Look-back window in ms */
|
||||
public final int detectionWindowMs;
|
||||
|
||||
// KPI calculation parameters
|
||||
/** Minimum acceptable estimated throughput (Mbps) */
|
||||
public final double minEstimatedThroughputMbps;
|
||||
/** Percentile (units are %) of estimated throughputs to use as the KPI */
|
||||
public final double throughputAggregationPercentile;
|
||||
/** Maximum acceptable latency (ms) */
|
||||
public final int maxLatencyThresholdMs;
|
||||
/** Maximum acceptable jitter (ms) */
|
||||
public final int maxJitterThresholdMs;
|
||||
/**
|
||||
* Maximum acceptable disconnection rate (disconnetions per hour). Note that
|
||||
* this signifies a rate and the units happen to be per hour - this does not
|
||||
* signify that every contiguous one-hour period be checked.
|
||||
*/
|
||||
public final int maxDisconnectionRatePerHour;
|
||||
|
||||
// High Level metrics thresholds
|
||||
/** Minimum acceptable tx rate (Mbps) */
|
||||
public final double minTxRateMbps;
|
||||
/** Maximum acceptable Packet Error Rate (PER) (units are %) */
|
||||
public final double maxPERPercent;
|
||||
/** Minimum acceptable idle airtime (units are %) */
|
||||
public final double minIdleAirtimePercent;
|
||||
/** Maximum acceptable number of clients for one radio */
|
||||
public final int maxNumClients;
|
||||
|
||||
// Low Level metrics thresholds
|
||||
/** Minimum acceptable RSSI (dBm) */
|
||||
public final int minRssidBm;
|
||||
/** Maximum acceptable noise (dBm) */
|
||||
public final int maxNoisedBm;
|
||||
/** Maximum acceptable intf airtime (units are %) */
|
||||
public final double maxIntfAirtimePercent;
|
||||
/** Maximum acceptable number of neighbors */
|
||||
public final int maxNumNeighbors;
|
||||
/** Minimum acceptable client bandwidth (MHz) for non-2G bands / */
|
||||
public final int minClientBandwidthMHz;
|
||||
/** Minimum acceptable Access Point (AP) bandwidth (MHz) for non-2G bands */
|
||||
public final int minApBandwidthMHz;
|
||||
/** Minimum acceptable self airtime ratio (units are %) */
|
||||
public final double minSelfAirtimeRatioPercent;
|
||||
/** Maximum acceptable tx dropped ratio (units are %) */
|
||||
public final double maxTxDroppedRatioPercent;
|
||||
|
||||
/** Default constructor */
|
||||
public RCAParams() {
|
||||
// 6 hours -> 21600000 ms
|
||||
this.detectionWindowMs = 21600000;
|
||||
|
||||
this.minEstimatedThroughputMbps = 10;
|
||||
this.throughputAggregationPercentile = 10.0;
|
||||
this.maxLatencyThresholdMs = 50;
|
||||
this.maxJitterThresholdMs = 20;
|
||||
this.maxDisconnectionRatePerHour = 20;
|
||||
|
||||
this.minTxRateMbps = 50;
|
||||
this.maxPERPercent = 10.0;
|
||||
this.minIdleAirtimePercent = 10.0;
|
||||
this.maxNumClients = 10;
|
||||
|
||||
this.minRssidBm = -70;
|
||||
this.maxNoisedBm = -95;
|
||||
this.maxIntfAirtimePercent = 75.0;
|
||||
this.maxNumNeighbors = 10;
|
||||
this.minClientBandwidthMHz = 80;
|
||||
this.minApBandwidthMHz = 80;
|
||||
this.minSelfAirtimeRatioPercent = 25.0;
|
||||
this.maxTxDroppedRatioPercent = 0.1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the given value is positive. If it is not, add a String
|
||||
* describing the problem to {@code errors}.
|
||||
*/
|
||||
private static void validatePositive(
|
||||
String varName,
|
||||
int value,
|
||||
List<String> errors
|
||||
) {
|
||||
if (value <= 0) {
|
||||
errors.add(varName + " must be positive.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the given value is positive. If it is not, add a String
|
||||
* describing the problem to {@code errors}.
|
||||
*/
|
||||
private static void validatePositive(
|
||||
String varName,
|
||||
double value,
|
||||
List<String> errors
|
||||
) {
|
||||
if (value <= 0) {
|
||||
errors.add(varName + " must be positive.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirm that the given value is a valid percentile (between 0 and 100
|
||||
* inclusive). If it is not, add a String describing the problem to
|
||||
* {@code errors}.
|
||||
*/
|
||||
private static void validatePercentile(
|
||||
String varName,
|
||||
double value,
|
||||
List<String> errors
|
||||
) {
|
||||
if (value < 0 || value > 100) {
|
||||
errors.add(varName + " must be between 0 and 100 inclusive.");
|
||||
}
|
||||
}
|
||||
|
||||
/** Return a list of errors (empty list of no errors) */
|
||||
public List<String> validate() {
|
||||
List<String> errors = new ArrayList<>();
|
||||
validatePositive("Detection window", detectionWindowMs, errors);
|
||||
|
||||
validatePositive(
|
||||
"Minimum estimated throughput",
|
||||
minEstimatedThroughputMbps,
|
||||
errors
|
||||
);
|
||||
validatePercentile(
|
||||
"Thoughput aggregation percentile",
|
||||
throughputAggregationPercentile,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Maximum latency threshold",
|
||||
maxLatencyThresholdMs,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Maximum jitter threshold",
|
||||
maxJitterThresholdMs,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Maximum disconnection rate",
|
||||
maxDisconnectionRatePerHour,
|
||||
errors
|
||||
);
|
||||
|
||||
validatePositive("Minimum tx rate", minTxRateMbps, errors);
|
||||
validatePercentile(
|
||||
"Maximum Packet Error Rate (PER)",
|
||||
maxPERPercent,
|
||||
errors
|
||||
);
|
||||
validatePercentile(
|
||||
"Minimum idle airtime",
|
||||
minIdleAirtimePercent,
|
||||
errors
|
||||
);
|
||||
validatePositive("Maximum number of clients", maxNumClients, errors);
|
||||
|
||||
validatePercentile(
|
||||
"Maximum intf airtime",
|
||||
maxIntfAirtimePercent,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Maximum number of neighbors",
|
||||
maxNumNeighbors,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Minimum client bandwidth",
|
||||
minClientBandwidthMHz,
|
||||
errors
|
||||
);
|
||||
validatePositive(
|
||||
"Minimum Access Point (AP) bandwidth",
|
||||
minApBandwidthMHz,
|
||||
errors
|
||||
);
|
||||
validatePercentile(
|
||||
"Minimum self airtime ratio",
|
||||
minSelfAirtimeRatioPercent,
|
||||
errors
|
||||
);
|
||||
validatePercentile(
|
||||
"Maximum tx dropped ratio",
|
||||
maxTxDroppedRatioPercent,
|
||||
errors
|
||||
);
|
||||
|
||||
return errors;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Library providing Root Cause Analysis (RCA) functionality.
|
||||
*/
|
||||
package com.facebook.openwifi.librca;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.openwifi.librca.stats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Aggregated statistics for each client.
|
||||
* Mainly handle KPI and metric calculations.
|
||||
*/
|
||||
public class ClientStats {
|
||||
/** Client MAC */
|
||||
public String station;
|
||||
|
||||
/** LinkStats that are of the same station(client) */
|
||||
public List<LinkStats> connections;
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.openwifi.librca.stats;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Aggregation Statistics Model of InputStats.
|
||||
* Aggregate by bssid, station and RadioConfig.
|
||||
*/
|
||||
public class LinkStats {
|
||||
public static class RadioConfig {
|
||||
public int channel;
|
||||
public int channelWidth;
|
||||
public int txPower;
|
||||
public String phy;
|
||||
}
|
||||
|
||||
public static class AssociationInfo {
|
||||
/** Rate information for receive/transmit data rate. */
|
||||
public static class Rate {
|
||||
public long bitRate;
|
||||
public int chWidth;
|
||||
public int mcs;
|
||||
}
|
||||
|
||||
public long connected;
|
||||
public long inactive;
|
||||
public int rssi;
|
||||
public long rxBytes;
|
||||
public long rxPackets;
|
||||
public Rate rxRate;
|
||||
public long txBytes;
|
||||
public long txDuration;
|
||||
public long txFailed;
|
||||
public long txPackets;
|
||||
public Rate txRate;
|
||||
public long txRetries;
|
||||
public int ackSignal;
|
||||
public int ackSignalAvg;
|
||||
|
||||
// The metrics below are from Interface the client was connected to.
|
||||
public long txPacketsCounters;
|
||||
public long txErrorsCounters;
|
||||
public long txDroppedCounters;
|
||||
|
||||
// The metrics below are from the radio the client was associated to.
|
||||
public long activeMsRadio;
|
||||
public long busyMsRadio;
|
||||
public long noiseRadio;
|
||||
public long receiveMsRadio;
|
||||
public long transmitMsRadio;
|
||||
|
||||
/** Unix time in milliseconds */
|
||||
public long timestamp;
|
||||
}
|
||||
|
||||
/** BSSID of the AP radio */
|
||||
public String bssid;
|
||||
|
||||
/** Client MAC */
|
||||
public String station;
|
||||
|
||||
/** Radio configuration parameters */
|
||||
public RadioConfig radioConfig;
|
||||
|
||||
/** Association list */
|
||||
public List<AssociationInfo> associationInfoList;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.openwifi.librca.stats.inputs;
|
||||
|
||||
/**
|
||||
* Input data model.
|
||||
*
|
||||
* TODO: very incomplete
|
||||
*/
|
||||
public class InputStats {
|
||||
/** Radio parameters */
|
||||
public static class Radio {
|
||||
public long active_ms;
|
||||
public long busy_ms;
|
||||
public int channel;
|
||||
public String channel_width;
|
||||
public long noise;
|
||||
public String phy;
|
||||
public long receive_ms;
|
||||
public long transmit_ms;
|
||||
public int tx_power;
|
||||
}
|
||||
|
||||
public static class SSID {
|
||||
public static class Association {
|
||||
public static class Rate {
|
||||
public long bitrate;
|
||||
public int chwidth;
|
||||
public int mcs;
|
||||
}
|
||||
|
||||
public String bssid; // bssid of the AP radio
|
||||
public String station; // client MAC
|
||||
public long connected;
|
||||
public long inactive;
|
||||
public int rssi;
|
||||
public long rx_bytes;
|
||||
public long rx_packets;
|
||||
public Rate rx_rate;
|
||||
public long tx_bytes;
|
||||
public long tx_duration;
|
||||
public long tx_failed;
|
||||
public long tx_offset;
|
||||
public long tx_packets;
|
||||
public Rate tx_rate;
|
||||
public long tx_retries;
|
||||
public int ack_signal;
|
||||
public int ack_signal_avg;
|
||||
}
|
||||
|
||||
public Association[] associations;
|
||||
public Radio radio;
|
||||
}
|
||||
|
||||
/** Counters are for the wireless interface as a whole */
|
||||
public static class Counters {
|
||||
public long rx_bytes;
|
||||
public long rx_packets;
|
||||
public long rx_errors;
|
||||
public long rx_dropped;
|
||||
public long tx_bytes;
|
||||
public long tx_packets;
|
||||
public long tx_errors;
|
||||
public long tx_dropped;
|
||||
}
|
||||
|
||||
public SSID[] ssids;
|
||||
public Counters counters;
|
||||
|
||||
/** Unix time in milliseconds */
|
||||
public long timestamp;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user