mirror of
				https://github.com/Telecominfraproject/wlan-cloud-rrm.git
				synced 2025-11-04 04:27:46 +00:00 
			
		
		
		
	Compare commits
	
		
			35 Commits
		
	
	
		
			token_refr
			...
			release/v2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					787ef196d2 | ||
| 
						 | 
					73693c07ef | ||
| 
						 | 
					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 | 
							
								
								
									
										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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								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", \
 | 
			
		||||
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):
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ fullnameOverride: ""
 | 
			
		||||
images:
 | 
			
		||||
  owrrm:
 | 
			
		||||
    repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owrrm
 | 
			
		||||
    tag: main
 | 
			
		||||
    tag: v2.8.0
 | 
			
		||||
    pullPolicy: Always
 | 
			
		||||
#    regcred:
 | 
			
		||||
#      registry: tip-tip-wlan-cloud-ucentral.jfrog.io
 | 
			
		||||
@@ -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));
 | 
			
		||||
 | 
			
		||||
@@ -6,29 +6,34 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk;
 | 
			
		||||
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
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.openwifirrm.RRMConfig;
 | 
			
		||||
import com.facebook.openwifirrm.Utils;
 | 
			
		||||
import com.facebook.openwifirrm.optimizers.channel.ChannelOptimizer;
 | 
			
		||||
import com.facebook.openwifirrm.ucentral.informationelement.Country;
 | 
			
		||||
import com.facebook.openwifirrm.ucentral.informationelement.LocalPowerConstraint;
 | 
			
		||||
import com.facebook.openwifirrm.ucentral.informationelement.QbssLoad;
 | 
			
		||||
import com.facebook.openwifirrm.ucentral.informationelement.TxPwrInfo;
 | 
			
		||||
import com.facebook.openwifirrm.ucentral.models.State;
 | 
			
		||||
import com.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;
 | 
			
		||||
@@ -47,25 +52,47 @@ public class UCentralUtils {
 | 
			
		||||
	/** The Gson instance. */
 | 
			
		||||
	private static final Gson gson = new Gson();
 | 
			
		||||
 | 
			
		||||
	/** Map of band to the band-specific lowest available channel*/
 | 
			
		||||
	public static final Map<String, Integer> LOWER_CHANNEL_LIMIT =
 | 
			
		||||
		new HashMap<>();
 | 
			
		||||
	static {
 | 
			
		||||
		UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 1);
 | 
			
		||||
		UCentralUtils.LOWER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 36);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Map of band to the band-specific highest available channel*/
 | 
			
		||||
	public static final Map<String, Integer> UPPER_CHANNEL_LIMIT =
 | 
			
		||||
		new HashMap<>();
 | 
			
		||||
	static {
 | 
			
		||||
		UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_2G, 11);
 | 
			
		||||
		UCentralUtils.UPPER_CHANNEL_LIMIT.put(UCentralConstants.BAND_5G, 165);
 | 
			
		||||
	}
 | 
			
		||||
	/** 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.
 | 
			
		||||
	 *
 | 
			
		||||
@@ -147,9 +174,13 @@ public class UCentralUtils {
 | 
			
		||||
				case TxPwrInfo.TYPE:
 | 
			
		||||
					ieContainer.txPwrInfo = TxPwrInfo.parse(contents);
 | 
			
		||||
					break;
 | 
			
		||||
				case RMEnabledCapabilities.TYPE:
 | 
			
		||||
					ieContainer.rmEnabledCapabilities =
 | 
			
		||||
						RMEnabledCapabilities.parse(contents);
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			} catch (Exception e) {
 | 
			
		||||
				logger.debug("Skipping invalid IE {}", ie);
 | 
			
		||||
				logger.error(String.format("Skipping invalid IE %s", ie), e);
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -249,28 +280,20 @@ public class UCentralUtils {
 | 
			
		||||
	 * Returns the results map
 | 
			
		||||
	 */
 | 
			
		||||
	public static Map<String, List<String>> getBandsMap(
 | 
			
		||||
		Map<String, JsonArray> deviceStatus
 | 
			
		||||
		Map<String, List<UCentralSchema.Radio>> deviceStatus
 | 
			
		||||
	) {
 | 
			
		||||
		Map<String, List<String>> bandsMap = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
		for (String serialNumber : deviceStatus.keySet()) {
 | 
			
		||||
			JsonArray radioList =
 | 
			
		||||
				deviceStatus.get(serialNumber).getAsJsonArray();
 | 
			
		||||
		for (
 | 
			
		||||
				int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
 | 
			
		||||
			Map.Entry<String, List<UCentralSchema.Radio>> entry : deviceStatus
 | 
			
		||||
				.entrySet()
 | 
			
		||||
		) {
 | 
			
		||||
				JsonElement e = radioList.get(radioIndex);
 | 
			
		||||
				if (!e.isJsonObject()) {
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
				JsonObject radioObject = e.getAsJsonObject();
 | 
			
		||||
				String band = radioObject.get("band").getAsString();
 | 
			
		||||
			String serialNumber = entry.getKey();
 | 
			
		||||
			for (UCentralSchema.Radio radio : entry.getValue()) {
 | 
			
		||||
				bandsMap
 | 
			
		||||
					.computeIfAbsent(band, k -> new ArrayList<>())
 | 
			
		||||
					.computeIfAbsent(radio.band, k -> new ArrayList<>())
 | 
			
		||||
					.add(serialNumber);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return bandsMap;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -284,75 +307,61 @@ public class UCentralUtils {
 | 
			
		||||
	 * @return the results map of {band, {device, list of available channels}}
 | 
			
		||||
	 */
 | 
			
		||||
	public static Map<String, Map<String, List<Integer>>> getDeviceAvailableChannels(
 | 
			
		||||
		Map<String, JsonArray> deviceStatus,
 | 
			
		||||
		Map<String, JsonObject> deviceCapabilities,
 | 
			
		||||
		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 (String serialNumber : deviceStatus.keySet()) {
 | 
			
		||||
			JsonArray radioList =
 | 
			
		||||
				deviceStatus.get(serialNumber).getAsJsonArray();
 | 
			
		||||
		for (
 | 
			
		||||
				int radioIndex = 0; radioIndex < radioList.size(); radioIndex++
 | 
			
		||||
			Map.Entry<String, List<UCentralSchema.Radio>> entry : deviceStatus
 | 
			
		||||
				.entrySet()
 | 
			
		||||
		) {
 | 
			
		||||
				JsonElement e = radioList.get(radioIndex);
 | 
			
		||||
				if (!e.isJsonObject()) {
 | 
			
		||||
					return null;
 | 
			
		||||
				}
 | 
			
		||||
				JsonObject radioObject = e.getAsJsonObject();
 | 
			
		||||
				String band = radioObject.get("band").getAsString();
 | 
			
		||||
 | 
			
		||||
				JsonObject capabilitesObject =
 | 
			
		||||
			String serialNumber = entry.getKey();
 | 
			
		||||
			for (UCentralSchema.Radio radio : entry.getValue()) {
 | 
			
		||||
				Map<String, Capabilities.Phy> capabilitiesPhyMap =
 | 
			
		||||
					deviceCapabilities.get(serialNumber);
 | 
			
		||||
				List<Integer> availableChannels = new ArrayList<>();
 | 
			
		||||
				if (capabilitesObject == null) {
 | 
			
		||||
				if (capabilitiesPhyMap == null) {
 | 
			
		||||
					availableChannels
 | 
			
		||||
						.addAll(defaultAvailableChannels.get(band));
 | 
			
		||||
						.addAll(defaultAvailableChannels.get(radio.band));
 | 
			
		||||
				} else {
 | 
			
		||||
					Set<Entry<String, JsonElement>> entrySet = capabilitesObject
 | 
			
		||||
					Set<Entry<String, Capabilities.Phy>> entrySet =
 | 
			
		||||
						capabilitiesPhyMap
 | 
			
		||||
							.entrySet();
 | 
			
		||||
					for (Map.Entry<String, JsonElement> f : entrySet) {
 | 
			
		||||
						String bandInsideObject = f.getValue()
 | 
			
		||||
							.getAsJsonObject()
 | 
			
		||||
							.get("band")
 | 
			
		||||
							.getAsString();
 | 
			
		||||
						if (bandInsideObject.equals(band)) {
 | 
			
		||||
					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 {
 | 
			
		||||
								JsonArray channelInfo = f.getValue()
 | 
			
		||||
									.getAsJsonObject()
 | 
			
		||||
									.get("dfs_channels")
 | 
			
		||||
									.getAsJsonArray();
 | 
			
		||||
 | 
			
		||||
								for (JsonElement d : channelInfo) {
 | 
			
		||||
									dfsChannels.add(d.getAsInt());
 | 
			
		||||
								int[] channelInfo = phy.dfs_channels;
 | 
			
		||||
								for (int d : channelInfo) {
 | 
			
		||||
									dfsChannels.add(d);
 | 
			
		||||
								}
 | 
			
		||||
							} catch (Exception d) {}
 | 
			
		||||
							try {
 | 
			
		||||
								JsonArray channelInfo = f.getValue()
 | 
			
		||||
									.getAsJsonObject()
 | 
			
		||||
									.get("channels")
 | 
			
		||||
									.getAsJsonArray();
 | 
			
		||||
								for (JsonElement c : channelInfo) {
 | 
			
		||||
									int channel = c.getAsInt();
 | 
			
		||||
								int[] channelInfo = phy.channels;
 | 
			
		||||
								for (int channel : channelInfo) {
 | 
			
		||||
									if (!dfsChannels.contains(channel)) {
 | 
			
		||||
										availableChannels.add(channel);
 | 
			
		||||
									}
 | 
			
		||||
								}
 | 
			
		||||
							} catch (Exception c) {
 | 
			
		||||
								availableChannels
 | 
			
		||||
									.addAll(defaultAvailableChannels.get(band));
 | 
			
		||||
									.addAll(
 | 
			
		||||
										defaultAvailableChannels.get(radio.band)
 | 
			
		||||
									);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				deviceAvailableChannels.computeIfAbsent(
 | 
			
		||||
					band,
 | 
			
		||||
					radio.band,
 | 
			
		||||
					k -> new HashMap<>()
 | 
			
		||||
				)
 | 
			
		||||
					.put(
 | 
			
		||||
@@ -371,10 +380,10 @@ public class UCentralUtils {
 | 
			
		||||
	 * Returns the results map
 | 
			
		||||
	 */
 | 
			
		||||
	public static Map<String, String> getBssidsMap(
 | 
			
		||||
		Map<String, State> latestState
 | 
			
		||||
		Map<String, ? extends State> latestState
 | 
			
		||||
	) {
 | 
			
		||||
		Map<String, String> bssidMap = new HashMap<>();
 | 
			
		||||
		for (Map.Entry<String, State> e : latestState.entrySet()) {
 | 
			
		||||
		for (Entry<String, ? extends State> e : latestState.entrySet()) {
 | 
			
		||||
			State state = e.getValue();
 | 
			
		||||
			for (
 | 
			
		||||
				int interfaceIndex = 0;
 | 
			
		||||
@@ -399,47 +408,6 @@ public class UCentralUtils {
 | 
			
		||||
		return bssidMap;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Generate the RRM service key. */
 | 
			
		||||
	public static String generateServiceKey(
 | 
			
		||||
		RRMConfig.ServiceConfig serviceConfig
 | 
			
		||||
	) {
 | 
			
		||||
		try {
 | 
			
		||||
			MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
 | 
			
		||||
			sha256.update(serviceConfig.publicEndpoint.getBytes());
 | 
			
		||||
			sha256.update(serviceConfig.privateEndpoint.getBytes());
 | 
			
		||||
			return Utils.bytesToHex(sha256.digest());
 | 
			
		||||
		} catch (NoSuchAlgorithmException e) {
 | 
			
		||||
			logger.error("Unable to generate service key", e);
 | 
			
		||||
			return "";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Converts channel number to that channel's center frequency in MHz.
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param channel channel number. See
 | 
			
		||||
	 *                {@link ChannelOptimizer#AVAILABLE_CHANNELS_BAND} for channels
 | 
			
		||||
	 *                in each band.
 | 
			
		||||
	 * @return the center frequency of the given channel in MHz
 | 
			
		||||
	 */
 | 
			
		||||
	public static int channelToFrequencyMHz(int channel) {
 | 
			
		||||
		if (
 | 
			
		||||
			ChannelOptimizer.AVAILABLE_CHANNELS_BAND
 | 
			
		||||
				.get(UCentralConstants.BAND_2G)
 | 
			
		||||
				.contains(channel)
 | 
			
		||||
		) {
 | 
			
		||||
			return 2407 + 5 * channel;
 | 
			
		||||
		} else if (
 | 
			
		||||
			ChannelOptimizer.AVAILABLE_CHANNELS_BAND
 | 
			
		||||
				.get(UCentralConstants.BAND_5G)
 | 
			
		||||
				.contains(channel)
 | 
			
		||||
		) {
 | 
			
		||||
			return 5000 + channel;
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new IllegalArgumentException("Must provide a valid channel.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Determines if the given channel is in the given band.
 | 
			
		||||
	 *
 | 
			
		||||
@@ -448,25 +416,17 @@ public class UCentralUtils {
 | 
			
		||||
	 * @return true if the given channel is in the given band; false otherwise
 | 
			
		||||
	 */
 | 
			
		||||
	public static boolean isChannelInBand(int channel, String band) {
 | 
			
		||||
		return LOWER_CHANNEL_LIMIT.get(band) <= channel &&
 | 
			
		||||
			channel <= UPPER_CHANNEL_LIMIT.get(band);
 | 
			
		||||
		return AVAILABLE_CHANNELS_BAND.get(band).contains(channel);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Given the channel, gets the band by checking lower bound and upper bound
 | 
			
		||||
	 * of each band
 | 
			
		||||
	 *
 | 
			
		||||
	 * @param channel channel number
 | 
			
		||||
	 * @return band if the channel can be mapped to a valid band; null otherwise
 | 
			
		||||
	 */
 | 
			
		||||
	public static String getBandFromChannel(int channel) {
 | 
			
		||||
		for (String band : UCentralConstants.BANDS) {
 | 
			
		||||
			if (isChannelInBand(channel, band)) {
 | 
			
		||||
				return band;
 | 
			
		||||
	/** Return which band contains the given frequency (MHz). */
 | 
			
		||||
	public static String freqToBand(int freqMHz) {
 | 
			
		||||
		if (2412 <= freqMHz && freqMHz <= 2484) {
 | 
			
		||||
			return "2G";
 | 
			
		||||
		} else {
 | 
			
		||||
			return "5G";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
		return null;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Tries to parse channel width, if it encounters an error it will return null.
 | 
			
		||||
@@ -515,4 +475,116 @@ public class UCentralUtils {
 | 
			
		||||
			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.
 | 
			
		||||
@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,52 +6,47 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import org.slf4j.Logger;
 | 
			
		||||
import org.slf4j.LoggerFactory;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
import com.google.gson.JsonObject;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This information element (IE) appears in wifiscan entries.
 | 
			
		||||
 * Refer to the 802.11 specification for more details. Language in
 | 
			
		||||
 * javadocs is taken from the specification.
 | 
			
		||||
 * 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 {
 | 
			
		||||
	private static final Logger logger = LoggerFactory.getLogger(Country.class);
 | 
			
		||||
 | 
			
		||||
	/** Defined in 802.11 */
 | 
			
		||||
	/** 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 {
 | 
			
		||||
		/**
 | 
			
		||||
		 * The lowest channel number in the CountryInfo.
 | 
			
		||||
		 * 8 bits unsigned - the lowest channel number in the CountryInfo.
 | 
			
		||||
		 */
 | 
			
		||||
		public final int firstChannelNumber;
 | 
			
		||||
		public final short firstChannelNumber;
 | 
			
		||||
		/**
 | 
			
		||||
		 * The maximum power, in dBm, allowed to be transmitted.
 | 
			
		||||
		 * 8 bits unsigned - The maximum power, in dBm, allowed to be transmitted.
 | 
			
		||||
		 */
 | 
			
		||||
		public final int maximumTransmitPowerLevel;
 | 
			
		||||
		public final short maximumTransmitPowerLevel;
 | 
			
		||||
		/**
 | 
			
		||||
		 * Number of channels this CountryInfo applies to. E.g., if First
 | 
			
		||||
		 * Channel Number is 2 and Number of Channels is 4, this CountryInfo
 | 
			
		||||
		 * 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 int numberOfChannels;
 | 
			
		||||
		public final short numberOfChannels;
 | 
			
		||||
 | 
			
		||||
		/** Constructor. */
 | 
			
		||||
		public CountryInfo(
 | 
			
		||||
			int firstChannelNumber,
 | 
			
		||||
			int maximumTransmitPowerLevel,
 | 
			
		||||
			int numberOfChannels
 | 
			
		||||
			short firstChannelNumber,
 | 
			
		||||
			short maximumTransmitPowerLevel,
 | 
			
		||||
			short numberOfChannels
 | 
			
		||||
		) {
 | 
			
		||||
			this.firstChannelNumber = firstChannelNumber;
 | 
			
		||||
			this.maximumTransmitPowerLevel = maximumTransmitPowerLevel;
 | 
			
		||||
@@ -60,13 +55,13 @@ public class Country {
 | 
			
		||||
 | 
			
		||||
		/** Parse CountryInfo from the appropriate Json object. */
 | 
			
		||||
		public static CountryInfo parse(JsonObject contents) {
 | 
			
		||||
			final int firstChannelNumber =
 | 
			
		||||
				contents.get("First Channel Number").getAsInt();
 | 
			
		||||
			final int maximumTransmitPowerLevel = contents
 | 
			
		||||
			final short firstChannelNumber =
 | 
			
		||||
				contents.get("First Channel Number").getAsShort();
 | 
			
		||||
			final short maximumTransmitPowerLevel = contents
 | 
			
		||||
				.get("Maximum Transmit Power Level (in dBm)")
 | 
			
		||||
				.getAsInt();
 | 
			
		||||
			final int numberOfChannels =
 | 
			
		||||
				contents.get("Number of Channels").getAsInt();
 | 
			
		||||
				.getAsShort();
 | 
			
		||||
			final short numberOfChannels =
 | 
			
		||||
				contents.get("Number of Channels").getAsShort();
 | 
			
		||||
			return new CountryInfo(
 | 
			
		||||
				firstChannelNumber,
 | 
			
		||||
				maximumTransmitPowerLevel,
 | 
			
		||||
@@ -99,15 +94,10 @@ public class Country {
 | 
			
		||||
				maximumTransmitPowerLevel == other.maximumTransmitPowerLevel &&
 | 
			
		||||
				numberOfChannels == other.numberOfChannels;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		@Override
 | 
			
		||||
		public String toString() {
 | 
			
		||||
			return "CountryInfo [firstChannelNumber=" + firstChannelNumber +
 | 
			
		||||
				", maximumTransmitPowerLevel=" + maximumTransmitPowerLevel +
 | 
			
		||||
				", numberOfChannels=" + numberOfChannels + "]";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Country */
 | 
			
		||||
	public final String country;
 | 
			
		||||
	/**
 | 
			
		||||
	 * Each constraint is a CountryInfo describing tx power constraints on
 | 
			
		||||
	 * one or more channels, for the current country.
 | 
			
		||||
@@ -115,7 +105,11 @@ public class Country {
 | 
			
		||||
	public final List<CountryInfo> constraints;
 | 
			
		||||
 | 
			
		||||
	/** Constructor */
 | 
			
		||||
	public Country(List<CountryInfo> countryInfos) {
 | 
			
		||||
	public Country(
 | 
			
		||||
		String country,
 | 
			
		||||
		List<CountryInfo> countryInfos
 | 
			
		||||
	) {
 | 
			
		||||
		this.country = country;
 | 
			
		||||
		this.constraints = Collections.unmodifiableList(countryInfos);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -125,12 +119,16 @@ public class Country {
 | 
			
		||||
		JsonElement constraintsObject = contents.get("constraints");
 | 
			
		||||
		if (constraintsObject != null) {
 | 
			
		||||
			for (JsonElement jsonElement : constraintsObject.getAsJsonArray()) {
 | 
			
		||||
				JsonObject innerElem = jsonElement.getAsJsonObject();
 | 
			
		||||
				CountryInfo countryInfo =
 | 
			
		||||
					CountryInfo.parse(jsonElement.getAsJsonObject());
 | 
			
		||||
					CountryInfo.parse(innerElem.get("Country Info").getAsJsonObject());
 | 
			
		||||
				constraints.add(countryInfo);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return new Country(constraints);
 | 
			
		||||
		return new Country(
 | 
			
		||||
			contents.get("Code").getAsString(),
 | 
			
		||||
			constraints
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
@@ -152,9 +150,4 @@ public class Country {
 | 
			
		||||
		Country other = (Country) obj;
 | 
			
		||||
		return Objects.equals(constraints, other.constraints);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return "Country [constraints=" + constraints + "]";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
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 HTOperation {
 | 
			
		||||
	/** Defined in 802.11 table 9-92 */
 | 
			
		||||
	public static final int TYPE = 61;
 | 
			
		||||
 | 
			
		||||
	/** Channel number of the primary channel. */
 | 
			
		||||
	public final byte primaryChannel;
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
@@ -15,32 +15,31 @@ import com.google.gson.JsonObject;
 | 
			
		||||
/**
 | 
			
		||||
 * This information element (IE) appears in wifiscan entries. It is called
 | 
			
		||||
 * "Local Power Constraint" in these entries, and just "Power Constraint" in
 | 
			
		||||
 * the 802.11 specification. Refer to the specification for more details.
 | 
			
		||||
 * 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 */
 | 
			
		||||
	/** Defined in 802.11 table 9-92 */
 | 
			
		||||
	public static final int TYPE = 32;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * Units are dB.
 | 
			
		||||
	 * 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 int localPowerConstraint;
 | 
			
		||||
	public final short localPowerConstraint;
 | 
			
		||||
 | 
			
		||||
	/** Constructor */
 | 
			
		||||
	public LocalPowerConstraint(int localPowerConstraint) {
 | 
			
		||||
	public LocalPowerConstraint(short localPowerConstraint) {
 | 
			
		||||
		this.localPowerConstraint = localPowerConstraint;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** Parse LocalPowerConstraint IE from appropriate Json object. */
 | 
			
		||||
	public static LocalPowerConstraint parse(JsonObject contents) {
 | 
			
		||||
		final int localPowerConstraint =
 | 
			
		||||
			contents.get("Local Power Constraint").getAsInt();
 | 
			
		||||
		final short localPowerConstraint =
 | 
			
		||||
			contents.get("Local Power Constraint").getAsShort();
 | 
			
		||||
		return new LocalPowerConstraint(localPowerConstraint);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -63,10 +62,4 @@ public class LocalPowerConstraint {
 | 
			
		||||
		LocalPowerConstraint other = (LocalPowerConstraint) obj;
 | 
			
		||||
		return localPowerConstraint == other.localPowerConstraint;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return "LocalPowerConstraint [localPowerConstraint=" +
 | 
			
		||||
			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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
@@ -15,22 +15,21 @@ import com.google.gson.JsonObject;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This information element (IE) appears in wifiscan entries. It is called
 | 
			
		||||
 * "QBSS Load" in these entries, and just "BSS Load" in the 802.11
 | 
			
		||||
 * specification. Refer to the specification for more details. Language in
 | 
			
		||||
 * "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 */
 | 
			
		||||
	/** Defined in 802.11 table 9-92 */
 | 
			
		||||
	public static final int TYPE = 11;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * The total number of STAs currently associated with the BSS.
 | 
			
		||||
	 * Unsigned 16 bits - The total number of STAs currently associated with the BSS.
 | 
			
		||||
	 */
 | 
			
		||||
	public final int stationCount;
 | 
			
		||||
	public final short stationCount;
 | 
			
		||||
	/**
 | 
			
		||||
	 * The Channel Utilization field is defined as the percentage of time,
 | 
			
		||||
	 * linearly scaled with 255 representing 100%, that the AP sensed the
 | 
			
		||||
	 * 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
 | 
			
		||||
@@ -40,22 +39,22 @@ public class QbssLoad {
 | 
			
		||||
	 * 		(dot11ChannelUtilizationBeaconIntervals * dot11BeaconPeriod * 1024)
 | 
			
		||||
	 * )
 | 
			
		||||
	 */
 | 
			
		||||
	public final int channelUtilization;
 | 
			
		||||
	public final short channelUtilization;
 | 
			
		||||
	/**
 | 
			
		||||
	 * The Available Admission Capacity field contains an unsigned integer that
 | 
			
		||||
	 * specifies the remaining amount of medium time available via explicit
 | 
			
		||||
	 * admission control, in units of 32 miscrosecond/second. The field is
 | 
			
		||||
	 * helpful for roaming STAs to select an AP that is likely to accept future
 | 
			
		||||
	 * admission control requests, but it does not represent an assurance that
 | 
			
		||||
	 * the HC admits these requests.
 | 
			
		||||
	 * 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 int availableAdmissionCapacity;
 | 
			
		||||
	public final short availableAdmissionCapacity;
 | 
			
		||||
 | 
			
		||||
	/** Constructor */
 | 
			
		||||
	public QbssLoad(
 | 
			
		||||
		int stationCount,
 | 
			
		||||
		int channelUtilization,
 | 
			
		||||
		int availableAdmissionCapacity
 | 
			
		||||
		short stationCount,
 | 
			
		||||
		short channelUtilization,
 | 
			
		||||
		short availableAdmissionCapacity
 | 
			
		||||
	) {
 | 
			
		||||
		this.stationCount = stationCount;
 | 
			
		||||
		this.channelUtilization = channelUtilization;
 | 
			
		||||
@@ -70,11 +69,11 @@ public class QbssLoad {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		contents = ccaContentJsonElement.getAsJsonObject();
 | 
			
		||||
		final int stationCount = contents.get("Station Count").getAsInt();
 | 
			
		||||
		final int channelUtilization =
 | 
			
		||||
			contents.get("Channel Utilization").getAsInt();
 | 
			
		||||
		final int availableAdmissionCapacity =
 | 
			
		||||
			contents.get("Available Admission Capabilities").getAsInt();
 | 
			
		||||
		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,
 | 
			
		||||
@@ -107,11 +106,4 @@ public class QbssLoad {
 | 
			
		||||
			channelUtilization == other.channelUtilization &&
 | 
			
		||||
			stationCount == other.stationCount;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return "QbssLoad [stationCount=" + stationCount +
 | 
			
		||||
			", channelUtilization=" + channelUtilization +
 | 
			
		||||
			", availableAdmissionCapacity=" + availableAdmissionCapacity + "]";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,39 +6,46 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import com.google.gson.JsonElement;
 | 
			
		||||
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. Refer to the specification for more details. Language in
 | 
			
		||||
 * 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 */
 | 
			
		||||
	/** Defined in 802.11 table 9-92 */
 | 
			
		||||
	public static final int TYPE = 195;
 | 
			
		||||
 | 
			
		||||
	/** Local maximum transmit power for 20 MHz. Required field. */
 | 
			
		||||
	public final Integer localMaxTxPwrConstraint20MHz;
 | 
			
		||||
	/** Local maximum transmit power for 40 MHz. Optional field. */
 | 
			
		||||
	public final Integer localMaxTxPwrConstraint40MHz;
 | 
			
		||||
	/** Local maximum transmit power for 80 MHz. Optional field. */
 | 
			
		||||
	public final Integer localMaxTxPwrConstraint80MHz;
 | 
			
		||||
	/** Local maximum transmit power for both 160 MHz and 80+80 MHz. Optional field. */
 | 
			
		||||
	public final Integer localMaxTxPwrConstraint160MHz;
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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(
 | 
			
		||||
		int localMaxTxPwrConstraint20MHz,
 | 
			
		||||
		int localMaxTxPwrConstraint40MHz,
 | 
			
		||||
		int localMaxTxPwrConstraint80MHz,
 | 
			
		||||
		int localMaxTxPwrConstraint160MHz
 | 
			
		||||
		short localMaxTxPwrConstraint20MHz,
 | 
			
		||||
		Short localMaxTxPwrConstraint40MHz,
 | 
			
		||||
		Short localMaxTxPwrConstraint80MHz,
 | 
			
		||||
		Short localMaxTxPwrConstraint160MHz
 | 
			
		||||
	) {
 | 
			
		||||
		this.localMaxTxPwrConstraint20MHz = localMaxTxPwrConstraint20MHz;
 | 
			
		||||
		this.localMaxTxPwrConstraint40MHz = localMaxTxPwrConstraint40MHz;
 | 
			
		||||
@@ -48,16 +55,26 @@ public class TxPwrInfo {
 | 
			
		||||
 | 
			
		||||
	/** Parse TxPwrInfo IE from appropriate Json object. */
 | 
			
		||||
	public static TxPwrInfo parse(JsonObject contents) {
 | 
			
		||||
		JsonObject innerObj = contents.get("Tx Pwr Info").getAsJsonObject();
 | 
			
		||||
		// required field
 | 
			
		||||
		int localMaxTxPwrConstraint20MHz =
 | 
			
		||||
			contents.get("Local Max Tx Pwr Constraint 20MHz").getAsInt();
 | 
			
		||||
		short localMaxTxPwrConstraint20MHz =
 | 
			
		||||
			innerObj.get("Local Max Tx Pwr Constraint 20MHz").getAsShort();
 | 
			
		||||
		// optional field
 | 
			
		||||
		Integer localMaxTxPwrConstraint40MHz =
 | 
			
		||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
			
		||||
		Integer localMaxTxPwrConstraint80MHz =
 | 
			
		||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
			
		||||
		Integer localMaxTxPwrConstraint160MHz =
 | 
			
		||||
			parseOptionalField(contents, "Local Max Tx Pwr Constraint 40MHz");
 | 
			
		||||
		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,
 | 
			
		||||
@@ -66,17 +83,6 @@ public class TxPwrInfo {
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	private static Integer parseOptionalField(
 | 
			
		||||
		JsonObject contents,
 | 
			
		||||
		String fieldName
 | 
			
		||||
	) {
 | 
			
		||||
		JsonElement element = contents.get(fieldName);
 | 
			
		||||
		if (element == null) {
 | 
			
		||||
			return null;
 | 
			
		||||
		}
 | 
			
		||||
		return element.getAsInt();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public int hashCode() {
 | 
			
		||||
		return Objects.hash(
 | 
			
		||||
@@ -107,13 +113,4 @@ public class TxPwrInfo {
 | 
			
		||||
				other.localMaxTxPwrConstraint40MHz &&
 | 
			
		||||
			localMaxTxPwrConstraint80MHz == other.localMaxTxPwrConstraint80MHz;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	@Override
 | 
			
		||||
	public String toString() {
 | 
			
		||||
		return "TxPwrInfo [localMaxTxPwrConstraint20MHz=" +
 | 
			
		||||
			localMaxTxPwrConstraint20MHz + ", localMaxTxPwrConstraint40MHz=" +
 | 
			
		||||
			localMaxTxPwrConstraint40MHz + ", localMaxTxPwrConstraint80MHz=" +
 | 
			
		||||
			localMaxTxPwrConstraint80MHz + ", localMaxTxPwrConstraint160MHz=" +
 | 
			
		||||
			localMaxTxPwrConstraint160MHz + "]";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
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 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.
 | 
			
		||||
@@ -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,11 +6,16 @@
 | 
			
		||||
 * 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 static class Interface {
 | 
			
		||||
		public static class Client {
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -115,7 +122,7 @@ public class State {
 | 
			
		||||
	public static class Radio {
 | 
			
		||||
		public long active_ms;
 | 
			
		||||
		public long busy_ms;
 | 
			
		||||
		public int channel;
 | 
			
		||||
		public int channel; // TODO might be int[] array??
 | 
			
		||||
		public String channel_width;
 | 
			
		||||
		public long noise;
 | 
			
		||||
		public String phy;
 | 
			
		||||
@@ -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,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 java.util.Objects;
 | 
			
		||||
 | 
			
		||||
import com.facebook.openwifi.cloudsdk.WifiScanEntry;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Represents a single entry in wifi scan results.
 | 
			
		||||
 * ies[] array is not stored directly, but parsed into WifiScanEntry fields
 | 
			
		||||
 * 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;
 | 
			
		||||
@@ -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,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 DeviceConfiguration {
 | 
			
		||||
	public static class DeviceConfigurationElement {
 | 
			
		||||
@@ -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,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 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
 | 
			
		||||
@@ -6,15 +6,15 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral;
 | 
			
		||||
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 static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertTrue;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertFalse;
 | 
			
		||||
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
 | 
			
		||||
public class UCentralUtilsTest {
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
 * LICENSE file in the root directory of this source tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
package com.facebook.openwifirrm.ucentral.informationelement;
 | 
			
		||||
package com.facebook.openwifi.cloudsdk.ies;
 | 
			
		||||
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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