diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..42b93c904 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @rdkcentral/networkmanager-maintainers diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml new file mode 100644 index 000000000..c58b1b0b1 --- /dev/null +++ b/.github/workflows/cla.yml @@ -0,0 +1,20 @@ +name: "CLA" + +permissions: + contents: read + pull-requests: write + actions: write + statuses: write + +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +jobs: + CLA-Lite: + name: "Signature" + uses: rdkcentral/cmf-actions/.github/workflows/cla.yml@v1 + secrets: + PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_ASSISTANT }} diff --git a/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml b/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml new file mode 100644 index 000000000..7b8c1cba1 --- /dev/null +++ b/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml @@ -0,0 +1,19 @@ +name: Fossid Stateless Diff Scan + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: read + +jobs: + call-fossid-workflow: + if: ${{ ! github.event.pull_request.head.repo.fork }} + uses: rdkcentral/build_tools_workflows/.github/workflows/fossid_integration_stateless_diffscan.yml@1.0.0 + secrets: + FOSSID_CONTAINER_USERNAME: ${{ secrets.FOSSID_CONTAINER_USERNAME }} + FOSSID_CONTAINER_PASSWORD: ${{ secrets.FOSSID_CONTAINER_PASSWORD }} + FOSSID_HOST_USERNAME: ${{ secrets.FOSSID_HOST_USERNAME }} + FOSSID_HOST_TOKEN: ${{ secrets.FOSSID_HOST_TOKEN }} diff --git a/.github/workflows/gdbus_proxy_L1_test.yml b/.github/workflows/gdbus_proxy_L1_test.yml new file mode 100644 index 000000000..ea62e0169 --- /dev/null +++ b/.github/workflows/gdbus_proxy_L1_test.yml @@ -0,0 +1,137 @@ +name: gdbus_proxy_l1_tests + +on: + push: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + pull_request: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + +env: + THUNDER_REF: "R4.4.3" + +jobs: + L1-tests: + name: Build and run unit tests + runs-on: ubuntu-22.04 + + steps: + # Set up Thunder cache + - name: Set up Thunder cache + id: cache + uses: actions/cache@v3 + with: + path: | + build/Thunder + build/ThunderInterfaces + build/ThunderTools + install + !install/etc/WPEFramework/plugins + !install/usr/bin/gdbusproxy_l1_test + !install/usr/include/gmock + !install/usr/include/gtest + !install/usr/lib/libgmockd.a + !install/usr/lib/libgmock_maind.a + !install/usr/lib/libgtestd.a + !install/usr/lib/libgtest_maind.a + !install/usr/lib/cmake/GTest + !install/usr/lib/pkgconfig/gmock.pc + !install/usr/lib/pkgconfig/gmock_main.pc + !install/usr/lib/pkgconfig/gtest.pc + !install/usr/lib/pkgconfig/gtest_main.pc + !install/usr/lib/wpeframework/plugins + key: thunder-${{ env.THUNDER_REF }}-gdbus + - name: Install packages + run: | + sudo apt update + sudo apt-get install -y pkg-config libglib2.0-dev libnm-dev libcurl4-openssl-dev lcov ninja-build + + - name: Configure Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: Configure CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + - name: Checkout thunder repositories + if: steps.cache.outputs.cache-hit != 'true' + run: | + git clone https://github.com/rdkcentral/ThunderTools ThunderTools --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/Thunder Thunder --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/ThunderInterfaces ThunderInterfaces --branch ${{ env.THUNDER_REF }} + + - name: Checkout networkmanager + uses: actions/checkout@v3 + with: + path: networkmanager + + - name: Apply Thunder Patches + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd ${{github.workspace}}/Thunder + git apply ${{github.workspace}}/networkmanager/tests/patches/thunder/SubscribeStub.patch + + - name: Build ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderTools" -B build/ThunderTools + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderTools --target install -j8 + + - name: Build Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/Thunder" -B build/Thunder + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBINDING=127.0.0.1 + -DPORT=9998 + && + cmake --build build/Thunder --target install -j8 + + - name: ThunderInterfaces + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderInterfaces" -B build/ThunderInterfaces + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderInterfaces --target install -j8 + + - name: Install IPowerManager header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/" + + - name: Install IConnectivityCheck header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + CPC_IFACE_DIR="$(dirname "$IFACE_DIR")/interfaces_cpc/interfaces" + mkdir -p "$CPC_IFACE_DIR" + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IConnectivityCheck.h "$CPC_IFACE_DIR/" + + - name: Build networkmanager with Gnome GDBUS Proxy + run: > + cmake + -S "${{github.workspace}}/networkmanager" + -B build/networkmanager_gdbus + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" -fprofile-arcs -ftest-coverage " + -DENABLE_GNOME_NETWORKMANAGER=ON + -DENABLE_GNOME_GDBUS=ON + -DENABLE_PLUGIN_CLI=ON + && + cmake --build build/networkmanager_gdbus --target install -j8 + + diff --git a/.github/workflows/legacy_L1_L2_test.yml b/.github/workflows/legacy_L1_L2_test.yml new file mode 100644 index 000000000..2fc3eff08 --- /dev/null +++ b/.github/workflows/legacy_L1_L2_test.yml @@ -0,0 +1,183 @@ +name: legacy_l1_l2_tests + +on: + push: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + pull_request: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + +env: + THUNDER_REF: "R4.4.3" + +jobs: + L1-tests: + permissions: + contents: read + pull-requests: write + name: Build and run unit tests + runs-on: ubuntu-22.04 + + steps: + # Set up Thunder cache + - name: Set up Thunder cache + id: cache + uses: actions/cache@v3 + with: + path: | + !build/Thunder + build/ThunderInterfaces + build/ThunderTools + install + !install/etc/WPEFramework/plugins + !install/usr/bin/rdkproxy_l1_test + !install/usr/include/gmock + !install/usr/include/gtest + !install/usr/lib/libgmockd.a + !install/usr/lib/libgmock_maind.a + !install/usr/lib/libgtestd.a + !install/usr/lib/libgtest_maind.a + !install/usr/lib/cmake/GTest + !install/usr/lib/pkgconfig/gmock.pc + !install/usr/lib/pkgconfig/gmock_main.pc + !install/usr/lib/pkgconfig/gtest.pc + !install/usr/lib/pkgconfig/gtest_main.pc + !install/usr/lib/wpeframework/plugins + key: thunder-${{ env.THUNDER_REF }}-legacy + - name: Install packages + run: | + sudo apt update + sudo apt-get install -y pkg-config libglib2.0-dev libnm-dev libcurl4-openssl-dev lcov ninja-build libgupnp-1.2-1 libgupnp-1.2-dev libgssdp-1.2-0 libsoup2.4-1 + + - name: Configure Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: Configure CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + - name: Checkout thunder repositories + if: steps.cache.outputs.cache-hit != 'true' + run: | + git clone https://github.com/rdkcentral/ThunderTools ThunderTools --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/Thunder Thunder --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/ThunderInterfaces ThunderInterfaces --branch ${{ env.THUNDER_REF }} + + - name: Checkout networkmanager + uses: actions/checkout@v3 + with: + path: networkmanager + + - name: Apply Thunder Patches + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd ${{github.workspace}}/Thunder + git apply ${{github.workspace}}/networkmanager/tests/patches/thunder/SubscribeStub.patch + + - name: Build ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderTools" -B build/ThunderTools + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderTools --target install -j8 + + - name: Build Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/Thunder" -B build/Thunder + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBINDING=127.0.0.1 + -DPORT=9998 + && + cmake --build build/Thunder --target install -j8 + + - name: ThunderInterfaces + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderInterfaces" -B build/ThunderInterfaces + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderInterfaces --target install -j8 + + - name: Install IPowerManager header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/" + + - name: Install IConnectivityCheck header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + CPC_IFACE_DIR="$(dirname "$IFACE_DIR")/interfaces_cpc/interfaces" + mkdir -p "$CPC_IFACE_DIR" + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IConnectivityCheck.h "$CPC_IFACE_DIR/" + + - name: Generate IARM headers + run: | + touch install/usr/lib/libIARMBus.so + mkdir -p install/usr/include/rdk/iarmbus + touch install/usr/include/rdk/iarmbus/libIARM.h + cd "${{github.workspace}}/networkmanager/tests/" + mkdir -p headers/rdk/iarmbus + cd headers + touch rdk/iarmbus/libIARM.h rdk/iarmbus/libIBus.h + + - name: Build networkmanager with RDK Proxy + run: > + cmake + -S "${{github.workspace}}/networkmanager" + -B build/networkmanager_rdk + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" + -I ${{github.workspace}}/networkmanager/tests/headers + -I ${{github.workspace}}/networkmanager/tests/headers/rdk/iarmbus + --include ${{github.workspace}}/networkmanager/tests/mocks/Iarm.h + --include ${{github.workspace}}/networkmanager/tests/mocks/secure_wrappermock.h + -Wall --coverage + " + -DENABLE_UNIT_TESTING=ON + -DENABLE_ROUTER_DISCOVERY_TOOL=OFF + && + cmake --build build/networkmanager_rdk --target install -j8 + + - name: Run unit tests for Legacy_Network + run: > + PATH=${{github.workspace}}/install/usr/bin:${PATH} + LD_LIBRARY_PATH=${{github.workspace}}/install/usr/lib:${{github.workspace}}/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + legacynetwork_tests + + - name: Run unit tests for Legacy_WiFi + run: > + PATH=${{github.workspace}}/install/usr/bin:${PATH} + LD_LIBRARY_PATH=${{github.workspace}}/install/usr/lib:${{github.workspace}}/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + legacywifi_tests + + - name: Generate coverage + run: | + lcov -c -o coverage.info -d build/networkmanager_rdk + lcov -r coverage.info '/usr/include/*' '*/build/networkmanager_rdk/*' \ + '*/install/usr/include/*' '*/tests/*' 'googlemock/*' 'googletest/*' \ + -o filtered_coverage.info + - name: Generate the html report + run: | + genhtml filtered_coverage.info --output-directory /tmp/coverage_report + + - name: Upload the coverage report to Pull request using actions + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + /tmp/coverage_report + /tmp/gtest_log diff --git a/.github/workflows/libnm_proxy_L1_test.yml b/.github/workflows/libnm_proxy_L1_test.yml new file mode 100644 index 000000000..6a60ae3fa --- /dev/null +++ b/.github/workflows/libnm_proxy_L1_test.yml @@ -0,0 +1,189 @@ +name: libnm_proxy_l1_tests + +on: + push: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + pull_request: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + +env: + THUNDER_REF: "R4.4.3" + +jobs: + L1-tests: + name: Build and run unit tests + # Note: Ubuntu 24.04 is required for libnm 1.46 which is needed for NetworkManagerPowerClient support + runs-on: ubuntu-24.04 + + steps: + # Set up Thunder cache + - name: Set up Thunder cache + id: cache + uses: actions/cache@v3 + with: + path: | + build/Thunder + build/ThunderInterfaces + build/ThunderTools + install + !install/etc/WPEFramework/plugins + !install/usr/bin/libnmproxy_l1_test + !install/usr/include/gmock + !install/usr/include/gtest + !install/usr/lib/libgmockd.a + !install/usr/lib/libgmock_maind.a + !install/usr/lib/libgtestd.a + !install/usr/lib/libgtest_maind.a + !install/usr/lib/cmake/GTest + !install/usr/lib/pkgconfig/gmock.pc + !install/usr/lib/pkgconfig/gmock_main.pc + !install/usr/lib/pkgconfig/gtest.pc + !install/usr/lib/pkgconfig/gtest_main.pc + !install/usr/lib/wpeframework/plugins + key: thunder-${{ env.THUNDER_REF }}-libnm + - name: Install packages + run: | + sudo apt update + sudo apt-get install -y pkg-config libglib2.0-dev libnm-dev libcurl4-openssl-dev lcov ninja-build + + - name: Configure Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: Configure CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + - name: Checkout thunder repositories + if: steps.cache.outputs.cache-hit != 'true' + run: | + git clone https://github.com/rdkcentral/ThunderTools ThunderTools --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/Thunder Thunder --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/ThunderInterfaces ThunderInterfaces --branch ${{ env.THUNDER_REF }} + + - name: Checkout networkmanager + uses: actions/checkout@v3 + with: + path: networkmanager + + - name: Apply Thunder Patches + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd ${{github.workspace}}/Thunder + git apply ${{github.workspace}}/networkmanager/tests/patches/thunder/SubscribeStub.patch + + - name: Build ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderTools" -B build/ThunderTools + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderTools --target install -j8 + + - name: Build Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/Thunder" -B build/Thunder + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBINDING=127.0.0.1 + -DPORT=9998 + && + cmake --build build/Thunder --target install -j8 + + - name: ThunderInterfaces + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderInterfaces" -B build/ThunderInterfaces + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderInterfaces --target install -j8 + + - name: Install IPowerManager header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/" + + - name: Install IConnectivityCheck header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + CPC_IFACE_DIR="$(dirname "$IFACE_DIR")/interfaces_cpc/interfaces" + mkdir -p "$CPC_IFACE_DIR" + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IConnectivityCheck.h "$CPC_IFACE_DIR/" + + - name: Generate dependency files + run: | + sudo bash -c 'echo "ETHERNET_INTERFACE=eth0 + WIFI_INTERFACE=wlan0 + DEFAULT_HOSTNAME=rdk_test_device " > /etc/device.properties' + + - name: Generate IARM headers + run: | + touch install/usr/lib/libIARMBus.so + touch install/usr/lib/libmfrlib.so + mkdir -p install/usr/include/rdk/iarmbus + mkdir -p install/usr/include/rdk/iarmmgrs-hal + touch install/usr/include/rdk/iarmbus/libIARM.h + touch install/usr/include/rdk/iarmmgrs-hal/mfrMgr.h + cd "${{github.workspace}}/networkmanager/tests/" + mkdir -p headers/rdk/iarmbus + mkdir -p headers/rdk/iarmmgrs-hal + cd headers + touch rdk/iarmmgrs-hal/mfrMgr.h + touch rdk/iarmbus/libIARM.h rdk/iarmbus/libIBus.h + + - name: Build networkmanager with Gnome libnm Proxy + run: > + cmake + -S "${{github.workspace}}/networkmanager" + -B build/networkmanager_libnm + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" -fprofile-arcs -ftest-coverage + -I ${{github.workspace}}/networkmanager/tests/headers + -I ${{github.workspace}}/networkmanager/tests/headers/rdk/iarmbus + -I ${{github.workspace}}/networkmanager/tests/headers/rdk/iarmmgrs-hal + --include ${{github.workspace}}/networkmanager/tests/mocks/Iarm.h + --include ${{github.workspace}}/networkmanager/tests/mocks/mfrMgr.h + " + -DENABLE_GNOME_NETWORKMANAGER=ON + -DENABLE_GNOME_NETWORKMANAGER=ON + -DENABLE_LEGACY_PLUGINS=OFF + -DENABLE_UNIT_TESTING=ON + -DENABLE_PLUGIN_CLI=OFF + -DENABLE_MIGRATION_MFRMGR_SUPPORT=ON + && + cmake --build build/networkmanager_libnm --target install -j8 + + - name: Run l2 libnm proxy tests + run: > + PATH=${{github.workspace}}/install/usr/bin:${PATH} + LD_LIBRARY_PATH=${{github.workspace}}/install/usr/lib:${{github.workspace}}/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + libnm_proxy_l2_test --gtest_color=yes --gtest_output="xml:/tmp/gtest_log/libnm_proxy_l2_test.xml" + + - name: Generate coverage + run: | + lcov --rc geninfo_unexecuted_blocks=1 -c -o coverage.info -d build/networkmanager_libnm/ --ignore-errors mismatch + lcov -e coverage.info '*/networkmanager/plugin/gnome/*' -o filtered_coverage.info + + - name: Generate the html report + run: | + genhtml filtered_coverage.info --output-directory /tmp/coverage_report + + - name: Upload the coverage report to Pull request using actions + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + /tmp/coverage_report + /tmp/gtest_log diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml new file mode 100644 index 000000000..c7a10413d --- /dev/null +++ b/.github/workflows/native_full_build.yml @@ -0,0 +1,26 @@ +name: Build Component in Native Environment + +on: + push: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**' ] + pull_request: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**' ] + +permissions: + contents: read + +jobs: + build-networkmanager: + name: Build networkmanager component in github rdkcentral + # Ubuntu 24.04 provides libnm 1.46, closer to the version used in RDK(1.43.7). + runs-on: ubuntu-24.04 + + steps: + + - name: Checkout code + uses: actions/checkout@v3 + + - name: native build + run: | + sh -x build_dependencies.sh + sh -x cov_build.sh diff --git a/.github/workflows/rdk_proxy_L1_L2_test.yml b/.github/workflows/rdk_proxy_L1_L2_test.yml new file mode 100644 index 000000000..3871ab948 --- /dev/null +++ b/.github/workflows/rdk_proxy_L1_L2_test.yml @@ -0,0 +1,182 @@ +name: rdk_proxy_l1_l2_tests + +on: + push: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + pull_request: + branches: [ main, develop, 'support/**', 'hotfix/**', 'topic/**'] + +env: + THUNDER_REF: "R4.4.3" + +jobs: + L1-tests: + name: Build and run unit tests + runs-on: ubuntu-22.04 + + steps: + # Set up Thunder cache + - name: Set up Thunder cache + id: cache + uses: actions/cache@v3 + with: + path: | + build/Thunder + build/ThunderInterfaces + build/ThunderTools + install + !install/etc/WPEFramework/plugins + !install/usr/bin/rdkproxy_l1_test + !install/usr/include/gmock + !install/usr/include/gtest + !install/usr/lib/libgmockd.a + !install/usr/lib/libgmock_maind.a + !install/usr/lib/libgtestd.a + !install/usr/lib/libgtest_maind.a + !install/usr/lib/cmake/GTest + !install/usr/lib/pkgconfig/gmock.pc + !install/usr/lib/pkgconfig/gmock_main.pc + !install/usr/lib/pkgconfig/gtest.pc + !install/usr/lib/pkgconfig/gtest_main.pc + !install/usr/lib/wpeframework/plugins + key: thunder-${{ env.THUNDER_REF }}-rdk + - name: Install packages + run: | + sudo apt update + sudo apt-get install -y pkg-config libglib2.0-dev libnm-dev libcurl4-openssl-dev lcov ninja-build libgupnp-1.2-1 libgupnp-1.2-dev libgssdp-1.2-0 libsoup2.4-1 + + - name: Configure Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: Configure CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + - name: Checkout thunder repositories + if: steps.cache.outputs.cache-hit != 'true' + run: | + git clone https://github.com/rdkcentral/ThunderTools ThunderTools --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/Thunder Thunder --branch ${{ env.THUNDER_REF }} + git clone https://github.com/rdkcentral/ThunderInterfaces ThunderInterfaces --branch ${{ env.THUNDER_REF }} + + - name: Checkout networkmanager + uses: actions/checkout@v3 + with: + path: networkmanager + + - name: Apply Thunder Patches + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd ${{github.workspace}}/Thunder + git apply ${{github.workspace}}/networkmanager/tests/patches/thunder/SubscribeStub.patch + + - name: Build ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderTools" -B build/ThunderTools + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderTools --target install -j8 + + - name: Build Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/Thunder" -B build/Thunder + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBINDING=127.0.0.1 + -DPORT=9998 + && + cmake --build build/Thunder --target install -j8 + + - name: ThunderInterfaces + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake + -S "${{github.workspace}}/ThunderInterfaces" -B build/ThunderInterfaces + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + && + cmake --build build/ThunderInterfaces --target install -j8 + + - name: Install IPowerManager header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IPowerManager.h "$IFACE_DIR/" + + - name: Install IConnectivityCheck header + run: | + IFACE_DIR=$(find ${{github.workspace}}/install/usr/include -maxdepth 2 -name "interfaces" -type d | head -1) + CPC_IFACE_DIR="$(dirname "$IFACE_DIR")/interfaces_cpc/interfaces" + mkdir -p "$CPC_IFACE_DIR" + cp ${{github.workspace}}/networkmanager/tests/mocks/thunder/IConnectivityCheck.h "$CPC_IFACE_DIR/" + + - name: Generate IARM headers + run: | + touch install/usr/lib/libIARMBus.so + mkdir -p install/usr/include/rdk/iarmbus + touch install/usr/include/rdk/iarmbus/libIARM.h + cd "${{github.workspace}}/networkmanager/tests/" + mkdir -p headers/rdk/iarmbus + cd headers + touch rdk/iarmbus/libIARM.h rdk/iarmbus/libIBus.h + + - name: Build networkmanager with RDK Proxy + run: > + cmake + -S "${{github.workspace}}/networkmanager" + -B build/networkmanager_rdk + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="${{github.workspace}}/install/usr" + -DCMAKE_MODULE_PATH="${{github.workspace}}/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" + -I ${{github.workspace}}/networkmanager/tests/headers + -I ${{github.workspace}}/networkmanager/tests/headers/rdk/iarmbus + --include ${{github.workspace}}/networkmanager/tests/mocks/Iarm.h + --include ${{github.workspace}}/networkmanager/tests/mocks/secure_wrappermock.h + -Wall --coverage + " + -DENABLE_UNIT_TESTING=ON + -DENABLE_ROUTER_DISCOVERY_TOOL=ON + -DENABLE_LEGACY_PLUGINS=OFF + && + cmake --build build/networkmanager_rdk --target install -j8 + + - name: Run l1 tests + run: > + PATH=${{github.workspace}}/install/usr/bin:${PATH} + LD_LIBRARY_PATH=${{github.workspace}}/install/usr/lib:${{github.workspace}}/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + upnpcli_l1_test --gtest_color=yes --gtest_output="xml:/tmp/gtest_log/upnpcli_l1_test.xml" + class_l1_test --gtest_color=yes --gtest_output="xml:/tmp/gtest_log/class_l1_test.xml" + + - name: Run l2 rdk proxy tests + run: > + PATH=${{github.workspace}}/install/usr/bin:${PATH} + LD_LIBRARY_PATH=${{github.workspace}}/install/usr/lib:${{github.workspace}}/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + rdkproxy_l2_test --gtest_color=yes --gtest_output="xml:/tmp/gtest_log/rdkproxy_l2_test.xml" + + - name: Generate coverage + run: | + lcov -c -o coverage.info -d build/networkmanager_rdk + lcov -r coverage.info '/usr/include/*' '*/build/networkmanager_rdk/*' \ + '*/install/usr/include/*' '*/tests/*' 'googlemock/*' 'googletest/*' \ + -o filtered_coverage.info + - name: Generate the html report + run: | + genhtml filtered_coverage.info --output-directory /tmp/coverage_report + + - name: Upload the coverage report to Pull request using actions + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: | + /tmp/coverage_report + /tmp/gtest_log diff --git a/.github/workflows/validate_pr_desc.yml b/.github/workflows/validate_pr_desc.yml new file mode 100644 index 000000000..3264db93f --- /dev/null +++ b/.github/workflows/validate_pr_desc.yml @@ -0,0 +1,82 @@ +name: PR Title Validation +permissions: + contents: read +on: + pull_request: + branches: [develop, main, 'support/**', 'hotfix/**', 'topic/**'] + types: [opened, edited, synchronize] + +jobs: + validate-pr-title: + runs-on: ubuntu-latest + steps: + - name: Validate PR Title Only + env: + PR_TITLE: ${{ github.event.pull_request.title }} + run: | + # Define valid ticket IDs + VALID_TICKET_IDS=("RDKEMW" "RDK") + + # Function to validate ticket format and ID + validate_ticket() { + local text="$1" + + echo "Validating PR title: $text" + + # Check if text matches the pattern - : + if [[ ! "$text" =~ ^[A-Z0-9]+-[0-9]+[[:space:]]*:[[:space:]]*.+ ]]; then + echo "ERROR: PR title format is invalid." + echo "Expected format: - : " + echo "Example: RDKEMW-123 : Fix playbook issue" + echo "" + echo "Valid ticket IDs are:" + printf "%s\n" "${VALID_TICKET_IDS[@]}" + return 1 + fi + + # Extract ticket ID from the text + local ticket_prefix=$(echo "$text" | sed -n 's/^\([A-Z0-9]\+\)-[0-9]\+[[:space:]]*:.*$/\1/p') + + if [ -z "$ticket_prefix" ]; then + echo "ERROR: Could not extract ticket ID from PR title." + echo "Expected format: - : " + echo "" + echo "Valid ticket IDs are:" + printf "%s\n" "${VALID_TICKET_IDS[@]}" + return 1 + fi + + # Check if extracted ticket ID is in the valid list + local valid=false + for valid_id in "${VALID_TICKET_IDS[@]}"; do + if [ "$ticket_prefix" = "$valid_id" ]; then + valid=true + break + fi + done + + if [ "$valid" = false ]; then + echo "ERROR: Invalid ticket ID '$ticket_prefix' in PR title" + echo "" + echo "Valid ticket IDs are:" + printf "%s\n" "${VALID_TICKET_IDS[@]}" + echo "" + echo "Your PR title should start with one of the above ticket IDs followed by a number." + echo "Example: RDKEMW-123 : Fix playbook issue" + return 1 + fi + + echo "PR title validation passed! Ticket ID: $ticket_prefix" + return 0 + } + + # Validate PR Title Only + echo "=== Validating PR Title ===" + if ! validate_ticket "$PR_TITLE"; then + echo "" + echo "VALIDATION FAILED: PR title must contain valid ticket ID from the approved list." + exit 1 + fi + + echo "" + echo "🎉 PR TITLE VALIDATION PASSED! Title contains valid ticket ID." diff --git a/CHANGELOG.md b/CHANGELOG.md index f731621cf..f9efdbef3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,349 @@ All notable changes to this RDK Service will be documented in this file. * Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development. -* For more details, refer to [versioning](https://github.com/rdkcentral/rdkservices#versioning) section under Main README. +## [4.2.0] - 2026-09-11 +### Changed +- Changed the default values for the number of ping request sent, the timeout and also the interval between the ping packets. +- Moving Ping and Trace methods as ASYNC is being designed + +## [4.1.0] - 2026-09-10 +### Changed +- Removed the explicit ReScan upon Wake-Up from DeepSleep because the Gnome NW sends SCAN requests with "AllowRoam" false which leads supplicant to Not to connect to WiFi; it ends-up scan-only + +## [4.0.0] - 2026-09-03 +### Added +- Implemented a new `onRouteChange` event notify availability of IP Route either from WiFi or Ethernet +- Integrated new InternetConnectivityManager external component to identify the internet availability without having 204 endpoint +- Publishing onInternetStatusChange event upon wake-up +- Added mutex protection before accessing lastConnectedSSID +- Changed the onWiFiStateChange event to include SSID name +- Updated documentation for events and method descriptions. + +## [3.7.0] - 2026-08-21 +### Changed +- Implemented a caching logic about the status of the interface & update based on events + +## [3.6.0] - 2026-08-11 +### Fixed +- Fixed the issue with connecting to a SSID that is not present in scan list + +## [3.5.0] - 2026-08-02 +### Added +- Added Coverity WorkFlow +- Added PowerManager boot dependency + +## [3.4.0] - 2026-07-17 +### Fixed +- Fixed the logging failure within the inprogress plugins +- Added mutex before accessing the scan filters +- Added t2 event for IPv6 Public IP +- Added thread to publish the events without blocking the caller notification thread + +## [3.3.0] - 2026-06-12 +### Fixed +- Fixed the memory leak that caused by the persistent m_nmClient and m_nmContext. + +## [3.2.0] - 2026-06-09 +### Added +- Implemented IP Caching logic to avoid multiple hard fetches from Gnome & invalidating the cache on change. + +## [3.1.0] - 2026-06-08 +### Changed +- Handled Power State changes within NetworkManager as below, +- Transition to DeepSleep will disconnect when NSM is OFF +- Transition from DeepSleep will Renew IP when NSM is ON + +## [3.0.0] - 2026-05-28 +### Changed +- The device hostname header that used to retrive has changed as "DEFAULT_HOSTNAME" +- Updated the WiFiStartScan to scan for specific SSID and also updated to take array of freq band as input instead of single freq + +## [2.3.0] - 2026-05-21 +### Fixed +- Fixed the issue which leads Enabling and Disabling of interfaces are taking longer +- Configured the NetworkManager Thunder Plugin to not to autostart; a systemd service will activate the plugin on boot-up + +## [2.2.0] - 2026-04-30 +### Added +- Added new method to connection to specific known SSID +- Extended WiFiConnect method to support BSSID and specific Band +- Defaulted to use RDKLogger and avoided redundant logging for few methods +- Added Minimal Ethernet Connection Profile for migration handling +- General improvements on RPC methods & crash resilience + +## [2.1.0] - 2026-03-20 +### Added +- Added T2 eventing from NetworkManager +- Updated documentation of the plugin + +## [2.0.0] - 2026-02-11 +### Changed +- The onAvailableSSIDs event signature changed to report strength, noise & frequency as Number +- Fixed the Activate Connection method to connect given (Ethernet and WiFi) connectionID + +## [1.13.0] - 2026-01-30 +### Added +- Implemented IPv4LL support for both Ethernet and WiFi interfaces +- Implemented logic to enable WiFi upon migration +- Fixed crash of Thunder Plugin upon Async WiFiConnect by using GCancellable + +## [1.12.0] - 2025-12-23 +### Fixed +- Fixed the Ethernet interface enablement upon EntOS migration + +## [1.11.0] - 2025-12-19 +### Added +- Added support to build for Thunder R5.x + +## [1.10.0] - 2025-12-05 +### Fixed +- Fixed the GetIPSettings for RDK Backend to return only when all the information like DNS info retrieved +- Fixed the retrieval of psk to pass it to MfrMgr. + +## [1.9.0] - 2025-11-21 +### Added +- Added shortterm solution to print the AP Info + +## [1.8.0] - 2025-11-14 +### Fixed +- Removed logging of WiFi PII information + +## [1.7.0] - 2025-11-13 +### Fixed +- Fixed bug MfrMgr Integration +- Subscribed to WiFi Events even when the device boots with wifi disabled +- Fixed the WiFi signal monitoring as the reporred RSSI was stale data + +## [1.6.0] - 2025-10-31 +### Added +- Integrated MfrMgr to persist WiFi Credential for DRI & Rollback to RDK-V Backend +- Implemented proper shutdown sequence for NetworkManager Plugin. +- Updated GDbus Support with all the latest fixes that went thro libnm backend + +## [1.5.0] - 2025-09-23 +### Changed +- Implemented L1/L2 and Part L3 for the plugin +- Implemented IP Address caching for Gnome backend +- Implemented a logic to return appropriate value for GetPrimaryInterface + +## [1.4.0] - 2025-09-23 +### Changed +- Implemented logic to not to persist SSID when asked +- Implemented IP Address caching to avoid making RPC request to networkmanager when interface status not changed. +- Fixed the Router Discovery app crash when AP does not support UPNP + +## [1.3.0] - 2025-09-17 +### Added +- Implemented NetworkManagerProxy library which can help Thunder to Create COM-RPC connection to Out-Of-Process plugin +- Implemented Internet Connectivity Monitoring for specific to Primary Interface. +- Updated to not to post onInternetStatusChanged event when secondary interface is disturbed. + +## [1.2.0] - 2025-09-15 +### Changed +- Reverted NetworkManager to continue to run as out-of-process-plugin + +## [1.1.0] - 2025-09-13 +### Changed +- Subscribed for Device Addition where driver for wlan0 could be inserted late +- Moved NetworkManager as in-process-plugin +- Update comments across the repo + +## [1.0.0] - 2025-08-29 +### Added +- Implemented additional L1/L2 for libnm backends +- Addressed Coverity issues in Connectivity Monitoring Thread +- Marking official production release. + +## [0.25.0] - 2025-08-27 +### Added +- Implemented SetHostname method which can send device's hostname over DHCP explicitly +- Implemented L1/L2 for netsrvmgr (RDK), libnm backends +- Clamped the WIFI Noise value to be within 0 dBm to -96 dBm +- Handled Plugin Termination sequence to stop the monitoring thread before exit +- Handled out-of-process plugin configuration as string +- Fixed Router Discovery to handle dual interface +- Fixed Internet Connectivity Monitoring to not to poll unless requested +- Addressed coverity reported issue + +## [0.24.0] - 2025-08-11 +### Added +- Use incoming EAP-PrivateKey Password to connect to WiFi when given + +## [0.23.0] - 2025-07-30 +### Added +- Added L1/L2 workflow for LegacyNetwork, LegacyWiFi and NetworkManager Plugins +- Fixed RemoveKnownSSID method to handle the invalid inputs +- Fixed the logging in GetIPSettings + +## [0.22.0] - 2025-07-14 +### Fixed +- Fixed trigger point for the connectivity monitoring +- Posting onWiFiStateChange Event when link-up for wifi event is received. + +## [0.21.0] - 2025-07-08 +### Fixed +- Fixed WiFi On/Off toggling failure +- Fixed the bug in IPAddress availability check upon wake-up +- Removed the SetPrimartInterface method +- Added input param validation for Ping and Stun methods + +## [0.20.0] - 2025-06-20 +### Fixed +- Fixed the bug in Resetting of Manual IPv4 +- Fixed the SSID Name Length Check +- Added Connectivity Improvement +- Fixed the WiFi Signal Monitor thread exception +- Fixed documentation of GetSupportedSecurityModes +- Fixed the WiFi Migration settings + +## [0.19.0] - 2025-05-29 +### Fixed +- Fixed Persistence related to interface enable/disable status +- Added addtional logs to understand WIFISignal monitoring thread + +## [0.18.0] - 2025-05-21 +### Fixed +- Fixed onActiveInterfaceChange event data. +- Implemented an improvement to the Ping method to have lesser interval between echo requests + +## [0.17.0] - 2025-05-02 +### Fixed +- Fixed the memory leak in Gnome-libnm port of the plugin. + +## [0.16.0] - 2025-05-01 +### Fixed +- Fixed the deregistration of Security Agent upon one successful WPS Connect. +- JSONRPC response for Ping in the legacy plugin is addressed + +## [0.15.0] - 2025-04-24 +### Fixed +- Implemented a CLI tool to test the plugin independently +- Implemented Retry logic to register with IARM upon failure for RDK Backend +- Fixed the memory leak that was observed in Gnome backend when connecting to SSID +- Fixed the events published upon WPS successful connects and failures. +- Fixed the WiFiConnect call with empty param from the legacy plugin + +## [0.14.0] - 2025-04-18 +### Added +- Process Monitoring for the out-of-process plugin added +- Added support Enable/Disable Logs to the Gnome NetworkManager daemon +- Fixed publishing of INTERNET Thunder Subsystem. +- Fixed the Memory Leak from the Scanned SSID names +- Fixed Set/Get Primary interface when both Ethernet and WiFi connected. + +## [0.13.0] - 2025-04-07 +### Changed +- Changed the out-of-process name +- Changed the WIFIConnect API scan & connect if the scan-result is empty +- Changed the GetIPSettings API to check the ipversion passed, and if empty, return IPv4 when available other IPv6. +- Fixed the GetAvailableInterfaces to return only the interfaces that are defined in device.properties +- Enable DEBUG to NetworkManager Gnome daemon when SetLogLevel called with DEBUG. + +## [0.12.0] - 2025-03-31 +### Added +- The file and folder structure of NetworkManager repo is re-organized +- Implemented a separate library for out-of-process plugin +- Implemented GDbus based APIs for all the NetworkManager APIs +- Implemented a Router Discovery tool +- Changed the approach to identify Open SSIDs +- Added INFO logging for the discovered SSIDs and the security methods discovered. +- Fixed Invalid Timeout in Connectivity Monitoring +- Fixed WiFiConnect Failure for previously saved SSID +- Fixed the missing Events on WPS Connect failure + +## [0.11.0] - 2025-03-07 +### Added +- Implemented concurrent interface support in public APIs +- Changed the approach to get the SNR value. +- Fixed RemoveKnownSSID() API to clear all the SSIDs when called with empty input +- Fixed minor bug in AddtoKnownSSID() API for Gnome backend +- Fixed Network Reset Flow + +## [0.10.0] - 2025-02-24 +### Added +- Implemented Security Agent to support WPS +- Implemented WiFi Signal Quality Monitoring using SNR value. +- Implemented L1 unit test code +- Fixed SetPrimaryInterface for Gnome support +- Fixed WiFiConnect failure to connect to unsecured SSIDs +- Fixed GetPrimaryInterface to gracefully handle libnm failure + +## [0.9.0] - 2025-01-30 +### Added +- Enhanced Internet Connectivity Monitoring +- Redefined the security modes +- Removed explicit API to start/stop connectivity monitoring +- Added retriveSSID method for migration ready +- Implemented initial code for gdbus based methods to communicate with NetworkManager +- Fixed Coverity Issues +- Fixed minor bugs + +## [0.8.0] - 2025-01-03 +### Changed +- Updated the definition of GetPublicIP method to take interface as additional input +- Updated the Legacy Plugin to use newly defined API + +## [0.7.0] - 2024-12-30 +### Added +- Gnome WPS custom design implemented +- Handle interface status based on IPAddress change event +- Cleanup STUN client codebase +- Handle JSON Event Subscription +- Handle Platform Init and Configure functions of out-of-process +- Handle the startup order +- Added L1 test cases for the plugins + +## [0.6.0] - 2024-12-10 +### Added +- Added specific SSID/frequency scanning +- Added retry logic in WPS Connect +- Added Unittest for Connectivity Class +- Fixed Continuous Connectivity Monitoring when no interface connected +- Fixed SetIPSettings on GNome backed + +## [0.5.4] - 2024-11-29 +### Fixed +- Fixed documentation of NetworkManager +- Handled IPAddressChange event + +## [0.5.3] - 2024-11-21 +### Fixed +- Fixed GetInterfaceState method +- Fixed bootup events on Gnome backend + +## [0.5.2] - 2024-11-19 +### Fixed +- Fixed Event data for DefaultInterfacechanged & WiFiThresholdChanged +- Fixed GetAvailableInterfaces on Gnome backend + +## [0.5.1] - 2024-11-15 +### Added +- Implemented WPS support on Gnome Backend +- Implemented L1/L2 unit test support +### Fixed +- Fixed GetIPSettings error +- Fixed IPAddressChange event + +## [0.5.0] - 2024-11-05 +### Added +- Enhanced WiFi Connection Structure to support EAP +- Defined JsonEnum to publish the Events +- Refactored Legacy Plugins to use ComRPC to communicate with NetworkManager +- Documentation Update +- Minor bug fixes + +## [0.4.0] - 2024-10-04 +### Added +- Added RDKLogger Integration +- Fixed Legacy Network API for getIPSetting2 +- Includes critical bug fixes + +## [0.3.0] - 2024-09-16 +### Added +- Gnome Network Manager support +- Fixed the missing Error events +- Fixed activation & subscription flow +- Includes bug fixes ## [0.2.0] - 2024-05-27 ### Added @@ -26,3 +368,4 @@ All notable changes to this RDK Service will be documented in this file. ## [0.1.0] - 2024-03-28 ### Added - Added NetworkManager plugin. A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device. + diff --git a/CMakeLists.txt b/CMakeLists.txt index 911b2f12e..a860fcfa1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,44 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2023 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# cmake_minimum_required(VERSION 3.3) project(NetworkManager) -find_package(WPEFramework) + +# Try WPEFramework first +find_package(WPEFramework QUIET CONFIG) + +# If WPEFramework isn't found, try Thunder +if (NOT WPEFramework_FOUND) + find_package(Thunder QUIET CONFIG) +endif() + +# Error out if neither is found +if (NOT WPEFramework_FOUND AND NOT Thunder_FOUND) + message(FATAL_ERROR + "Neither WPEFramework nor Thunder could be found.\n" + "Install one of them and ensure its CMake package is discoverable via CMAKE_PREFIX_PATH or default locations.") +endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") -set(VERSION_MAJOR 0) +set(VERSION_MAJOR 4) set(VERSION_MINOR 2) -set(VERSION_PATCH 11) +set(VERSION_PATCH 0) add_compile_definitions(NETWORKMANAGER_MAJOR_VERSION=${VERSION_MAJOR}) add_compile_definitions(NETWORKMANAGER_MINOR_VERSION=${VERSION_MINOR}) @@ -14,132 +46,59 @@ add_compile_definitions(NETWORKMANAGER_PATCH_VERSION=${VERSION_PATCH}) set(PROJECT_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) - include(CmakeHelperFunctions) string(TOLOWER ${NAMESPACE} STORAGE_DIRECTORY) get_directory_property(SEVICES_DEFINES COMPILE_DEFINITIONS) -set(PLUGIN_NAME NetworkManager) -set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) -set(PLUGIN_LEGACY_DEPRECATED_NETWORK ${NAMESPACE}Network) -set(PLUGIN_LEGACY_DEPRECATED_WIFI ${NAMESPACE}WiFiManager) - -find_package(${NAMESPACE}Core REQUIRED) -find_package(${NAMESPACE}Plugins REQUIRED) -find_package(CURL) -if(ENABLE_GNOME_NETWORKMANAGER) -pkg_check_modules(GLIB REQUIRED glib-2.0) -pkg_check_modules(LIBNM REQUIRED libnm) -else() -find_package(IARMBus REQUIRED) -endif () - - -message("Setup ProxyStub for INetworkManager.h") -find_package(CompileSettingsDebug CONFIG REQUIRED) -find_package(ProxyStubGenerator REQUIRED) - -set(ProxyStubGenerator_DIR ${CMAKE_SYSROOT}${CMAKE_INSTALL_PREFIX}/tools/cmake ${ProxyStubGenerator_DIR}) - -if(NOT GENERATOR_SEARCH_PATH) - set(GENERATOR_SEARCH_PATH ${CMAKE_SYSROOT}${CMAKE_INSTALL_PREFIX}/include/${NAMESPACE}) +option(ENABLE_LEGACY_PLUGINS "Enable Legacy Plugins" ON) +option(USE_RDK_LOGGER "Enable RDK Logger for logging" OFF ) +option(ENABLE_UNIT_TESTING "Enable unit tests" OFF) +option(USE_TELEMETRY "Enable Telemetry T2 support" OFF) +option(USE_CONNECTIVITYCHECKMGR "Enable ConnectivityCheckMgr delegation (compiles the delegation client and reads the TR-181 RFC feature flag via rfcapi)" OFF) +option(ENABLE_ETHERNET_CONNECTION_HANDLING + "Enable pre-sleep Ethernet deactivation" OFF) + +if(ENABLE_ETHERNET_CONNECTION_HANDLING) + add_definitions(-DENABLE_ETHERNET_CONNECTION_HANDLING) + message(STATUS "Ethernet connection handling: enabled") endif() -message("The Search path is, ${GENERATOR_SEARCH_PATH}") -ProxyStubGenerator(INPUT "${CMAKE_CURRENT_SOURCE_DIR}" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH ${GENERATOR_SEARCH_PATH}) - -file(GLOB PROXY_STUB_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/ProxyStubs*.cpp") - -message("Setup ${PROJECT_NAME} v${PROJECT_VERSION}") - -set(PLUGIN_NETWORKMANAGER_LOGLEVEL "5" CACHE STRING "To configure default loglevel NetworkManager plugin") - - -include_directories(${PROJECT_SOURCE_DIR}) -# Build the main plugin that runs inside the WPEFramework daemon -add_library(${MODULE_NAME} SHARED - NetworkManager.cpp - NetworkManagerJsonRpc.cpp - NetworkManagerLogger.cpp - NetworkManagerImplementation.cpp - NetworkManagerConnectivity.cpp - NetworkManagerStunClient.cpp - WiFiSignalStrengthMonitor.cpp - Module.cpp - ${PROXY_STUB_SOURCES}) - -target_link_libraries(${MODULE_NAME} PRIVATE - ${NAMESPACE}Core::${NAMESPACE}Core - ${NAMESPACE}Plugins::${NAMESPACE}Plugins - ) - -set_target_properties(${MODULE_NAME} PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED YES - FRAMEWORK FALSE) - -include_directories(${PROJECT_SOURCE_DIR}/interface) -include_directories(${PROJECT_SOURCE_DIR}/service) -include_directories(${PROJECT_SOURCE_DIR}/../helpers) - -install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) - - -# Build the implementation that runs out-of-process behind a COM-RPC interface - -if(ENABLE_GNOME_NETWORKMANAGER) - target_sources(${MODULE_NAME} PRIVATE NetworkManagerGnomeProxy.cpp - NetworkManagerGnomeWIFI.cpp - NetworkManagerGnomeEvents.cpp - NetworkManagerGnomeUtils.cpp ) - target_include_directories(${MODULE_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBNM_INCLUDE_DIRS}) - target_link_libraries(${MODULE_NAME} PRIVATE ${LIBNM_LIBRARIES}) -else() - target_sources(${MODULE_NAME} PRIVATE NetworkManagerRDKProxy.cpp) - target_include_directories(${MODULE_NAME} PRIVATE ${IARMBUS_INCLUDE_DIRS}) - target_link_libraries(${MODULE_NAME} PRIVATE ${IARMBUS_LIBRARIES}) +# Backend identity macros are consumed by shared headers; define them globally +# so all targets (plugin, tests, tools) compile against the same API surface. +if(ENABLE_GNOME_NETWORKMANAGER AND ENABLE_GNOME_GDBUS) + add_compile_definitions(NM_BACKEND_GDBUS=1) +elseif(NOT ENABLE_GNOME_NETWORKMANAGER) + add_compile_definitions(NM_BACKEND_RDK=1) endif() - -target_link_libraries(${MODULE_NAME} PRIVATE ${CURL_LIBRARIES}) -target_include_directories(${MODULE_NAME} PRIVATE ${CURL_INCLUDE_DIRS}) - -add_library(${PLUGIN_LEGACY_DEPRECATED_NETWORK} SHARED - LegacyPlugin_NetworkAPIs.cpp - NetworkManagerLogger.cpp - Module.cpp -) - -target_link_libraries(${PLUGIN_LEGACY_DEPRECATED_NETWORK} PRIVATE - ${NAMESPACE}Core::${NAMESPACE}Core - ${NAMESPACE}Plugins::${NAMESPACE}Plugins - ) - -set_target_properties(${PLUGIN_LEGACY_DEPRECATED_NETWORK} PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED YES) - -install(TARGETS ${PLUGIN_LEGACY_DEPRECATED_NETWORK} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) - -add_library(${PLUGIN_LEGACY_DEPRECATED_WIFI} SHARED - LegacyPlugin_WiFiManagerAPIs.cpp - NetworkManagerLogger.cpp - Module.cpp -) - -target_link_libraries(${PLUGIN_LEGACY_DEPRECATED_WIFI} PRIVATE - ${NAMESPACE}Core::${NAMESPACE}Core - ${NAMESPACE}Plugins::${NAMESPACE}Plugins - ) - -set_target_properties(${PLUGIN_LEGACY_DEPRECATED_WIFI} PROPERTIES - CXX_STANDARD 11 - CXX_STANDARD_REQUIRED YES) - -install(TARGETS ${PLUGIN_LEGACY_DEPRECATED_WIFI} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) - -write_config() -write_config(PLUGINS LegacyPlugin_NetworkAPIs CLASSNAME Network LOCATOR lib${PLUGIN_LEGACY_DEPRECATED_NETWORK}.so) -write_config(PLUGINS LegacyPlugin_WiFiManagerAPIs CLASSNAME WiFiManager LOCATOR lib${PLUGIN_LEGACY_DEPRECATED_WIFI}.so) - +if (USE_TELEMETRY) + find_package(T2 REQUIRED) + add_compile_definitions(USE_TELEMETRY=1) + message("Telemetry support enabled") +endif(USE_TELEMETRY) + +# Optional ConnectivityCheckMgr delegation. Compiles the delegation client and pulls +# in the rfcapi library used to read the TR-181 RFC feature flag at runtime, e.g. +# Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.ConnectivityCheckMgr.Enable. +if (USE_CONNECTIVITYCHECKMGR) + find_library(RFCAPI_LIBRARY rfcapi REQUIRED) + find_path(RFCAPI_INCLUDE_DIR rfcapi.h) + add_compile_definitions(USE_CONNECTIVITYCHECKMGR=1) + message(STATUS "ConnectivityCheckMgr delegation enabled (rfcapi lib=${RFCAPI_LIBRARY}, include=${RFCAPI_INCLUDE_DIR})") +endif(USE_CONNECTIVITYCHECKMGR) + +add_subdirectory(interface) +add_subdirectory(definition) +add_subdirectory(plugin) + +if (ENABLE_LEGACY_PLUGINS) + add_subdirectory(legacy) +endif (ENABLE_LEGACY_PLUGINS) + +add_subdirectory(tools) + +if(ENABLE_UNIT_TESTING) + add_subdirectory(tests/l1Test) + add_subdirectory(tests/l2Test) +endif(ENABLE_UNIT_TESTING) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e626c75e4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,4 @@ +Contributing +============ + +If you would like to contribute code to this project you can do so through GitHub by forking the repository and sending a pull request. Before RDK accepts your code into the project you must sign the RDK Contributor License Agreement (CLA). diff --git a/COPYING b/COPYING new file mode 120000 index 000000000..7a694c969 --- /dev/null +++ b/COPYING @@ -0,0 +1 @@ +LICENSE \ No newline at end of file diff --git a/INetworkManager.h b/INetworkManager.h deleted file mode 100644 index 4e8ebe8d6..000000000 --- a/INetworkManager.h +++ /dev/null @@ -1,303 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2023 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#pragma once -#include "Module.h" - -// @stubgen:include - -namespace WPEFramework -{ - namespace Exchange - { - enum myNetwork_ids { - ID_NETWORKMANAGER = 0x800004E0, - ID_NETWORKMANAGER_NOTIFICATION = ID_NETWORKMANAGER + 1, - ID_NETWORKMANAGER_INTERFACE_DETAILS_ITERATOR = ID_NETWORKMANAGER + 2, - ID_NETWORKMANAGER_WIFI_SECURITY_MODE_ITERATOR = ID_NETWORKMANAGER + 3 - }; - - /* @json */ - struct EXTERNAL INetworkManager: virtual public Core::IUnknown - { - // All interfaces require a unique ID, defined in Ids.h - enum { ID = ID_NETWORKMANAGER }; - - // Define the RPC methods - enum InterfaceType : uint8_t - { - INTERFACE_TYPE_ETHERNET, - INTERFACE_TYPE_WIFI, - INTERFACE_TYPE_P2P, - }; - - struct EXTERNAL InterfaceDetails { - string m_type; - string m_name; - string m_mac; - bool m_isEnabled; - bool m_isConnected; - }; - - enum IPAddressType : uint8_t - { - IP_ADDRESS_V4, - IP_ADDRESS_V6, - IP_ADDRESS_BOTH - }; - - struct EXTERNAL IPAddressInfo { - string m_ipAddrType; - bool m_autoConfig; - string m_dhcpServer; - string m_v6LinkLocal; - string m_ipAddress; - uint32_t m_prefix; - string m_gateway; - string m_primaryDns; - string m_secondaryDns; - }; - - // Define the RPC methods - enum InternetStatus : uint8_t - { - INTERNET_NOT_AVAILABLE, - INTERNET_LIMITED, - INTERNET_CAPTIVE_PORTAL, - INTERNET_FULLY_CONNECTED, - INTERNET_UNKNOWN, - }; - - enum WiFiFrequency : uint8_t - { - WIFI_FREQUENCY_WHATEVER, - WIFI_FREQUENCY_2_4_GHZ, - WIFI_FREQUENCY_5_GHZ, - WIFI_FREQUENCY_6_GHZ - }; - - enum WiFiWPS : uint8_t - { - WIFI_WPS_PBC, - WIFI_WPS_PIN, - WIFI_WPS_SERIALIZED_PIN - }; - - enum WIFISecurityMode : uint8_t - { - WIFI_SECURITY_NONE, - WIFI_SECURITY_WEP_64, - WIFI_SECURITY_WEP_128, - WIFI_SECURITY_WPA_PSK_TKIP, - WIFI_SECURITY_WPA_PSK_AES, - WIFI_SECURITY_WPA2_PSK_TKIP, - WIFI_SECURITY_WPA2_PSK_AES, - WIFI_SECURITY_WPA_ENTERPRISE_TKIP, - WIFI_SECURITY_WPA_ENTERPRISE_AES, - WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, - WIFI_SECURITY_WPA2_ENTERPRISE_AES, - WIFI_SECURITY_WPA_WPA2_PSK, - WIFI_SECURITY_WPA_WPA2_ENTERPRISE, - WIFI_SECURITY_WPA3_PSK_AES, - WIFI_SECURITY_WPA3_SAE - }; - - struct EXTERNAL WiFiScanResults { - string m_ssid; - WIFISecurityMode m_securityMode; - string m_signalStrength; - double m_frequency; - }; - - struct EXTERNAL WiFiConnectTo { - string m_ssid; - string m_passphrase; - WIFISecurityMode m_securityMode; - string m_identity; - string m_caCert; - string m_clientCert; - string m_privateKey; - string m_privateKeyPasswd; - bool m_persistSSIDInfo; - }; - - struct EXTERNAL WiFiSSIDInfo { - string m_ssid; - string m_bssid; - WIFISecurityMode m_securityMode; - string m_signalStrength; - double m_frequency; - string m_rate; - string m_noise; - }; - - struct EXTERNAL WIFISecurityModeInfo { - WIFISecurityMode m_securityMode; - string m_securityModeText; - }; - - enum WiFiSignalQuality : uint8_t - { - WIFI_SIGNAL_DISCONNECTED, - WIFI_SIGNAL_WEAK, - WIFI_SIGNAL_FAIR, - WIFI_SIGNAL_GOOD, - WIFI_SIGNAL_EXCELLENT - }; - - enum NMLogging : uint8_t - { - LOG_LEVEL_FATAL, - LOG_LEVEL_ERROR, - LOG_LEVEL_WARNING, - LOG_LEVEL_INFO, - LOG_LEVEL_VERBOSE, - LOG_LEVEL_TRACE - }; - - // The state of the interface - enum InterfaceState : uint8_t - { - INTERFACE_ADDED, - INTERFACE_LINK_UP, - INTERFACE_LINK_DOWN, - INTERFACE_ACQUIRING_IP, - INTERFACE_REMOVED, - INTERFACE_DISABLED - }; - - enum WiFiState : uint8_t - { - WIFI_STATE_UNINSTALLED, - WIFI_STATE_DISABLED, - WIFI_STATE_DISCONNECTED, - WIFI_STATE_PAIRING, - WIFI_STATE_CONNECTING, - WIFI_STATE_CONNECTED, - WIFI_STATE_SSID_NOT_FOUND, - WIFI_STATE_SSID_CHANGED, - WIFI_STATE_CONNECTION_LOST, - WIFI_STATE_CONNECTION_FAILED, - WIFI_STATE_CONNECTION_INTERRUPTED, - WIFI_STATE_INVALID_CREDENTIALS, - WIFI_STATE_AUTHENTICATION_FAILED, - WIFI_STATE_ERROR, - WIFI_STATE_INVALID - }; - - using IInterfaceDetailsIterator = RPC::IIteratorType; - using ISecurityModeIterator = RPC::IIteratorType; - using IStringIterator = RPC::IIteratorType; - - /* @brief Get all the Available Interfaces */ - virtual uint32_t GetAvailableInterfaces (IInterfaceDetailsIterator*& interfaces/* @out */) = 0; - - /* @brief Get the Primary Interface used for external world communication */ - virtual uint32_t GetPrimaryInterface (string& interface /* @out */) = 0; - /* @brief Set the Primary Interface used for external world communication */ - virtual uint32_t SetPrimaryInterface (const string& interface/* @in */) = 0; - - /* @brief Enable/Disable the given interface */ - virtual uint32_t SetInterfaceState (const string& interface /* @in */, const bool& enabled /* @in */) = 0; - /* @brief Get the state of given interface */ - virtual uint32_t GetInterfaceState (const string& interface /* @in */, bool& isEnabled /* @out */) = 0; - - /* @brief Get IP Address Of the Interface */ - virtual uint32_t GetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, IPAddressInfo& result /* @out */) = 0; - /* @brief Set IP Address Of the Interface */ - virtual uint32_t SetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, const IPAddressInfo& address /* @in */) = 0; - - /* @brief Get STUN Endpoint to be used for identifying Public IP */ - virtual uint32_t GetStunEndpoint (string &endPoint /* @out */, uint32_t &port /* @out */, uint32_t &bindTimeout /* @out */, uint32_t &cacheTimeout /* @out */) const = 0; - /* @brief Set STUN Endpoint to be used to identify Public IP */ - virtual uint32_t SetStunEndpoint (string const endPoint /* @in */, const uint32_t port /* @in */, const uint32_t bindTimeout /* @in */, const uint32_t cacheTimeout /* @in */) = 0; - - /* @brief Get ConnectivityTest Endpoints */ - virtual uint32_t GetConnectivityTestEndpoints(IStringIterator*& endPoints/* @out */) const = 0; - /* @brief Set ConnectivityTest Endpoints */ - virtual uint32_t SetConnectivityTestEndpoints(IStringIterator* const endPoints /* @in */) = 0; - - /* @brief Get Internet Connectivty Status */ - virtual uint32_t IsConnectedToInternet(const string &ipversion /* @in */, InternetStatus &result /* @out */) = 0; - /* @brief Get Authentication URL if the device is behind Captive Portal */ - virtual uint32_t GetCaptivePortalURI(string &endPoints/* @out */) const = 0; - - /* @brief Start The Internet Connectivity Monitoring */ - virtual uint32_t StartConnectivityMonitoring(const uint32_t interval /* @in */) = 0; - /* @brief Stop The Internet Connectivity Monitoring */ - virtual uint32_t StopConnectivityMonitoring(void) const = 0; - - /* @brief Get the Public IP used for external world communication */ - virtual uint32_t GetPublicIP (const string &ipversion /* @in */, string& ipAddress /* @out */) = 0; - - /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ - virtual uint32_t Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const uint16_t timeOutInSeconds /* @in */, const string guid /* @in */, string& response /* @out */) = 0; - - /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ - virtual uint32_t Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const string guid /* @in */, string& response /* @out */) = 0; - - - // WiFi Specific Methods - /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ - virtual uint32_t StartWiFiScan(const WiFiFrequency frequency /* @in */) = 0; - virtual uint32_t StopWiFiScan(void) = 0; - - virtual uint32_t GetKnownSSIDs(IStringIterator*& ssids /* @out */) = 0; - virtual uint32_t AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) = 0; - virtual uint32_t RemoveKnownSSID(const string& ssid /* @in */) = 0; - - virtual uint32_t WiFiConnect(const WiFiConnectTo& ssid /* @in */) = 0; - virtual uint32_t WiFiDisconnect(void) = 0; - virtual uint32_t GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) = 0; - - virtual uint32_t StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) = 0; - virtual uint32_t StopWPS(void) = 0; - virtual uint32_t GetWifiState(WiFiState &state /* @out */) = 0; - virtual uint32_t GetWiFiSignalStrength(string& ssid /* @out */, string& signalStrength /* @out */, WiFiSignalQuality& quality /* @out */) = 0; - virtual uint32_t GetSupportedSecurityModes(ISecurityModeIterator*& securityModes /* @out */) const = 0; - - /* @brief Set the network manager plugin log level */ - virtual uint32_t SetLogLevel(const NMLogging& logLevel /* @in */) = 0; - - /* @brief configure network manager plugin */ - virtual uint32_t Configure(const string& configLine /* @in */, NMLogging& logLevel /* @out */) = 0; - - /* @event */ - struct EXTERNAL INotification : virtual public Core::IUnknown - { - enum { ID = ID_NETWORKMANAGER_NOTIFICATION }; - - // Network Notifications that other processes can subscribe to - virtual void onInterfaceStateChange(const InterfaceState event /* @in */, const string interface /* @in */) = 0; - virtual void onActiveInterfaceChange(const string prevActiveInterface /* @in */, const string currentActiveinterface /* @in */) = 0; - virtual void onIPAddressChange(const string interface /* @in */, const bool isAcquired /* @in */, const bool isIPv6 /* @in */, const string ipAddress /* @in */) = 0; - virtual void onInternetStatusChange(const InternetStatus oldState /* @in */, const InternetStatus newstate /* @in */) = 0; - - // WiFi Notifications that other processes can subscribe to - virtual void onAvailableSSIDs(const string jsonOfWiFiScanResults /* @in */) = 0; - virtual void onWiFiStateChange(const WiFiState state /* @in */) = 0; - virtual void onWiFiSignalStrengthChange(const string ssid /* @in */, const string signalLevel /* @in */, const WiFiSignalQuality signalQuality /* @in */) = 0; - }; - - // Allow other processes to register/unregister from our notifications - virtual uint32_t Register(INetworkManager::INotification* notification) = 0; - virtual uint32_t Unregister(INetworkManager::INotification* notification) = 0; - }; - } -} diff --git a/LegacyPlugin_NetworkAPIs.conf.in b/LegacyPlugin_NetworkAPIs.conf.in deleted file mode 100644 index 0519daf3d..000000000 --- a/LegacyPlugin_NetworkAPIs.conf.in +++ /dev/null @@ -1,3 +0,0 @@ -precondition = ["Platform"] -callsign = "org.rdk.Network" -autostart = "true" diff --git a/LegacyPlugin_NetworkAPIs.config b/LegacyPlugin_NetworkAPIs.config deleted file mode 100644 index fcec1be20..000000000 --- a/LegacyPlugin_NetworkAPIs.config +++ /dev/null @@ -1,3 +0,0 @@ -set (autostart true) -set (preconditions Platform) -set (callsign "org.rdk.Network") diff --git a/LegacyPlugin_NetworkAPIs.cpp b/LegacyPlugin_NetworkAPIs.cpp deleted file mode 100644 index 3227370f0..000000000 --- a/LegacyPlugin_NetworkAPIs.cpp +++ /dev/null @@ -1,951 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2022 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ -#include "LegacyPlugin_NetworkAPIs.h" -#include "NetworkManagerLogger.h" - - -using namespace std; -using namespace WPEFramework::Plugin; -#define API_VERSION_NUMBER_MAJOR 2 -#define API_VERSION_NUMBER_MINOR 0 -#define API_VERSION_NUMBER_PATCH 0 -#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager.1" -#define SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS 5000 - -#define LOGINFOMETHOD() { string json; parameters.ToString(json); NMLOG_TRACE("Legacy params=%s", json.c_str() ); } -#define LOGTRACEMETHODFIN() { string json; response.ToString(json); NMLOG_TRACE("Legacy response=%s", json.c_str() ); } - -namespace WPEFramework -{ - class Job : public Core::IDispatch { - public: - Job(function work) - : _work(work) - { - } - void Dispatch() override - { - _work(); - } - - private: - function _work; - }; - - static Plugin::Metadata metadata( - // Version (Major, Minor, Patch) - API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH, - // Preconditions - {}, - // Terminations - {}, - // Controls - {} - ); - - Network* _gNWInstance = nullptr; - namespace Plugin - { - SERVICE_REGISTRATION(Network, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); - Network::Network() - : PluginHost::JSONRPC() - , m_service(nullptr) - , m_subsIfaceStateChange(false) - , m_subsActIfaceChange(false) - , m_subsIPAddrChange(false) - , m_subsInternetChange(false) - { - _gNWInstance = this; - m_defaultInterface = "wlan0"; - m_timer.connect(std::bind(&Network::subscribeToEvents, this)); - RegisterLegacyMethods(); - } - - Network::~Network() - { - _gNWInstance = nullptr; - } - - void Network::activatePrimaryPlugin() - { - uint32_t result = Core::ERROR_ASYNC_FAILED; - string callsign(NETWORK_MANAGER_CALLSIGN); - Core::Event event(false, true); - Core::IWorkerPool::Instance().Submit(Core::ProxyType(Core::ProxyType::Create([&]() { - auto interface = m_service->QueryInterfaceByCallsign(callsign); - if (interface == nullptr) { - result = Core::ERROR_UNAVAILABLE; - NMLOG_WARNING("no IShell for %s", callsign.c_str()); - } else { - NMLOG_INFO("Activating %s", callsign.c_str()); - result = interface->Activate(PluginHost::IShell::reason::REQUESTED); - interface->Release(); - } - event.SetEvent(); - }))); - event.Lock(); - - return; - } - - const string Network::Initialize(PluginHost::IShell* service ) - { - m_service = service; - m_service->AddRef(); - - string callsign(NETWORK_MANAGER_CALLSIGN); - - string token = ""; - - // TODO: use interfaces and remove token - auto security = m_service->QueryInterfaceByCallsign("SecurityAgent"); - if (security != nullptr) { - string payload = "http://localhost"; - if (security->CreateToken( - static_cast(payload.length()), - reinterpret_cast(payload.c_str()), - token) - == Core::ERROR_NONE) { - NMLOG_TRACE("Network plugin got security token"); - } else { - NMLOG_WARNING("Network plugin failed to get security token"); - } - security->Release(); - } else { - NMLOG_INFO("Network plugin: No security agent"); - } - - string query = "token=" + token; - - auto interface = m_service->QueryInterfaceByCallsign(callsign); - if (interface != nullptr) - { - PluginHost::IShell::state state = interface->State(); - NMLOG_INFO("Current status of the %s plugin is %d", callsign.c_str(), state); - - if((PluginHost::IShell::state::ACTIVATED == state) || (PluginHost::IShell::state::ACTIVATION == state)) - { - NMLOG_INFO("Dependency Plugin '%s' Ready", callsign.c_str()); - } - else - { - activatePrimaryPlugin(); - } - interface->Release(); - } - - Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); - m_networkmanager = make_shared >(_T(NETWORK_MANAGER_CALLSIGN), _T(NETWORK_MANAGER_CALLSIGN), false, query); - - m_timer.start(SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS); - return string(); - } - - void Network::Deinitialize(PluginHost::IShell* /* service */) - { - UnregisterLegacyMethods(); - m_service->Release(); - m_service = nullptr; - _gNWInstance = nullptr; - } - - string Network::Information() const - { - return(string()); - } - - /** - * Hook up all our JSON RPC methods - * - * Each method definition comprises of: - * * Input parameters - * * Output parameters - * * Method name - * * Function that implements that method - */ - void Network::RegisterLegacyMethods() - { - CreateHandler({2}); - - Register("getStbIp", &Network::getStbIp, this); - Register("getSTBIPFamily", &Network::getSTBIPFamily, this); - Register("getInterfaces", &Network::getInterfaces, this); - Register("isInterfaceEnabled", &Network::isInterfaceEnabled, this); - Register("getPublicIP", &Network::getPublicIP, this); - Register("setInterfaceEnabled", &Network::setInterfaceEnabled, this); - Register("getDefaultInterface", &Network::getDefaultInterface, this); - Register("setDefaultInterface", &Network::setDefaultInterface, this); - Register("setIPSettings", &Network::setIPSettings, this); - Register("getIPSettings", &Network::getIPSettings, this); - Register("getInternetConnectionState", &Network::getInternetConnectionState, this); - Register("ping", &Network::doPing, this); - Register("isConnectedToInternet", &Network::isConnectedToInternet, this); - Register("setStunEndPoint", &Network::setStunEndPoint, this); - Register("trace", &Network::doTrace, this); - Register("setConnectivityTestEndpoints", &Network::setConnectivityTestEndpoints, this); - Register("startConnectivityMonitoring", &Network::startConnectivityMonitoring, this); - Register("getCaptivePortalURI", &Network::getCaptivePortalURI, this); - Register("stopConnectivityMonitoring", &Network::stopConnectivityMonitoring, this); - GetHandler(2)->Register("setIPSettings", &Network::setIPSettings, this); - GetHandler(2)->Register("getIPSettings", &Network::getIPSettings, this); - } - - /** - * Unregister all our JSON-RPC methods - */ - void Network::UnregisterLegacyMethods() - { - Unregister("getInterfaces"); - Unregister("isInterfaceEnabled"); - Unregister("getPublicIP"); - Unregister("setInterfaceEnabled"); - Unregister("getDefaultInterface"); - Unregister("setDefaultInterface"); - Unregister("setIPSettings"); - Unregister("getIPSettings"); - Unregister("getInternetConnectionState"); - Unregister("ping"); - Unregister("isConnectedToInternet"); - Unregister("setConnectivityTestEndpoints"); - Unregister("startConnectivityMonitoring"); - Unregister("getCaptivePortalURI"); - Unregister("stopConnectivityMonitoring"); - } - -#define CIDR_NETMASK_IP_LEN 32 -const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { - "128.0.0.0", - "192.0.0.0", - "224.0.0.0", - "240.0.0.0", - "248.0.0.0", - "252.0.0.0", - "254.0.0.0", - "255.0.0.0", - "255.128.0.0", - "255.192.0.0", - "255.224.0.0", - "255.240.0.0", - "255.248.0.0", - "255.252.0.0", - "255.254.0.0", - "255.255.0.0", - "255.255.128.0", - "255.255.192.0", - "255.255.224.0", - "255.255.240.0", - "255.255.248.0", - "255.255.252.0", - "255.255.254.0", - "255.255.255.0", - "255.255.255.128", - "255.255.255.192", - "255.255.255.224", - "255.255.255.240", - "255.255.255.248", - "255.255.255.252", - "255.255.255.254", - "255.255.255.255", - }; - - uint32_t Network::getInterfaces (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - JsonArray array; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetAvailableInterfaces"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if ((rc == Core::ERROR_NONE) && (tmpResponse["success"].Boolean())) - { - const JsonArray& tmpArray = tmpResponse["interfaces"].Array(); - for (int i=0; iInvoke(5000, _T("SetStunEndpoint"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::setInterfaceEnabled (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - string interface; - JsonObject tmpParameters; - - LOGINFOMETHOD(); - if("WIFI" == parameters["interface"].String()) - interface = "wlan0"; - else if("ETHERNET" == parameters["interface"].String()) - interface = "eth0"; - - tmpParameters["interface"] = interface; - tmpParameters["enabled"] = parameters["enabled"]; - - if (m_networkmanager) - { - rc = m_networkmanager->Invoke(5000, _T("SetInterfaceState"), tmpParameters, response); - } - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::getDefaultInterface (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetPrimaryInterface"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - if ("wlan0" == tmpResponse["interface"].String()) - response["interface"] = "WIFI"; - else if("eth0" == tmpResponse["interface"].String()) - response["interface"] = "ETHERNET"; - response["success"] = tmpResponse["success"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::setDefaultInterface(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - string interface; - LOGINFOMETHOD(); - if("WIFI" == parameters["interface"].String()) - tmpParameters["interface"] = "wlan0"; - else if("ETHERNET" == parameters["interface"].String()) - tmpParameters["interface"] = "eth0"; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("SetPrimaryInterface"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - - return rc; - } - - uint32_t Network::setIPSettings(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - JsonObject tmpParameters; - LOGINFOMETHOD(); - - if("WIFI" == parameters["interface"].String()) - tmpParameters["interface"] = "wlan0"; - else if("ETHERNET" == parameters["interface"].String()) - tmpParameters["interface"] = "eth0"; - - tmpParameters["ipversion"] = parameters["ipversion"]; - tmpParameters["autoconfig"] = parameters["autoconfig"]; - tmpParameters["ipaddress"] = parameters["ipaddr"]; - auto it = find(begin(CIDR_PREFIXES), end(CIDR_PREFIXES), parameters["netmask"].String()); - if (it == end(CIDR_PREFIXES)) - return rc; - else - tmpParameters["prefix"] = distance(begin(CIDR_PREFIXES), it); - tmpParameters["gateway"] = parameters["gateway"]; - tmpParameters["primarydns"] = parameters["primarydns"]; - tmpParameters["secondarydns"] = parameters["secondarydns"]; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("SetIPSettings"), tmpParameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["supported"] = true; - response["success"] = tmpResponse["success"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::getIPSettings (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - JsonObject tmpParameters; - size_t index; - - LOGINFOMETHOD(); - - if (parameters.HasLabel("ipversion")) - tmpParameters["ipversion"] = parameters["ipversion"]; - if (parameters.HasLabel("interface")) - { - if ("WIFI" == parameters["interface"].String()) - tmpParameters["interface"] = "wlan0"; - else if("ETHERNET" == parameters["interface"].String()) - tmpParameters["interface"] = "eth0"; - } - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetIPSettings"), tmpParameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - string ipversion = tmpResponse["ipversion"].String(); - std::transform(ipversion.begin(), ipversion.end(), ipversion.begin(), ::toupper); - - if (parameters.HasLabel("interface")) - { - response["interface"] = parameters["interface"]; - } - else - { - if ("wlan0" == m_defaultInterface) - response["interface"] = "WIFI"; - else if("eth0" == m_defaultInterface) - response["interface"] = "ETHERNET"; - } - - response["ipversion"] = tmpResponse["ipversion"]; - response["autoconfig"] = tmpResponse["autoconfig"]; - response["ipaddr"] = tmpResponse["ipaddress"]; - if(tmpResponse["ipaddress"].String().empty()) - response["netmask"] = ""; - else if ("IPV4" == ipversion) - { - index = tmpResponse["prefix"].Number(); - if(CIDR_NETMASK_IP_LEN <= index) - return Core::ERROR_GENERAL; - response["netmask"] = CIDR_PREFIXES[index]; - } - else if ("IPV6" == ipversion) - { - response["netmask"] = tmpResponse["prefix"]; - } - response["gateway"] = tmpResponse["gateway"]; - response["dhcpserver"] = tmpResponse["dhcpserver"]; - response["primarydns"] = tmpResponse["primarydns"]; - response["secondarydns"] = tmpResponse["secondarydns"]; - response["success"] = tmpResponse["success"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - uint32_t Network::isConnectedToInternet(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - - LOGINFOMETHOD(); - string ipversion = parameters["ipversion"].String(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("IsConnectedToInternet"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["connectedToInternet"] = tmpResponse["isConnectedToInternet"]; - if(ipversion == "IPV4" || ipversion == "IPV6") - response["ipversion"] = ipversion.c_str(); - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - uint32_t Network::getInternetConnectionState(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - uint32_t rc1 = Core::ERROR_GENERAL; - string endPoint; - JsonObject tmpResponse; - JsonObject captivePortalResponse; - JsonObject tmpParameters; - string status; - - LOGINFOMETHOD(); - string ipversion = parameters["ipversion"].String(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("IsConnectedToInternet"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - status = tmpResponse["status"].String(); - NMLOG_TRACE("status = %s\n", status.c_str() ); - NMLOG_TRACE("tmpResponse[status].String() = %s\n", tmpResponse["status"].String().c_str() ); - if(status == "LIMITED_INTERNET") - response["state"] = static_cast(2); - else if(status == "CAPTIVE_PORTAL") - { - response["state"] = static_cast(1); - rc1 = getCaptivePortalURI(tmpParameters, captivePortalResponse); - if (Core::ERROR_NONE == rc1) - response["uri"] = captivePortalResponse["uri"]; - } - else if(status == "FULLY_CONNECTED") - response["state"] = static_cast(3); - else - response["state"] = static_cast(0); - - if(ipversion == "IPV4" || ipversion == "IPV6") - response["ipversion"] = ipversion.c_str(); - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - uint32_t Network::doPing(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - struct in_addr ipv4address; - struct in6_addr ipv6address; - JsonObject tmpParameters; - JsonObject tmpResponse; - string endpoint{}; - - LOGINFOMETHOD(); - - endpoint = parameters["endpoint"].String(); - - if (inet_pton(AF_INET, endpoint.c_str(), &ipv4address) > 0) - tmpParameters["ipversion"] = "IPv4"; - else if (inet_pton(AF_INET6, endpoint.c_str(), &ipv6address) > 0) - tmpParameters["ipversion"] = "IPv6"; - - tmpParameters["noOfRequest"] = parameters["packets"]; - tmpParameters["endpoint"] = parameters["endpoint"]; - tmpParameters["timeout"] = 5; - tmpParameters["guid"] = parameters["guid"]; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(15000, _T("Ping"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["target"] = parameters["endpoint"]; - response["guid"] = parameters["guid"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::doTrace(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - tmpParameters["endpoint"] = parameters["endpoint"].String(); - tmpParameters["packets"] = parameters["packets"].Number(); - tmpParameters["guid"] = ""; - - if(m_networkmanager) - rc = m_networkmanager->Invoke(20000, _T("Trace"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::getPublicIP(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - string interface; - string ipversion{"IPv4"}; - JsonObject tmpParameters; - JsonObject tmpResponse; - - LOGINFOMETHOD(); - if("WIFI" == parameters["iface"].String()) - interface = "wlan0"; - else if("ETHERNET" == parameters["iface"].String()) - interface = "eth0"; - - if(parameters["ipv6"].Boolean()) - ipversion = "IPv6"; - else - ipversion = "IPv4"; - - tmpParameters["interface"] = interface; - tmpParameters["ipversion"] = ipversion; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetPublicIP"), tmpParameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["public_ip"] = tmpResponse["publicIP"]; - response["success"] = tmpResponse["success"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::isInterfaceEnabled (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - - LOGINFOMETHOD(); - string interface = parameters["interface"].String(); - string newInterface = ""; - - if("WIFI" == interface) - newInterface = "wlan0"; - else if("ETHERNET" == interface) - newInterface = "eth0"; - - tmpParameters["interface"] = newInterface; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetInterfaceState"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - - return rc; - } - - uint32_t Network::setConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("SetConnectivityTestEndpoints"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::startConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - uint32_t interval = parameters["interval"].Number(); - - NMLOG_TRACE("connectivity interval = %d", interval); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StartConnectivityMonitoring"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::getCaptivePortalURI(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string endPoint; - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetCaptivePortalURI"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::stopConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StopConnectivityMonitoring"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t Network::getStbIp(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - LOGTRACEMETHODFIN(); - JsonObject tmpResponse; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetIPSettings"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["ip"] = tmpResponse["ipaddress"]; - response["success"] = true; - } - return rc; - } - - uint32_t Network::getSTBIPFamily(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - LOGTRACEMETHODFIN(); - JsonObject tmpResponse; - JsonObject tmpParameters; - - tmpParameters["ipversion"] = parameters["family"]; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetIPSettings"), tmpParameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["ip"] = tmpResponse["ipaddress"]; - response["success"] = true; - } - return rc; - } - - /** Private */ - void Network::subscribeToEvents(void) - { - uint32_t errCode = Core::ERROR_GENERAL; - if (m_networkmanager) - { - if (!m_subsIfaceStateChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onInterfaceStateChange"), &Network::onInterfaceStateChange); - if (Core::ERROR_NONE == errCode) - m_subsIfaceStateChange = true; - else - NMLOG_ERROR ("Subscribe to onInterfaceStateChange failed, errCode: %u", errCode); - } - - if (!m_subsActIfaceChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onActiveInterfaceChange"), &Network::onActiveInterfaceChange); - if (Core::ERROR_NONE == errCode) - m_subsActIfaceChange = true; - else - { - NMLOG_ERROR("Subscribe to onActiveInterfaceChange failed, errCode: %u", errCode); - } - } - - if (!m_subsIPAddrChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onIPAddressChange"), &Network::onIPAddressChange); - if (Core::ERROR_NONE == errCode) - m_subsIPAddrChange = true; - else - { - NMLOG_ERROR("Subscribe to onIPAddressChange failed, errCode: %u", errCode); - } - } - - if (!m_subsInternetChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onInternetStatusChange"), &Network::onInternetStatusChange); - if (Core::ERROR_NONE == errCode) - m_subsInternetChange = true; - else - { - NMLOG_ERROR("Subscribe to onInternetStatusChange failed, errCode: %u", errCode); - } - } - } - - if (m_subsIfaceStateChange && m_subsActIfaceChange && m_subsIPAddrChange && m_subsInternetChange) - m_timer.stop(); - } - - string Network::getInterfaceMapping(const string & interface) - { - if(interface == "wlan0") - return string("WIFI"); - else if(interface == "eth0") - return string("ETHERNET"); - return string(" "); - } - - /** Event Handling and Publishing */ - void Network::ReportonInterfaceStateChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - JsonObject legacyParams; - string json; - string state = parameters["state"].String(); - - legacyParams["interface"] = getInterfaceMapping(parameters["interface"].String()); - - /* State check */ - if(state == "INTERFACE_ADDED") - legacyParams["enabled"] = true; - else if(state == "INTERFACE_REMOVED") - legacyParams["enabled"] = false; - else if(state == "INTERFACE_LINK_UP") - legacyParams["status"] = "CONNECTED"; - else if(state == "INTERFACE_LINK_DOWN") - legacyParams["status"] = "DISCONNECTED"; - - if((state == "INTERFACE_ADDED") || (state == "INTERFACE_REMOVED")) - { - Notify("onInterfaceStatusChanged", legacyParams); - } - else if((state == "INTERFACE_LINK_UP") || (state == "INTERFACE_LINK_DOWN")) - { - Notify("onConnectionStatusChanged", legacyParams); - } - - return; - } - - void Network::ReportonActiveInterfaceChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - JsonObject legacyParams; - - legacyParams["oldInterfaceName"] = getInterfaceMapping(parameters["oldInterfaceName"].String()); - legacyParams["newInterfaceName"] = getInterfaceMapping(parameters["newInterfaceName"].String()); - - m_defaultInterface = parameters["newInterfaceName"].String(); - Notify("onDefaultInterfaceChanged", legacyParams); - return; - } - - void Network::ReportonIPAddressChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - JsonObject legacyParams; - legacyParams["interface"] = getInterfaceMapping(parameters["interface"].String()); - - if (parameters["isIPv6"].Boolean()) - { - legacyParams["ip6Address"] = parameters["ipAddress"]; - legacyParams["ip4Address"] = ""; - } - else - { - legacyParams["ip4Address"] = parameters["ipAddress"]; - legacyParams["ip6Address"] = ""; - } - - legacyParams["status"] = parameters["status"]; - - Notify("onIPAddressStatusChanged", legacyParams); - - if ("ACQUIRED" == parameters["status"].String()) - m_defaultInterface = parameters["interface"].String(); - - return; - } - - void Network::ReportonInternetStatusChange(const JsonObject& parameters) - { - Notify("onInternetStatusChange", parameters); - return; - } - - void Network::onInterfaceStateChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - if(_gNWInstance) - _gNWInstance->ReportonInterfaceStateChange(parameters); - - return; - } - - void Network::onActiveInterfaceChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - if(_gNWInstance) - _gNWInstance->ReportonActiveInterfaceChange(parameters); - return; - } - - void Network::onIPAddressChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - if(_gNWInstance) - _gNWInstance->ReportonIPAddressChange(parameters); - return; - } - - void Network::onInternetStatusChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - if(_gNWInstance) - _gNWInstance->ReportonInternetStatusChange(parameters); - } - } -} diff --git a/LegacyPlugin_WiFiManagerAPIs.conf.in b/LegacyPlugin_WiFiManagerAPIs.conf.in deleted file mode 100644 index 090ef0da5..000000000 --- a/LegacyPlugin_WiFiManagerAPIs.conf.in +++ /dev/null @@ -1,3 +0,0 @@ -precondition = ["Platform"] -callsign = "org.rdk.Wifi" -autostart = "true" diff --git a/LegacyPlugin_WiFiManagerAPIs.config b/LegacyPlugin_WiFiManagerAPIs.config deleted file mode 100644 index 3428986fd..000000000 --- a/LegacyPlugin_WiFiManagerAPIs.config +++ /dev/null @@ -1,3 +0,0 @@ -set (autostart true) -set (preconditions Platform) -set (callsign "org.rdk.Wifi") diff --git a/LegacyPlugin_WiFiManagerAPIs.cpp b/LegacyPlugin_WiFiManagerAPIs.cpp deleted file mode 100644 index fb878db4f..000000000 --- a/LegacyPlugin_WiFiManagerAPIs.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2022 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ -#include "LegacyPlugin_WiFiManagerAPIs.h" -#include "NetworkManagerLogger.h" -#include "INetworkManager.h" - -using namespace std; -using namespace WPEFramework::Plugin; -#define API_VERSION_NUMBER_MAJOR 2 -#define API_VERSION_NUMBER_MINOR 0 -#define API_VERSION_NUMBER_PATCH 0 -#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager.1" -#define SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS 5000 - -#define LOGINFOMETHOD() { string json; parameters.ToString(json); NMLOG_TRACE("Legacy params=%s", json.c_str() ); } -#define LOGTRACEMETHODFIN() { string json; response.ToString(json); NMLOG_TRACE("Legacy response=%s", json.c_str() ); } - -namespace WPEFramework -{ - class Job : public Core::IDispatch { - public: - Job(function work) - : _work(work) - { - } - void Dispatch() override - { - _work(); - } - - private: - function _work; - }; - - static Plugin::Metadata metadata( - // Version (Major, Minor, Patch) - API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH, - // Preconditions - {}, - // Terminations - {}, - // Controls - {} - ); - - WiFiManager* _gWiFiInstance = nullptr; - namespace Plugin - { - SERVICE_REGISTRATION(WiFiManager, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); - - WiFiManager::WiFiManager() - : PluginHost::JSONRPC() - , m_service(nullptr) - , m_subsWiFiStateChange(false) - , m_subsAvailableSSIDs(false) - , m_subsWiFiStrengthChange(false) - { - _gWiFiInstance = this; - m_timer.connect(std::bind(&WiFiManager::subscribeToEvents, this)); - RegisterLegacyMethods(); - } - - WiFiManager::~WiFiManager() - { - _gWiFiInstance = nullptr; - } - - void WiFiManager::activatePrimaryPlugin() - { - uint32_t result = Core::ERROR_ASYNC_FAILED; - string callsign(NETWORK_MANAGER_CALLSIGN); - Core::Event event(false, true); - Core::IWorkerPool::Instance().Submit(Core::ProxyType(Core::ProxyType::Create([&]() { - auto interface = m_service->QueryInterfaceByCallsign(callsign); - if (interface == nullptr) { - result = Core::ERROR_UNAVAILABLE; - NMLOG_WARNING("no IShell for %s", callsign.c_str()); - } else { - NMLOG_INFO("Activating %s", callsign.c_str()); - result = interface->Activate(PluginHost::IShell::reason::REQUESTED); - interface->Release(); - } - event.SetEvent(); - }))); - event.Lock(); - - return; - } - - const string WiFiManager::Initialize(PluginHost::IShell* service ) - { - m_service = service; - m_service->AddRef(); - - string callsign(NETWORK_MANAGER_CALLSIGN); - string token = ""; - - // TODO: use interfaces and remove token - auto security = m_service->QueryInterfaceByCallsign("SecurityAgent"); - if (security != nullptr) { - string payload = "http://localhost"; - if (security->CreateToken( - static_cast(payload.length()), - reinterpret_cast(payload.c_str()), - token) - == Core::ERROR_NONE) { - NMLOG_TRACE("WiFi manager plugin got security token"); - } else { - NMLOG_WARNING("WiFi manager plugin failed to get security token"); - } - security->Release(); - } else { - NMLOG_INFO("WiFi manager plugin: No security agent"); - } - - string query = "token=" + token; - auto interface = m_service->QueryInterfaceByCallsign(callsign); - if (interface != nullptr) - { - PluginHost::IShell::state state = interface->State(); - NMLOG_INFO("Current status of the %s plugin is %d", callsign.c_str(), state); - - if((PluginHost::IShell::state::ACTIVATED == state) || (PluginHost::IShell::state::ACTIVATION == state)) - { - NMLOG_INFO("Dependency Plugin '%s' Ready", callsign.c_str()); - } - else - { - activatePrimaryPlugin(); - } - interface->Release(); - } - - Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); - m_networkmanager = make_shared >(_T(NETWORK_MANAGER_CALLSIGN), _T(NETWORK_MANAGER_CALLSIGN), false, query); - - m_timer.start(SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS); - return string(); - } - - void WiFiManager::Deinitialize(PluginHost::IShell* /* service */) - { - UnregisterLegacyMethods(); - m_service->Release(); - m_service = nullptr; - _gWiFiInstance = nullptr; - } - - string WiFiManager::Information() const - { - return(string()); - } - - /** - * Hook up all our JSON RPC methods - * - * Each method definition comprises of: - * * Input parameters - * * Output parameters - * * Method name - * * Function that implements that method - */ - void WiFiManager::RegisterLegacyMethods() - { - CreateHandler({2}); - Register("cancelWPSPairing", &WiFiManager::cancelWPSPairing, this); - Register("clearSSID", &WiFiManager::clearSSID, this); - Register("connect", &WiFiManager::connect, this); - Register("disconnect", &WiFiManager::disconnect, this); - Register("getConnectedSSID", &WiFiManager::getConnectedSSID, this); - Register("getCurrentState", &WiFiManager::getCurrentState, this); - Register("getPairedSSID", &WiFiManager::getPairedSSID, this); - Register("getPairedSSIDInfo", &WiFiManager::getPairedSSIDInfo, this); - Register("getSupportedSecurityModes", &WiFiManager::getSupportedSecurityModes, this); - Register("initiateWPSPairing", &WiFiManager::initiateWPSPairing, this); - GetHandler(2)->Register("initiateWPSPairing", &WiFiManager::initiateWPSPairing, this); - Register("isPaired", &WiFiManager::isPaired, this); - Register("saveSSID", &WiFiManager::saveSSID, this); - Register("startScan", &WiFiManager::startScan, this); - Register("stopScan", &WiFiManager::stopScan, this); - } - - /** - * Unregister all our JSON-RPC methods - */ - void WiFiManager::UnregisterLegacyMethods() - { - Unregister("cancelWPSPairing"); - Unregister("clearSSID"); - Unregister("connect"); - Unregister("disconnect"); - Unregister("getConnectedSSID"); - Unregister("getCurrentState"); - Unregister("getPairedSSID"); - Unregister("getPairedSSIDInfo"); - Unregister("getSupportedSecurityModes"); - Unregister("initiateWPSPairing"); - Unregister("isPaired"); - Unregister("saveSSID"); - Unregister("startScan"); - Unregister("stopScan"); - } - - uint32_t WiFiManager::cancelWPSPairing (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StopWPS"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["result"] = string(); - } - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::clearSSID (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - tmpParameters["ssid"] = "ssid"; // The input ssid name does not matter at this point in time as there is only one ssid persisted at any given point in time. - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("RemoveKnownSSID"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["result"] = 0; - } - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::connect(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("WiFiConnect"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::getConnectedSSID (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpResponse; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetConnectedSSID"), parameters, tmpResponse); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["ssid"] = tmpResponse["ssid"]; - response["bssid"] = tmpResponse["bssid"]; - response["rate"] = tmpResponse["rate"]; - response["noise"] = tmpResponse["noise"]; - response["security"] = tmpResponse["securityMode"]; - response["signalStrength"] = tmpResponse["signalStrength"]; - response["frequency"] = tmpResponse["frequency"]; - response["success"] = tmpResponse["success"]; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::getCurrentState(const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetWifiState"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::getPairedSSID(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetKnownSSIDs"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::getPairedSSIDInfo(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetConnectedSSID"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::getSupportedSecurityModes(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("GetSupportedSecurityModes"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::isPaired (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - LOGINFOMETHOD(); - JsonObject tmpResponse; - - rc = getPairedSSID(parameters, tmpResponse); - - if (Core::ERROR_NONE == rc) - { - JsonArray array = tmpResponse["ssids"].Array(); - if (0 == array.Length()) - { - response["result"] = 1; - } - else - { - response["result"] = 0; - } - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::saveSSID (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("AddToKnownSSIDs"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["result"] = 0; - } - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::disconnect (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - - LOGINFOMETHOD(); - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("WiFiDisconnect"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["result"] = 0; - } - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::initiateWPSPairing (const JsonObject& parameters, JsonObject& response) - { - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - LOGINFOMETHOD(); - if (parameters.HasLabel("method")) - { - string method = parameters["method"].String(); - if (method == "PBC") - { - tmpParameters["method"] = 0; - } - else if (method == "PIN") - { - tmpParameters["method"] = 1; - tmpParameters["wps_pin"] = parameters.HasLabel("wps_pin"); - } - else if (method == "SERIALIZED_PIN") - { - tmpParameters["method"] = 2; - } - } - else - tmpParameters["method"] = 0; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StartWPS"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["result"] = string(); - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::startScan(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - JsonObject tmpParameters; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StartWiFiScan"), tmpParameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t WiFiManager::stopScan(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (m_networkmanager) - rc = m_networkmanager->Invoke(5000, _T("StopWiFiScan"), parameters, response); - else - rc = Core::ERROR_UNAVAILABLE; - - LOGTRACEMETHODFIN(); - return rc; - } - - /** Private */ - void WiFiManager::subscribeToEvents(void) - { - uint32_t errCode = Core::ERROR_GENERAL; - if (m_networkmanager) - { - if (!m_subsWiFiStateChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onWiFiStateChange"), &WiFiManager::onWiFiStateChange); - if (Core::ERROR_NONE == errCode) - m_subsWiFiStateChange = true; - else - NMLOG_ERROR ("Subscribe to onInterfaceStateChange failed, errCode: %u", errCode); - } - - if (!m_subsAvailableSSIDs) - { - errCode = m_networkmanager->Subscribe(5000, _T("onAvailableSSIDs"), &WiFiManager::onAvailableSSIDs); - if (Core::ERROR_NONE == errCode) - m_subsAvailableSSIDs = true; - else - NMLOG_ERROR("Subscribe to onIPAddressChange failed, errCode: %u", errCode); - } - - if (!m_subsWiFiStrengthChange) - { - errCode = m_networkmanager->Subscribe(5000, _T("onWiFiSignalStrengthChange"), &WiFiManager::onWiFiSignalStrengthChange); - if (Core::ERROR_NONE == errCode) - m_subsWiFiStrengthChange = true; - else - NMLOG_ERROR("Subscribe to onActiveInterfaceChange failed, errCode: %u", errCode); - } - } - - if (m_subsWiFiStateChange && m_subsAvailableSSIDs && m_subsWiFiStrengthChange) - m_timer.stop(); - } - - bool WiFiManager::ErrorCodeMapping(const uint32_t ipvalue, uint32_t &opvalue) - { - bool ret = true; - - switch (ipvalue) - { - case Exchange::INetworkManager::WIFI_STATE_SSID_CHANGED: - opvalue = WIFI_SSID_CHANGED; - break; - case Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST: - opvalue = WIFI_CONNECTION_LOST; - break; - case Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED: - opvalue = WIFI_CONNECTION_FAILED; - break; - case Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED: - opvalue = WIFI_CONNECTION_INTERRUPTED; - break; - case Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS: - opvalue = WIFI_INVALID_CREDENTIALS; - break; - case Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND: - opvalue = WIFI_NO_SSID; - break; - case Exchange::INetworkManager::WIFI_STATE_ERROR: - opvalue = WIFI_UNKNOWN; - break; - case Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED: - opvalue = WIFI_AUTH_FAILED; - break; - default: - ret = false; - break; - } - return ret; - } - - /** Event Handling and Publishing */ - void WiFiManager::onWiFiStateChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - JsonObject legacyResult; - JsonObject legacyErrorResult; - uint32_t errorCode; - uint32_t state = parameters["state"].Number(); - - legacyResult["state"] = parameters["state"]; - legacyResult["isLNF"] = false; - - if(_gWiFiInstance) - { - if(ErrorCodeMapping(state, errorCode)) - { - legacyErrorResult["code"] = errorCode; - NMLOG_INFO("onError with errorcode as, %u", errorCode); - _gWiFiInstance->Notify("onError", legacyErrorResult); - } - else - { - NMLOG_INFO("onWiFiStateChange with state as: %u", state); - _gWiFiInstance->Notify("onWIFIStateChanged", legacyResult); - } - } - return; - } - - void WiFiManager::onAvailableSSIDs(const JsonObject& parameters) - { - LOGINFOMETHOD(); - - if(_gWiFiInstance) - _gWiFiInstance->Notify("onAvailableSSIDs", parameters); - - return; - } - - void WiFiManager::onWiFiSignalStrengthChange(const JsonObject& parameters) - { - LOGINFOMETHOD(); - JsonObject legacyParams; - legacyParams["signalStrength"] = parameters["signalQuality"]; - legacyParams["strength"] = parameters["signalLevel"]; - - if (_gWiFiInstance) - _gWiFiInstance->Notify("onWifiSignalThresholdChanged", legacyParams); - - return; - } - } -} diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000..422ac085c --- /dev/null +++ b/NOTICE @@ -0,0 +1,8 @@ +This component contains software that is Copyright (c) 2023 RDK Management. +The component is licensed to you under the Apache License, Version 2.0 (the "License"). +You may not use the component except in compliance with the License. + +The component may include material which is licensed under other licenses / copyrights as +listed below. Your use of this material within the component is also subject to the terms and +conditions of these licenses. The LICENSE file contains the text of all the licenses which apply +within this component. diff --git a/NetworkManager.config b/NetworkManager.config deleted file mode 100644 index c11c09bd3..000000000 --- a/NetworkManager.config +++ /dev/null @@ -1,26 +0,0 @@ -set(autostart true) -set(callsign "org.rdk.NetworkManager") - -map() - key(root) - map() - kv(outofprocess true) - end() - key(connectivity) - map() - kv(endpoint_1 ${PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_1}) - kv(endpoint_2 ${PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_2}) - kv(endpoint_3 ${PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_3}) - kv(endpoint_4 ${PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_4}) - kv(endpoint_5 ${PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_5}) - kv(interval, @PLUGIN_NETWORKMANAGER_CONN_MONITOR_INTERVAL@) - end() - key(stun) - map() - kv(endpoint, ${PLUGIN_NETWORKMANAGER_STUN_ENDPOINT}) - kv(port, PLUGIN_NETWORKMANAGER_STUN_PORT) - kv(interval, 30) - end() - kv(loglevel, 3) -end() -ans(configuration) diff --git a/NetworkManagerConnectivity.cpp b/NetworkManagerConnectivity.cpp deleted file mode 100644 index e75334de3..000000000 --- a/NetworkManagerConnectivity.cpp +++ /dev/null @@ -1,727 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include - -#include "NetworkManagerImplementation.h" -#include "NetworkManagerConnectivity.h" -#include "NetworkManagerLogger.h" - -namespace WPEFramework -{ - namespace Plugin - { - - extern NetworkManagerImplementation* _instance; - - static const char* getInternetStateString(nsm_internetState state) - { - switch(state) - { - case NO_INTERNET: return "NO_INTERNET"; - case LIMITED_INTERNET: return "LIMITED_INTERNET"; - case CAPTIVE_PORTAL: return "CAPTIVE_PORTAL"; - case FULLY_CONNECTED: return "FULLY_CONNECTED"; - default: return "UNKNOWN"; - } - } - - bool EndpointCache::isEndpointCashFileExist() - { - std::ifstream fileStream(CachefilePath); - return fileStream.is_open(); - } - - void EndpointCache::writeEnpointsToFile(const std::vector& endpoints) - { - std::ofstream outputFile(CachefilePath); - if (outputFile.is_open()) - { - for (const std::string& str : endpoints) - { - outputFile << str << '\n'; - } - outputFile.close(); - } - else - { - NMLOG_ERROR("Connectivity endpoints file write error"); - } - } - - std::vector EndpointCache::readEnpointsFromFile() - { - std::vector readStrings; - std::ifstream inputFile(CachefilePath); - if (inputFile.is_open()) - { - std::string line; - while (std::getline(inputFile, line)) - { - readStrings.push_back(line); - } - inputFile.close(); - } - else - { - NMLOG_ERROR("Failed to open connectivity endpoint cache file"); - } - return readStrings; - } - - TestConnectivity::TestConnectivity(const std::vector& endpoints, long timeout_ms, bool headReq, nsm_ipversion ipversion) - { - internetSate = UNKNOWN; - if(endpoints.size() < 1) { - NMLOG_ERROR("Endpoints size error ! curl check not possible"); - return; - } - - internetSate = checkCurlResponse(endpoints, timeout_ms, headReq, ipversion); - } - - static bool curlVerboseEnabled() { - std::ifstream fileStream("/tmp/nm.plugin.debug"); - return fileStream.is_open(); - } - - static long current_time () - { - struct timespec ts; - clock_gettime (CLOCK_MONOTONIC, &ts); - return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); - } - static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) { - #ifdef DBG_CURL_GET_RESPONSE - LOG_DBG("%s",(char*)ptr); - #endif - return size * nmemb; - } - - nsm_internetState TestConnectivity::checkCurlResponse(const std::vector& endpoints, long timeout_ms, bool headReq, nsm_ipversion ipversion) - { - long deadline = current_time() + timeout_ms, time_now = 0, time_earlier = 0; - - CURLM *curl_multi_handle = curl_multi_init(); - if (!curl_multi_handle) - { - NMLOG_ERROR("curl_multi_init returned NULL"); - return NO_INTERNET; - } - - CURLMcode mc; - std::vector curl_easy_handles; - std::vector http_responses; - struct curl_slist *chunk = NULL; - chunk = curl_slist_append(chunk, "Cache-Control: no-cache, no-store"); - chunk = curl_slist_append(chunk, "Connection: close"); - for (const auto& endpoint : endpoints) - { - CURL *curl_easy_handle = curl_easy_init(); - if (!curl_easy_handle) - { - NMLOG_ERROR("endpoint = <%s> curl_easy_init returned NULL", endpoint.c_str()); - continue; - } - curl_easy_setopt(curl_easy_handle, CURLOPT_URL, endpoint.c_str()); - curl_easy_setopt(curl_easy_handle, CURLOPT_PRIVATE, endpoint.c_str()); - /* set our custom set of headers */ - curl_easy_setopt(curl_easy_handle, CURLOPT_HTTPHEADER, chunk); - curl_easy_setopt(curl_easy_handle, CURLOPT_USERAGENT, "RDKCaptiveCheck/1.0"); - if(!headReq) - { - // NMLOG_TRACE("curlopt get request"); - /* HTTPGET request added insted of HTTPHEAD request fix for DELIA-61526 */ - curl_easy_setopt(curl_easy_handle, CURLOPT_HTTPGET, 1L); - } - curl_easy_setopt(curl_easy_handle, CURLOPT_WRITEFUNCTION, writeFunction); - curl_easy_setopt(curl_easy_handle, CURLOPT_TIMEOUT_MS, deadline - current_time()); - if ((ipversion == CURL_IPRESOLVE_V4) || (ipversion == CURL_IPRESOLVE_V6)) - { - NMLOG_INFO("curlopt ipversion = %s reqtyp = %s", ipversion == CURL_IPRESOLVE_V4?"ipv4 only":"ipv6 only", headReq? "HEAD":"GET"); - curl_easy_setopt(curl_easy_handle, CURLOPT_IPRESOLVE, ipversion); - } - else - { - NMLOG_INFO("curlopt ipversion = whatever reqtyp = %s", headReq? "HEAD":"GET"); - } - if(curlVerboseEnabled()) - curl_easy_setopt(curl_easy_handle, CURLOPT_VERBOSE, 1L); - if (CURLM_OK != (mc = curl_multi_add_handle(curl_multi_handle, curl_easy_handle))) - { - NMLOG_ERROR("endpoint = <%s> curl_multi_add_handle returned %d (%s)", endpoint.c_str(), mc, curl_multi_strerror(mc)); - curl_easy_cleanup(curl_easy_handle); - continue; - } - curl_easy_handles.push_back(curl_easy_handle); - } - int handles, msgs_left; - char *url = nullptr; - #if LIBCURL_VERSION_NUM < 0x074200 - int numfds, repeats = 0; - #endif - char *endpoint = nullptr; - while (1) - { - if (CURLM_OK != (mc = curl_multi_perform(curl_multi_handle, &handles))) - { - NMLOG_ERROR("curl_multi_perform returned %d (%s)", mc, curl_multi_strerror(mc)); - break; - } - for (CURLMsg *msg; NULL != (msg = curl_multi_info_read(curl_multi_handle, &msgs_left)); ) - { - long response_code = -1; - if (msg->msg != CURLMSG_DONE) - continue; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &endpoint); - if (CURLE_OK == msg->data.result) { - if (curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) - { - //NMLOG_TRACE("endpoint = <%s> http response code <%d>", endpoint, static_cast(response_code)); - if (HttpStatus_302_Found == response_code) { - if ( (curl_easy_getinfo(msg->easy_handle, CURLINFO_REDIRECT_URL, &url) == CURLE_OK) && url != nullptr) { - NMLOG_TRACE("captive portal found !!!"); - captivePortalURI = url; - } - } - } - } - else - NMLOG_ERROR("endpoint = <%s> curl error = %d (%s)", endpoint, msg->data.result, curl_easy_strerror(msg->data.result)); - http_responses.push_back(response_code); - } - time_earlier = time_now; - time_now = current_time(); - if (handles == 0 || time_now >= deadline) - break; - #if LIBCURL_VERSION_NUM < 0x074200 - if (CURLM_OK != (mc = curl_multi_wait(curl_multi_handle, NULL, 0, deadline - time_now, &numfds))) - { - LOGERR("curl_multi_wait returned %d (%s)", mc, curl_multi_strerror(mc)); - break; - } - if (numfds == 0) - { - repeats++; - if (repeats > 1) - usleep(10*1000); /* sleep 10 ms */ - } - else - repeats = 0; - #else - if (CURLM_OK != (mc = curl_multi_poll(curl_multi_handle, NULL, 0, deadline - time_now, NULL))) - { - NMLOG_ERROR("curl_multi_poll returned %d (%s)", mc, curl_multi_strerror(mc)); - break; - } - #endif - } - - if(curlVerboseEnabled()) { - NMLOG_TRACE("endpoints count = %d response count %d, handles = %d, deadline = %ld, time_now = %ld, time_earlier = %ld", - static_cast(endpoints.size()), static_cast(http_responses.size()), handles, deadline, time_now, time_earlier); - } - - for (const auto& curl_easy_handle : curl_easy_handles) - { - curl_easy_getinfo(curl_easy_handle, CURLINFO_PRIVATE, &endpoint); - //LOG_DBG("endpoint = <%s> terminating attempt", endpoint); - curl_multi_remove_handle(curl_multi_handle, curl_easy_handle); - curl_easy_cleanup(curl_easy_handle); - } - curl_multi_cleanup(curl_multi_handle); - /* free the custom headers */ - curl_slist_free_all(chunk); - return checkInternetStateFromResponseCode(http_responses); - } - - /* - * verifying Most occurred response code is 50 % or more - * Example 1 : - * if we have 5 endpoints so response also 5 ( 204 302 204 204 200 ) . Here count is 204 :- 3, 302 :- 1, 200 :- 1 - * Return Internet State: FULLY_CONNECTED - 60 % - * Example 2 : - * if we have 4 endpoints so response also 4 ( 204 204 200 200 ) . Here count is 204 :- 2, 200 :- 2 - * Return Internet State: FULLY_CONNECTED - 50 % - */ - - nsm_internetState TestConnectivity::checkInternetStateFromResponseCode(const std::vector& responses) - { - nsm_internetState InternetConnectionState = NO_INTERNET; - nsm_connectivity_httpcode http_response_code = HttpStatus_response_error; - - int max_count = 0; - for (int element : responses) - { - int element_count = count(responses.begin(), responses.end(), element); - if (element_count > max_count) - { - http_response_code = static_cast(element); - max_count = element_count; - } - } - - /* Calculate the percentage of the most frequent code occurrences */ - float percentage = (static_cast(max_count) / responses.size()); - - /* 50 % connectivity check */ - if (percentage >= 0.5) - { - switch (http_response_code) - { - case HttpStatus_204_No_Content: - InternetConnectionState = FULLY_CONNECTED; - NMLOG_INFO("Internet State: FULLY_CONNECTED - %.1f%%", (percentage*100)); - break; - case HttpStatus_200_OK: - InternetConnectionState = LIMITED_INTERNET; - NMLOG_INFO("Internet State: LIMITED_INTERNET - %.1f%%", (percentage*100)); - break; - case HttpStatus_511_Authentication_Required: - case HttpStatus_302_Found: - InternetConnectionState = CAPTIVE_PORTAL; - NMLOG_INFO("Internet State: CAPTIVE_PORTAL - %.1f%%", (percentage*100)); - break; - default: - InternetConnectionState = NO_INTERNET; - if(http_response_code == -1) - NMLOG_ERROR("Internet State: NO_INTERNET (curl error)"); - else - NMLOG_WARNING("Internet State: NO_INTERNET (http code: %d - %.1f%%)", static_cast(http_response_code), percentage * 100); - break; - } - } - return InternetConnectionState; - } - - ConnectivityMonitor::ConnectivityMonitor() - { - if(endpointCache.isEndpointCashFileExist()) - { - std::vector cachedEndPnt = endpointCache.readEnpointsFromFile(); - setConnectivityMonitorEndpoints(cachedEndPnt); - NMLOG_WARNING("cached connectivity endpoints loaded .."); - } - else - connectivityMonitorEndpt.push_back("http://clients3.google.com/generate_204"); - - doContinuousMonitor = false; - doConnectivityMonitor = false; - gInternetState = nsm_internetState::UNKNOWN; - gIpv4InternetState = nsm_internetState::UNKNOWN; - gIpv6InternetState = nsm_internetState::UNKNOWN; - ginterfaceStatus = false; - } - - ConnectivityMonitor::~ConnectivityMonitor() - { - NMLOG_WARNING("~ConnectivityMonitor"); - doContinuousMonitor = false; - doConnectivityMonitor = false; - cvConnectivityMonitor.notify_one(); - cvContinuousMonitor.notify_one(); - if (continuousMonitorThrd.joinable()) - continuousMonitorThrd.join(); - if (connectivityMonitorThrd.joinable()) - connectivityMonitorThrd.join(); - } - - std::vector ConnectivityMonitor::getConnectivityMonitorEndpoints() - { - const std::lock_guard lock(endpointMutex); - std::vector endpoints; - for (auto endpoint : connectivityMonitorEndpt) { - endpoints.push_back(endpoint); - } - return endpoints; - } - - void ConnectivityMonitor::setConnectivityMonitorEndpoints(const std::vector &endpoints) - { - const std::lock_guard lock(endpointMutex); - connectivityMonitorEndpt.clear(); - for (auto endpoint : endpoints) { - if(!endpoint.empty() && endpoint.size() > 3) - connectivityMonitorEndpt.push_back(endpoint.c_str()); - else - NMLOG_ERROR("endpoint not vallied = %s", endpoint.c_str()); - } - - // write the endpoints to a file - endpointCache.writeEnpointsToFile(connectivityMonitorEndpt); - - std::string endpointsStr; - for (const auto& endpoint : connectivityMonitorEndpt) - endpointsStr.append(endpoint).append(" "); - NMLOG_INFO("Connectivity monitor endpoints -: %d :- %s", static_cast(connectivityMonitorEndpt.size()), endpointsStr.c_str()); - } - - bool ConnectivityMonitor::isConnectedToInternet(nsm_ipversion ipversion) - { - if (nsm_internetState::FULLY_CONNECTED == getInternetState(ipversion)) - { - NMLOG_INFO("isConnectedToInternet %s = true", (ipversion == nsm_ipversion::NSM_IPRESOLVE_WHATEVER)?"":(ipversion == nsm_ipversion::NSM_IPRESOLVE_V4? "IPv4":"IPv6")); - return true; - } - NMLOG_WARNING("isConnectedToInternet %s = false",(ipversion == nsm_ipversion::NSM_IPRESOLVE_WHATEVER)?"":(ipversion == nsm_ipversion::NSM_IPRESOLVE_V4? "IPv4":"IPv6") ); - return false; - } - - nsm_internetState ConnectivityMonitor::getInternetState(nsm_ipversion ipversion) - { - nsm_internetState internetState = nsm_internetState::UNKNOWN; - // If monitor connectivity is running take the cache value - - if ( doContinuousMonitor && (nsm_ipversion::NSM_IPRESOLVE_V4 == ipversion || nsm_ipversion::NSM_IPRESOLVE_WHATEVER == ipversion) - && gIpv4InternetState != nsm_internetState::UNKNOWN ) { - NMLOG_WARNING("Reading Ipv4 internet state cached value %s", getInternetStateString(gIpv4InternetState)); - internetState = gIpv4InternetState; - } - else if ( doContinuousMonitor && (nsm_ipversion::NSM_IPRESOLVE_V6 == ipversion || nsm_ipversion::NSM_IPRESOLVE_WHATEVER == ipversion) - && gIpv6InternetState != nsm_internetState::UNKNOWN ) { - NMLOG_WARNING("Reading Ipv6 internet state cached value %s", getInternetStateString(gIpv6InternetState)); - internetState = gIpv6InternetState; - } - else - { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, NMCONNECTIVITY_CURL_GET_REQUEST, ipversion); - internetState = testInternet.getInternetState(); - } - return internetState; - } - - std::string ConnectivityMonitor::getCaptivePortalURI() - { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, NMCONNECTIVITY_CURL_GET_REQUEST, NSM_IPRESOLVE_WHATEVER); - if(nsm_internetState::CAPTIVE_PORTAL == testInternet.getInternetState()) - { - NMLOG_WARNING("captive portal URI = %s", testInternet.getCaptivePortal().c_str()); - return testInternet.getCaptivePortal(); - } - NMLOG_WARNING("No captive portal found !"); - return std::string(""); - } - - bool ConnectivityMonitor::startContinuousConnectivityMonitor(int timeoutInSeconds) - { - continuousMonitorTimeout.store(timeoutInSeconds >= NMCONNECTIVITY_MONITOR_MIN_INTERVAL ? timeoutInSeconds : NMCONNECTIVITY_MONITOR_DEFAULT_INTERVAL); - if (doContinuousMonitor) - { - if(doConnectivityMonitor) - { - NMLOG_INFO("continuous monitor new timeout updated %d Sec", continuousMonitorTimeout.load()); - } - else - { - NMLOG_INFO("continuous monitor restarted with %d Sec", continuousMonitorTimeout.load()); - cvContinuousMonitor.notify_one(); - } - return true; - } - - if (continuousMonitorThrd.joinable()) { - NMLOG_WARNING("continuous monitor joinable thread running"); - doContinuousMonitor = false; - continuousMonitorThrd.join(); - } - - doContinuousMonitor = true; - continuousMonitorThrd = std::thread(&ConnectivityMonitor::continuousMonitorFunction, this); - NMLOG_INFO("continuous connectivity monitor started with %d Sec", continuousMonitorTimeout.load()); - if(!continuousMonitorThrd.joinable()) { - NMLOG_ERROR("continuous connectivity monitor start Failed"); - return false; - } - return true; - } - - bool ConnectivityMonitor::stopContinuousConnectivityMonitor() - { - doContinuousMonitor = false; - cvContinuousMonitor.notify_one(); - if (continuousMonitorThrd.joinable()) - continuousMonitorThrd.join(); - NMLOG_INFO("continuous connectivity monitor stopped"); - return true; - } - - /* - * - * call startConnectivityMonitor function - * --> when IP address accuired - * --> when etherenet/wifi disconnected - */ - bool ConnectivityMonitor::startConnectivityMonitor(bool interfaceStatus) - { - ginterfaceStatus = interfaceStatus; /* this will give interface status connected/ disconnected */ - gInternetState = UNKNOWN; - gIpv4InternetState = UNKNOWN; - gIpv6InternetState = UNKNOWN; - if (doConnectivityMonitor) - { - cvConnectivityMonitor.notify_one(); - NMLOG_TRACE("trigger connectivity monitor thread"); - return true; - } - - if (connectivityMonitorThrd.joinable()) { // cleanup of previous thread - doConnectivityMonitor = false; - connectivityMonitorThrd.join(); - } - - doConnectivityMonitor = true; - connectivityMonitorThrd = std::thread(&ConnectivityMonitor::connectivityMonitorFunction, this); - if(!connectivityMonitorThrd.joinable()) { - NMLOG_ERROR("connectivity monitor start failed"); - return false; - } - - NMLOG_ERROR("connectivity monitor started %d", NMCONNECTIVITY_MONITOR_MIN_INTERVAL); - return true; - } - - void ConnectivityMonitor::notifyInternetStatusChangedEvent(nsm_internetState newInternetState) - { - if(_instance != nullptr) - { - Exchange::INetworkManager::InternetStatus oldState = static_cast(gInternetState.load()); - Exchange::INetworkManager::InternetStatus newState = static_cast(newInternetState); - _instance->ReportInternetStatusChangedEvent(oldState , newState); - gInternetState = newInternetState; - } - else - NMLOG_WARNING("NetworkManagerImplementation Instance NULL notifyInternetStatusChange failed."); - } - - void ConnectivityMonitor::continuousMonitorFunction() - { - int TempInterval = continuousMonitorTimeout.load(); - std::mutex connMutex; - nsm_ipversion ipResolveTyp = NSM_IPRESOLVE_WHATEVER; - int notifyPreRetry = 1; - nsm_internetState currentInternetState = nsm_internetState::UNKNOWN; - - do - { - if(doConnectivityMonitor) - { - NMLOG_TRACE("connectivity monitor running so skiping ccm check"); - gIpv4InternetState = UNKNOWN; - gIpv6InternetState = UNKNOWN; - std::unique_lock lock(connMutex); - cvContinuousMonitor.wait_for(lock, std::chrono::seconds(continuousMonitorTimeout.load())); - ipResolveTyp = NSM_IPRESOLVE_WHATEVER; /* some interface change happense*/ - continue; - } - else if (ipResolveTyp == NSM_IPRESOLVE_WHATEVER) - { - nsm_internetState ipV4InternetState = UNKNOWN; - nsm_internetState ipV6InternetState = UNKNOWN; - auto curlCheckThrdIpv4 = [&]() { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, - NMCONNECTIVITY_CURL_GET_REQUEST, NSM_IPRESOLVE_V4); - ipV4InternetState = testInternet.getInternetState(); - }; - - auto curlCheckThrdIpv6 = [&]() { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, - NMCONNECTIVITY_CURL_GET_REQUEST, NSM_IPRESOLVE_V6); - ipV6InternetState = testInternet.getInternetState(); - }; - std::thread ipv4thread(curlCheckThrdIpv4); - std::thread ipv6thread(curlCheckThrdIpv6); - // Wait for both threads to finish - ipv4thread.join(); - ipv6thread.join(); - if(ipV4InternetState == FULLY_CONNECTED) { - ipResolveTyp = NSM_IPRESOLVE_V4; - currentInternetState = ipV4InternetState; - NMLOG_INFO("connectivity monitor default ip resolve IPV4"); - } - else if(ipV6InternetState == FULLY_CONNECTED) { - ipResolveTyp = NSM_IPRESOLVE_V6; - currentInternetState = ipV6InternetState; - NMLOG_INFO("connectivity monitor default ip resolve IPV6"); - } - else /* not changing ip resolve type */ - currentInternetState = ipV4InternetState; - } - else /* IPV4 or IPV6 based on default values */ - { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, - NMCONNECTIVITY_CURL_HEAD_REQUEST, ipResolveTyp); - currentInternetState = testInternet.getInternetState(); - if(ipResolveTyp == NSM_IPRESOLVE_V4) - gIpv4InternetState = currentInternetState; - else if(ipResolveTyp == NSM_IPRESOLVE_V6) - gIpv6InternetState = currentInternetState; - } - - if (currentInternetState == NO_INTERNET) - { - if(gInternetState == FULLY_CONNECTED && notifyPreRetry < NMCONNECTIVITY_NO_INTERNET_RETRY_COUNT) - { - /* it will prevent posting notification */ - currentInternetState = gInternetState; - TempInterval = 5; - NMLOG_INFO("No internet retrying connection check %d ...", notifyPreRetry); - notifyPreRetry++; - /* no internet state retry do it in ipv4 and ipv6 sepratly in two thread */ - ipResolveTyp = NSM_IPRESOLVE_WHATEVER; - } - else - { - notifyPreRetry = 1; - TempInterval = continuousMonitorTimeout.load(); - } - } - else { - notifyPreRetry = 1; - TempInterval = continuousMonitorTimeout.load(); - } - - if(gInternetState != currentInternetState) - { - NMLOG_INFO("Internet state changed to %s", getInternetStateString(currentInternetState)); - /* Notify Internet state change */ - notifyInternetStatusChangedEvent(currentInternetState); - } - - //NMLOG_INFO("icm %d, ccm %d", doConnectivityMonitor.load(), doContinuousMonitor.load()); - if(!doContinuousMonitor) - break; - - /* wait for next interval */ - std::unique_lock lock(connMutex); - if (cvContinuousMonitor.wait_for(lock, std::chrono::seconds(TempInterval)) != std::cv_status::timeout) - NMLOG_INFO("continous connectivity monitor recieved signal. skping %d sec interval", TempInterval); - - } while(doContinuousMonitor); - - gIpv4InternetState = UNKNOWN; - gIpv6InternetState = UNKNOWN; - NMLOG_TRACE("continous connectivity monitor exit"); - } - - void ConnectivityMonitor::connectivityMonitorFunction() - { - int TempInterval = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; - std::mutex connMutex; - bool notifyNow = true; - int notifyPreRetry = 1; - nsm_internetState currentInternetState = nsm_internetState::UNKNOWN; - nsm_internetState tempInternetState = nsm_internetState::UNKNOWN; - - do - { - TestConnectivity testInternet(getConnectivityMonitorEndpoints(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, - NMCONNECTIVITY_CURL_GET_REQUEST, NSM_IPRESOLVE_WHATEVER); - currentInternetState = testInternet.getInternetState(); - if(currentInternetState == CAPTIVE_PORTAL) - { - /* set to every 30 sec interval */ - TempInterval = NMCONNECTIVITY_CONN_MONITOR_RETRY_INTERVAL; - } - else if(currentInternetState == LIMITED_INTERNET) - { - TempInterval = NMCONNECTIVITY_CONN_MONITOR_RETRY_INTERVAL; - } - else // fullyconnect / noInternet - { - if(notifyPreRetry <= NMCONNECTIVITY_CONN_MONITOR_RETRY_COUNT - 1) - { - if(tempInternetState != currentInternetState ) // check for continous same state - { - tempInternetState = currentInternetState; - notifyPreRetry = 1; - NMLOG_INFO("Connectivity check retrying %d ...", notifyPreRetry); - } - else - { - notifyPreRetry++; - NMLOG_INFO("Connectivity check retrying %d ...", notifyPreRetry); - } - - if(gInternetState != UNKNOWN) - currentInternetState = gInternetState; - TempInterval = 5; - } - else if(tempInternetState != currentInternetState) // last state have change - { - tempInternetState = currentInternetState; - notifyPreRetry = 1; - TempInterval = 5; - NMLOG_INFO("Connectivity check retrying %d ...", notifyPreRetry); - } - else - { - if(currentInternetState == FULLY_CONNECTED) - { - doConnectivityMonitor = false; // self exit - notifyNow = true; // post current state when retry complete - } - else if(ginterfaceStatus) // interface is active and still no internet, continue check every 30 sec - { - TempInterval = NMCONNECTIVITY_CONN_MONITOR_RETRY_INTERVAL; - /* notify if retry completed and state stil no internet state */ - if(notifyPreRetry == NMCONNECTIVITY_CONN_MONITOR_RETRY_COUNT) - { - notifyPreRetry++; - notifyNow = true; - } - } - else // no interface connected - { - doConnectivityMonitor = false; - notifyNow = true; - } - } - } - - if(gInternetState != currentInternetState || notifyNow) - { - notifyNow = false; - NMLOG_INFO("notify internet state %s", getInternetStateString(currentInternetState)); - notifyInternetStatusChangedEvent(currentInternetState); - } - - if(!doConnectivityMonitor) - break; - /* wait for next interval */ - std::unique_lock lock(connMutex); - if (cvConnectivityMonitor.wait_for(lock, std::chrono::seconds(TempInterval)) != std::cv_status::timeout) { - NMLOG_INFO("connectivity monitor recieved signal. skping %d sec interval", TempInterval); - notifyPreRetry = 1; - notifyNow = true; // new signal came should notify in next check - } - - } while(doConnectivityMonitor); - - if(!doContinuousMonitor) - gInternetState = nsm_internetState::UNKNOWN; // no continous monitor running reset to unknow - NMLOG_TRACE("initial connectivity monitor exit"); - } - - } // namespace Plugin -} // namespace WPEFramework diff --git a/NetworkManagerConnectivity.h b/NetworkManagerConnectivity.h deleted file mode 100644 index 2a39ce234..000000000 --- a/NetworkManagerConnectivity.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum nsm_ipversion -{ - NSM_IPRESOLVE_WHATEVER = 0, /* default, resolves addresses to all IP*/ - NSM_IPRESOLVE_V4 = 1, /* resolve to IPv4 addresses */ - NSM_IPRESOLVE_V6 = 2 /* resolve to IPv6 addresses */ -}; - -enum nsm_internetState { - NO_INTERNET, - LIMITED_INTERNET, - CAPTIVE_PORTAL, - FULLY_CONNECTED, - UNKNOWN, -}; - -enum nsm_connectivity_httpcode { - HttpStatus_response_error = 99, - HttpStatus_200_OK = 200, - HttpStatus_204_No_Content = 204, - HttpStatus_301_Moved_Permanentl = 301, - HttpStatus_302_Found = 302, // captive portal - HttpStatus_307_Temporary_Redirect = 307, - HttpStatus_308_Permanent_Redirect = 308, - HttpStatus_403_Forbidden = 403, - HttpStatus_404_Not_Found = 404, - HttpStatus_511_Authentication_Required = 511 // captive portal RFC 6585 -}; - -#define NMCONNECTIVITY_CURL_HEAD_REQUEST true -#define NMCONNECTIVITY_CURL_GET_REQUEST false - -#define NMCONNECTIVITY_MONITOR_DEFAULT_INTERVAL 60 // sec -#define NMCONNECTIVITY_MONITOR_MIN_INTERVAL 5 // sec -#define NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS 5000 // ms -#define NMCONNECTIVITY_NO_INTERNET_RETRY_COUNT 4 // 4 retry -#define NMCONNECTIVITY_CONN_MONITOR_RETRY_COUNT 3 // 3 retry -#define NMCONNECTIVITY_CAPTIVE_MONITOR_INTERVAL 30 // sec -#define NMCONNECTIVITY_CONN_MONITOR_RETRY_INTERVAL 30 // sec - -namespace WPEFramework -{ - namespace Plugin - { - /* save user specific endponint in to a chache file and load form the file if monitorEndpoints are empty case wpeframework restared */ - class EndpointCache { - public: - bool isEndpointCashFileExist(); - void writeEnpointsToFile(const std::vector& endpoints); - std::vector readEnpointsFromFile(); - - EndpointCache() : CachefilePath("/tmp/nm.plugin.endpoints") {} - ~EndpointCache(){} - private: - std::string CachefilePath; - }; - - class TestConnectivity - { - TestConnectivity(const TestConnectivity&) = delete; - const TestConnectivity& operator=(const TestConnectivity&) = delete; - - public: - TestConnectivity(const std::vector& endpoints, long timeout_ms = 2000, bool = true, nsm_ipversion ipversion = NSM_IPRESOLVE_WHATEVER); - ~TestConnectivity(){} - std::string getCaptivePortal() {return captivePortalURI;} - nsm_internetState getInternetState(){return internetSate;} - private: - nsm_internetState checkCurlResponse(const std::vector& endpoints, long timeout_ms, bool headReq, nsm_ipversion ipversion); - nsm_internetState checkInternetStateFromResponseCode(const std::vector& responses); - std::string captivePortalURI; - nsm_internetState internetSate; - }; - - class ConnectivityMonitor - { - public: - ConnectivityMonitor(); - ~ConnectivityMonitor(); - bool startContinuousConnectivityMonitor(int timeoutInSeconds); - bool stopContinuousConnectivityMonitor(); - bool startConnectivityMonitor(bool interfaceStatus); - void setConnectivityMonitorEndpoints(const std::vector &endpoints); - std::vector getConnectivityMonitorEndpoints(); - bool isConnectedToInternet(nsm_ipversion ipversion); - nsm_internetState getInternetState(nsm_ipversion ipversion); - std::string getCaptivePortalURI(); - - private: - ConnectivityMonitor(const ConnectivityMonitor&) = delete; - ConnectivityMonitor& operator=(const ConnectivityMonitor&) = delete; - void connectivityMonitorFunction(); - void notifyInternetStatusChangedEvent(nsm_internetState newState); - /* connectivity monitor */ - std::thread connectivityMonitorThrd; - std::condition_variable cvConnectivityMonitor; - std::atomic continuousMonitorTimeout; - std::atomic doConnectivityMonitor; - std::vector connectivityMonitorEndpt; - /*continuous connectivity monitor */ - std::atomic doContinuousMonitor; - std::thread continuousMonitorThrd; - std::condition_variable cvContinuousMonitor; - void continuousMonitorFunction(); - - EndpointCache endpointCache; - std::mutex endpointMutex; - std::atomic ginterfaceStatus; - std::atomic gInternetState; - std::atomic gIpv4InternetState; - std::atomic gIpv6InternetState; - }; - } // namespace Plugin -} // namespace WPEFramework diff --git a/NetworkManagerGnomeEvents.cpp b/NetworkManagerGnomeEvents.cpp deleted file mode 100644 index f4dfcce8a..000000000 --- a/NetworkManagerGnomeEvents.cpp +++ /dev/null @@ -1,620 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "Module.h" -#include "NetworkManagerGnomeEvents.h" -#include "NetworkManagerLogger.h" -#include "NetworkManagerGnomeUtils.h" -#include "NetworkManagerImplementation.h" -#include "INetworkManager.h" - -namespace WPEFramework -{ - namespace Plugin - { - - extern NetworkManagerImplementation* _instance; - static GnomeNetworkManagerEvents *_nmEventInstance = nullptr; - - // const char* ifnameEth = "enx207bd51e02ad"; - // const char* ifnameWlan = "wlp0s20f3"; - - static void primaryConnectionCb(NMClient *client, GParamSpec *param, NMEvents *nmEvents) - { - NMActiveConnection *primaryConn; - const char *activeConnId = NULL; - const char *connectionTyp = NULL; - primaryConn = nm_client_get_primary_connection(client); - nmEvents->activeConn = primaryConn; - if (primaryConn) - { - activeConnId = nm_active_connection_get_id(primaryConn); - connectionTyp = nm_active_connection_get_connection_type(primaryConn); - NMLOG_INFO("active connection - %s (%s)", activeConnId, connectionTyp); - std::string newIface =""; - - if (0 == strncmp("802-3-ethernet", connectionTyp, sizeof("802-3-ethernet"))) - newIface = "eth0"; - else if(0 == strncmp("802-11-wireless", connectionTyp, sizeof("802-11-wireless"))) - newIface = "wlan0"; - else - NMLOG_WARNING("active connection not an ethernet/wifi %s", connectionTyp); - - GnomeNetworkManagerEvents::onActiveInterfaceChangeCb(newIface); - } - else - NMLOG_ERROR("now there's no active connection"); - } - - static void deviceStateChangeCb(NMDevice *device, GParamSpec *pspec, NMEvents *nmEvents) - { - NMDeviceState deviceState; - deviceState = nm_device_get_state(device); - std::string ifname = nm_device_get_iface(device); - if(ifname == nmEvents->ifnameWlan0) - { - if(!NM_IS_DEVICE_WIFI(device)) { - NMLOG_FATAL("not a wifi device !"); - return; - } - NMDeviceStateReason reason = nm_device_get_state_reason(device); - std::string wifiState; - // NMLOG_TRACE("\033[0;31m NMDeviceStateReason %d \033[0m",reason); - switch (reason) - { - case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: - wifiState = "WIFI_STATE_UNINSTALLED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED); - break; - case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: - wifiState = "WIFI_STATE_SSID_NOT_FOUND"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND); - break; - case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: // supplicant took too long to authenticate - case NM_DEVICE_STATE_REASON_NO_SECRETS: - wifiState = "WIFI_STATE_AUTHENTICATION_FAILED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED); - break; - case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: // 802.1x supplicant failed - wifiState = "WIFI_STATE_ERROR"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_ERROR); - break; - case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: // 802.1x supplicant configuration failed - wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED); - break; - case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: // 802.1x supplicant disconnected - wifiState = "WIFI_STATE_INVALID_CREDENTIALS"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS); - break; - default: - { - switch (deviceState) - { - case NM_DEVICE_STATE_UNKNOWN: - wifiState = "WIFI_STATE_UNINSTALLED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED); - break; - case NM_DEVICE_STATE_UNMANAGED: - wifiState = "WIFI_STATE_DISABLED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISABLED); - break; - case NM_DEVICE_STATE_UNAVAILABLE: - case NM_DEVICE_STATE_DISCONNECTED: - wifiState = "WIFI_STATE_DISCONNECTED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISCONNECTED); - break; - case NM_DEVICE_STATE_PREPARE: - wifiState = "WIFI_STATE_PAIRING"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_PAIRING); - break; - case NM_DEVICE_STATE_CONFIG: - wifiState = "WIFI_STATE_CONNECTING"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTING); - break; - case NM_DEVICE_STATE_IP_CONFIG: - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP,"wlan0"); - break; - case NM_DEVICE_STATE_ACTIVATED: - wifiState = "WIFI_STATE_CONNECTED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTED); - break; - case NM_DEVICE_STATE_DEACTIVATING: - wifiState = "WIFI_STATE_CONNECTION_LOST"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST); - break; - case NM_DEVICE_STATE_FAILED: - wifiState = "WIFI_STATE_CONNECTION_FAILED"; - GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED); - break; - case NM_DEVICE_STATE_NEED_AUTH: - //GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED); - //wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; - break; - default: - wifiState = "Un handiled"; - } - } - } - NMLOG_TRACE("wifi state: %s", wifiState.c_str()); - } - else if(ifname == nmEvents->ifnameEth0) - { - switch (deviceState) - { - case NM_DEVICE_STATE_UNKNOWN: - case NM_DEVICE_STATE_UNMANAGED: - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_DISABLED, "eth0"); - break; - case NM_DEVICE_STATE_UNAVAILABLE: - case NM_DEVICE_STATE_DISCONNECTED: - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, "eth0"); - break; - case NM_DEVICE_STATE_PREPARE: - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, "eth0"); - break; - case NM_DEVICE_STATE_IP_CONFIG: - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP,"eth0"); - case NM_DEVICE_STATE_NEED_AUTH: - case NM_DEVICE_STATE_SECONDARIES: - case NM_DEVICE_STATE_ACTIVATED: - case NM_DEVICE_STATE_DEACTIVATING: - default: - NMLOG_WARNING("Unhandiled state change"); - } - } - - NMLOG_TRACE("%s state: (%d)", ifname.c_str(), deviceState); - - } - - static void ip4ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) - { - if (!ipConfig) { - NMLOG_ERROR("IP config is null"); - return; - } - - NMDevice *device = (NMDevice*)userData; - if((device == NULL) || (!NM_IS_DEVICE(device))) - return; - - const char* iface = nm_device_get_iface(device); - if(iface == NULL) - return; - std::string ifname = iface; - - GPtrArray *addresses = nm_ip_config_get_addresses(ipConfig); - if (!addresses) { - NMLOG_ERROR("No addresses found"); - return; - } - else { - if(addresses->len == 0) { - GnomeNetworkManagerEvents::onAddressChangeCb(ifname, "", false, false); - return; - } - } - - for (guint i = 0; i < addresses->len; ++i) { - NMIPAddress *address = (NMIPAddress *)g_ptr_array_index(addresses, i); - if (nm_ip_address_get_family(address) == AF_INET) { - const char *ipAddress = nm_ip_address_get_address(address); - if(ipAddress != NULL) - GnomeNetworkManagerEvents::onAddressChangeCb(iface, ipAddress, true, false); - } - } - } - - static void ip6ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) - { - if (!ipConfig) { - NMLOG_ERROR("ip config is null"); - return; - } - - NMDevice *device = (NMDevice*)userData; - if( ((device != NULL) && NM_IS_DEVICE(device)) ) - { - const char* iface = nm_device_get_iface(device); - if(iface == NULL) - return; - std::string ifname = iface; - GPtrArray *addresses = nm_ip_config_get_addresses(ipConfig); - if (!addresses) { - NMLOG_ERROR("No addresses found"); - return; - } - else { - if(addresses->len == 0) { - GnomeNetworkManagerEvents::onAddressChangeCb(ifname, "", false, true); - return; - } - } - - for (guint i = 0; i < addresses->len; ++i) { - NMIPAddress *address = (NMIPAddress *)g_ptr_array_index(addresses, i); - if (nm_ip_address_get_family(address) == AF_INET6) { - const char *ipAddress = nm_ip_address_get_address(address); - //int prefix = nm_ip_address_get_prefix(address); - if(ipAddress != NULL) { - GnomeNetworkManagerEvents::onAddressChangeCb(iface, ipAddress, true, true); - } - } - } - } - } - - static void deviceAddedCB(NMClient *client, NMDevice *device, NMEvents *nmEvents) - { - if( ((device != NULL) && NM_IS_DEVICE(device)) ) - { - std::string ifname = nm_device_get_iface(device); - if(ifname == nmEvents->ifnameWlan0) { - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, "wlan0"); - } - else if(ifname == nmEvents->ifnameEth0) { - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, "eth0"); - } - else { - // TODO Check and remove if not needed - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, ifname); - } - /* ip events added only for eth0 and wlan0 */ - if((ifname == nmEvents->ifnameEth0) || (ifname == nmEvents->ifnameWlan0)) - { - g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(deviceStateChangeCb), nmEvents); - // TODO call notify::" NM_DEVICE_ACTIVE_CONNECTION if needed - NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); - NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); - if (ipv4Config) { - g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); - } - - if (ipv6Config) { - g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); - } - } - } - else - NMLOG_TRACE("device error null"); - } - - static void deviceRemovedCB(NMClient *client, NMDevice *device, NMEvents *nmEvents) - { - if( ((device != NULL) && NM_IS_DEVICE(device)) ) - { - std::string ifname = nm_device_get_iface(device); - if(ifname == nmEvents->ifnameWlan0) { - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED,"wlan0"); - g_signal_handlers_disconnect_by_func(device, (gpointer)deviceStateChangeCb, nmEvents); - } - else if(ifname == nmEvents->ifnameEth0) { - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, "eth0"); - g_signal_handlers_disconnect_by_func(device, (gpointer)deviceStateChangeCb, nmEvents); - } - else { - // TODO Check and remove if not needed - GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, ifname); - } - } - - // guint disconnected_count = g_signal_handlers_disconnect_matched( _nmEventInstance->activeConn, - // G_SIGNAL_MATCH_FUNC, - // 0, 0, NULL, - // (gpointer)onActiveConnectionStateChanged, - // NULL ); - // NMLOG_ERROR("Disconnected %u signal handlers\n", disconnected_count); - - } - - static void clientStateChangedCb (NMClient *client, GParamSpec *pspec, gpointer user_data) - { - - switch (nm_client_get_state (client)) { - case NM_STATE_DISCONNECTED: - NMLOG_WARNING("internet connection down"); - break; - case NM_STATE_CONNECTED_GLOBAL: - NMLOG_TRACE("global internet connection success"); - break; - default: - break; - } - } - - static void managerRunningCb (NMClient *client, GParamSpec *pspec, gpointer user_data) - { - if (nm_client_get_nm_running (client)) { - NMLOG_INFO("network manager daemon is running"); - } else { - NMLOG_FATAL("network manager daemon not running !"); - // TODO check need any client reconnection or not ? - } - } - - void* GnomeNetworkManagerEvents::networkMangerEventMonitor(void *arg) - { - if(arg == nullptr) - { - NMLOG_FATAL("function argument error: nm event monitor failed"); - return nullptr; - } - - NMEvents *nmEvents = static_cast(arg); - primaryConnectionCb(nmEvents->client, NULL, nmEvents); - g_signal_connect (nmEvents->client, "notify::" NM_CLIENT_NM_RUNNING,G_CALLBACK (managerRunningCb), nmEvents); - g_signal_connect(nmEvents->client, "notify::" NM_CLIENT_STATE, G_CALLBACK (clientStateChangedCb),nmEvents); - g_signal_connect(nmEvents->client, "notify::" NM_CLIENT_PRIMARY_CONNECTION, G_CALLBACK(primaryConnectionCb), nmEvents); - - const GPtrArray *devices = nullptr; - devices = nm_client_get_devices(nmEvents->client); - - g_signal_connect(nmEvents->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK(deviceAddedCB), nmEvents); - g_signal_connect(nmEvents->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK(deviceRemovedCB), nmEvents); - - for (u_int count = 0; count < devices->len; count++) - { - NMDevice *device = NM_DEVICE(g_ptr_array_index(devices, count)); - if( ((device != NULL) && NM_IS_DEVICE(device)) ) - { - g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(deviceStateChangeCb), nmEvents); - //g_signal_connect(device, "notify::" NM_DEVICE_ACTIVE_CONNECTION, G_CALLBACK(deviceActiveConnChangeCb), NULL); - std::string ifname = nm_device_get_iface(device); - if((ifname == nmEvents->ifnameEth0) || (ifname == nmEvents->ifnameWlan0)) /* ip events added only for eth0 and wlan0 */ - { - NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); - NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); - if (ipv4Config) { - g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); - } - - if (ipv6Config) { - g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); - } - - if(NM_IS_DEVICE_WIFI(device)) { - nmEvents->wifiDevice = NM_DEVICE_WIFI(device); - g_signal_connect(nmEvents->wifiDevice, "notify::" NM_DEVICE_WIFI_LAST_SCAN, G_CALLBACK(GnomeNetworkManagerEvents::onAvailableSSIDsCb), nmEvents); - } - } - else - NMLOG_TRACE("device type not eth/wifi"); - } - } - - NMLOG_INFO("registered all networkmnager dbus events"); - g_main_loop_run(nmEvents->loop); - //g_main_loop_unref(nmEvents->loop); - return nullptr; - } - - bool GnomeNetworkManagerEvents::startNetworkMangerEventMonitor() - { - NMLOG_TRACE("starting gnome event monitor"); - if (NULL == nmEvents.client) { - NMLOG_ERROR("Client Connection NULL DBUS event Failed!"); - return false; - } - if(!isEventThrdActive) { - isEventThrdActive = true; - // Create event monitor thread - eventThrdID = g_thread_new("nm_event_thrd", GnomeNetworkManagerEvents::networkMangerEventMonitor, &nmEvents); - } - return true; - } - - void GnomeNetworkManagerEvents::stopNetworkMangerEventMonitor() - { - // g_signal_handlers_disconnect_by_func(client, G_CALLBACK(primaryConnectionCb), NULL); - if (nmEvents.loop != NULL) { - g_main_loop_quit(nmEvents.loop); - } - if (eventThrdID) { - g_thread_join(eventThrdID); // Wait for the thread to finish - eventThrdID = NULL; // Reset the thread ID - NMLOG_WARNING("gnome event monitor stoped"); - } - isEventThrdActive = false; - } - - GnomeNetworkManagerEvents::~GnomeNetworkManagerEvents() - { - NMLOG_INFO("~GnomeNetworkManagerEvents"); - stopNetworkMangerEventMonitor(); - if(nmEvents.client != nullptr) - g_object_unref(nmEvents.client); - if (nmEvents.loop != NULL) { - g_main_loop_unref(nmEvents.loop); - nmEvents.loop = NULL; - } - } - - GnomeNetworkManagerEvents* GnomeNetworkManagerEvents::getInstance() - { - static GnomeNetworkManagerEvents instance; - return &instance; - } - - GnomeNetworkManagerEvents::GnomeNetworkManagerEvents() - { - NMLOG_TRACE("GnomeNetworkManagerEvents"); - GError *error = NULL; - nmEvents.client = nm_client_new(NULL, &error); - if(!nmEvents.client || error ) - { - if (error) { - NMLOG_ERROR("Could not connect to NetworkManager: %s", error->message); - g_error_free(error); - } - NMLOG_INFO("networkmanger client connection failed"); - return; - } - - NMLOG_INFO("libnm client connection success version: %s", nm_client_get_version(nmEvents.client)); - nmEvents.loop = g_main_loop_new(NULL, FALSE); - if(nmEvents.loop == NULL) { - NMLOG_FATAL("GMain loop failed Fatal Error: Event will not work"); - return; - } - _nmEventInstance = this; - std::string wifiInterface = "wlan0", ethernetInterface = "eth0"; - nmUtils::GetInterfacesName(wifiInterface, ethernetInterface); - nmEvents.ifnameEth0 = ethernetInterface; - nmEvents.ifnameWlan0 = wifiInterface; - } - - /* Gnome networkmanger new events */ - - void GnomeNetworkManagerEvents::onActiveInterfaceChangeCb(std::string newIface) - { - static std::string oldIface = "Unknown"; - - if(oldIface != newIface) - { - if(_instance != nullptr) - _instance->ReportActiveInterfaceChangedEvent(oldIface, newIface); - NMLOG_INFO("old interface - %s new interface - %s", oldIface.c_str(), newIface.c_str()); - oldIface = newIface; - } - } - - void GnomeNetworkManagerEvents::onInterfaceStateChangeCb(uint8_t newState, std::string iface) - { - std::string state = ""; - switch (newState) - { - case Exchange::INetworkManager::INTERFACE_ADDED: - state = "INTERFACE_ADDED"; - break; - case Exchange::INetworkManager::INTERFACE_LINK_UP: - state = "INTERFACE_LINK_UP"; - break; - case Exchange::INetworkManager::INTERFACE_LINK_DOWN: - state = "INTERFACE_LINK_DOWN"; - break; - case Exchange::INetworkManager::INTERFACE_ACQUIRING_IP: - state = "INTERFACE_ACQUIRING_IP"; - break; - case Exchange::INetworkManager::INTERFACE_REMOVED: - state = "INTERFACE_REMOVED"; - break; - case Exchange::INetworkManager::INTERFACE_DISABLED: - state = "INTERFACE_DISABLED"; - break; - default: - state = "Unknown"; - } - NMLOG_INFO("%s interface state changed - %s", iface.c_str(), state.c_str()); - if(_instance != nullptr) - _instance->ReportInterfaceStateChangedEvent(static_cast(newState), iface); - } - - void GnomeNetworkManagerEvents::onWIFIStateChanged(uint8_t state) - { - if(_instance != nullptr) - _instance->ReportWiFiStateChangedEvent(static_cast(state)); - } - - void GnomeNetworkManagerEvents::onAddressChangeCb(std::string iface, std::string ipAddress, bool acqired, bool isIPv6) - { - static std::map ipv6Map; - static std::map ipv4Map; - - if (isIPv6) - { - if (ipAddress.empty()) { - ipAddress = ipv6Map[iface]; - ipv6Map[iface].clear(); - } - else { - if (ipv6Map[iface].find(ipAddress) == std::string::npos) { // same ip comes multiple time so avoding that - if (!ipv6Map[iface].empty()) - ipv6Map[iface] += " "; - ipv6Map[iface] += ipAddress; // SLAAC protocol may include multip ipv6 address - } - else - return; // skip same ip event posting - } - } - else - { - if (ipAddress.empty()) - ipAddress = ipv4Map[iface]; - else - ipv4Map[iface] = ipAddress; - } - - if(_instance != nullptr) - _instance->ReportIPAddressChangedEvent(iface, acqired, true, ipAddress); - NMLOG_INFO("iface:%s - ipaddress:%s - %s - isIPv6:%s", iface.c_str(), ipAddress.c_str(), acqired?"acquired":"lost", isIPv6?"true":"false"); - } - - void GnomeNetworkManagerEvents::onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData) - { - NMLOG_INFO("wifi scanning completed ..."); - if(!NM_IS_DEVICE_WIFI(wifiDevice)) - { - NMLOG_ERROR("Not a wifi object "); - return; - } - JsonArray ssidList = JsonArray(); - string ssidListJson; - NMAccessPoint *ap = nullptr; - const GPtrArray *accessPoints = nm_device_wifi_get_access_points(wifiDevice); - for (guint i = 0; i < accessPoints->len; i++) - { - JsonObject ssidObj; - ap = static_cast(accessPoints->pdata[i]); - ssidObj = nmUtils::apToJsonObject(ap); - ssidList.Add(ssidObj); - } - - ssidList.ToString(ssidListJson); - if(_nmEventInstance->debugLogs) { - _nmEventInstance->debugLogs = false; - NMLOG_TRACE("Number of Access Points Available = %d", static_cast(accessPoints->len)); - NMLOG_TRACE("Scanned APIs are = %s",ssidListJson.c_str()); - } - - if(!_nmEventInstance->stopWifiScan) { - _instance->ReportAvailableSSIDsEvent(ssidListJson); - } - } - - void GnomeNetworkManagerEvents::setwifiScanOptions(bool doScan, bool enableLogs) - { - stopWifiScan.store(doScan); - if(stopWifiScan) - { - NMLOG_WARNING("stop periodic wifi scan result"); - } - debugLogs = enableLogs; - } - - } // Plugin -} // WPEFramework diff --git a/NetworkManagerGnomeEvents.h b/NetworkManagerGnomeEvents.h deleted file mode 100644 index e63c1b1e9..000000000 --- a/NetworkManagerGnomeEvents.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once -#include -#include -#include -#include -#include - -namespace WPEFramework -{ - namespace Plugin - { - - typedef struct { - NMClient *client; - GMainLoop *loop; - NMDevice *device; - NMDeviceWifi *wifiDevice; - NMActiveConnection *activeConn; - std::string ifnameWlan0; - std::string ifnameEth0; - } NMEvents; - - class GnomeNetworkManagerEvents - { - - public: - static void onInterfaceStateChangeCb(uint8_t newState, std::string iface); // ReportInterfaceStateChangedEvent - static void onAddressChangeCb(std::string iface, std::string ipAddress, bool acqired, bool isIPv6); // ReportIPAddressChangedEvent - static void onActiveInterfaceChangeCb(std::string newInterface); // ReportActiveInterfaceChangedEvent - static void onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData); // ReportAvailableSSIDsEvent - static void onWIFIStateChanged(uint8_t state); // ReportWiFiStateChangedEvent - - public: - static GnomeNetworkManagerEvents* getInstance(); - bool startNetworkMangerEventMonitor(); - void stopNetworkMangerEventMonitor(); - void setwifiScanOptions(bool doScan, bool enableLogs = false); - - private: - static void* networkMangerEventMonitor(void *arg); - GnomeNetworkManagerEvents(); - ~GnomeNetworkManagerEvents(); - std::atomicisEventThrdActive = {false}; - std::atomicstopWifiScan = {false}; - std::atomicdebugLogs = {false}; - NMEvents nmEvents; - GThread *eventThrdID; - }; - - } // Plugin -} // WPEFramework diff --git a/NetworkManagerGnomeProxy.cpp b/NetworkManagerGnomeProxy.cpp deleted file mode 100644 index d3114db80..000000000 --- a/NetworkManagerGnomeProxy.cpp +++ /dev/null @@ -1,569 +0,0 @@ -#include "NetworkManagerImplementation.h" -#include "NetworkManagerGnomeWIFI.h" -#include "NetworkManagerGnomeEvents.h" -#include "NetworkManagerGnomeUtils.h" -#include -#include -#include - -static NMClient *client; - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; -using namespace std; - -static std::vector interfaceList; - -namespace WPEFramework -{ - namespace Plugin - { - wifiManager *wifi = nullptr; - GnomeNetworkManagerEvents *nmEvent = nullptr; - const float signalStrengthThresholdExcellent = -50.0f; - const float signalStrengthThresholdGood = -60.0f; - const float signalStrengthThresholdFair = -67.0f; - NetworkManagerImplementation* _instance = nullptr; - - void NetworkManagerInternalEventHandler(const char *owner, int eventId, void *data, size_t len) - { - return; - } - - void NetworkManagerImplementation::platform_init() - { - ::_instance = this; - GError *error = NULL; - // initialize the NMClient object - client = nm_client_new(NULL, &error); - if (client == NULL) { - NMLOG_TRACE("Error initializing NMClient: %s", error->message); - g_error_free(error); - return; - } - - nmEvent = GnomeNetworkManagerEvents::getInstance(); - nmEvent->startNetworkMangerEventMonitor(); - wifi = wifiManager::getInstance(); - return; - } - - uint32_t NetworkManagerImplementation::GetAvailableInterfaces (Exchange::INetworkManager::IInterfaceDetailsIterator*& interfacesItr/* @out */) - { - // [Process.cpp:78](Dispatch)<1>: We still have living object [1]. - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - string interfaces[2]; - string wifiInterface; - string ethernetInterface; - NMDeviceType type; - InterfaceDetails tmp; - NMDeviceState state; - NMDevice *device = NULL; - - if(interfaceList.empty()) - { - nmUtils::GetInterfacesName(wifiInterface, ethernetInterface); - interfaces[0] = wifiInterface; - interfaces[1] = ethernetInterface; - for (size_t i = 0; i < 2; i++) { - if(!interfaces[i].empty()) - { - device = nm_client_get_device_by_iface(client, interfaces[i].c_str()); - if (device) - { - if(i == 0) - tmp.m_type = string("WIFI"); - else - tmp.m_type = string("ETHERNET"); - tmp.m_name = interfaces[i].c_str(); - tmp.m_mac = nm_device_get_hw_address(device); - state = nm_device_get_state(device); - tmp.m_isEnabled = (state > NM_DEVICE_STATE_UNAVAILABLE) ? 1 : 0; - tmp.m_isConnected = (state > NM_DEVICE_STATE_DISCONNECTED) ? 1: 0; - interfaceList.push_back(tmp); - g_clear_object(&device); - } - } - } - } - - using Implementation = RPC::IteratorType; - interfacesItr = Core::Service::Create(interfaceList); - rc = Core::ERROR_NONE; - return rc; - } - - /* @brief Get the active Interface used for external world communication */ - uint32_t NetworkManagerImplementation::GetPrimaryInterface (string& interface /* @out */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - GError *error = NULL; - NMActiveConnection *activeConn = NULL; - NMRemoteConnection *remoteConn = NULL; - - activeConn = nm_client_get_primary_connection(client); - if (activeConn == NULL) { - NMLOG_TRACE("Error getting primary connection: %s", error->message); - g_error_free(error); - return rc; - } - remoteConn = nm_active_connection_get_connection(activeConn); - - interface = nm_connection_get_interface_name(NM_CONNECTION(remoteConn)); - if(!interface.empty()) - rc = Core::ERROR_NONE; - return rc; - } - - /* @brief Set the active Interface used for external world communication */ - uint32_t NetworkManagerImplementation::SetPrimaryInterface (const string& interface/* @in */) - { - uint32_t rc = Core::ERROR_NONE; - const GPtrArray *connections = nm_client_get_connections(client); - NMConnection *conn = NULL; - NMSettingConnection *settings; - NMRemoteConnection *remoteConnection; - for (guint i = 0; i < connections->len; i++) { - NMConnection *connection = NM_CONNECTION(connections->pdata[i]); - settings = nm_connection_get_setting_connection(connection); - - /* Check if the interface name matches */ - if (g_strcmp0(nm_setting_connection_get_interface_name(settings), interface.c_str()) == 0) { - conn = connection; - break; - } - } - g_object_set(settings, - NM_SETTING_CONNECTION_AUTOCONNECT, - true, - NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY, - NM_SETTING_CONNECTION_AUTOCONNECT_PRIORITY_MAX, - NULL); - const char *uuid = nm_connection_get_uuid(conn); - remoteConnection = nm_client_get_connection_by_uuid(client, uuid); - nm_remote_connection_commit_changes(remoteConnection, false, NULL, NULL); - - return rc; - } - - uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool& enabled /* @in */) - { - uint32_t rc = Core::ERROR_NONE; - const GPtrArray *devices = nm_client_get_devices(client); - NMDevice *device = NULL; - - for (guint i = 0; i < devices->len; ++i) { - device = NM_DEVICE(g_ptr_array_index(devices, i)); - - // Get the device details - const char *name = nm_device_get_iface(device); - - // Check if the device name matches - if (g_strcmp0(name, interface.c_str()) == 0) { - nm_device_set_managed(device, enabled); - - NMLOG_TRACE("Interface %s status set to %s", interface.c_str(), enabled ? "Enabled" : "Disabled"); - } - } - - // Cleanup - if(device) - g_clear_object(&device); - return rc; - } - - uint32_t NetworkManagerImplementation::GetInterfaceState(const string& interface/* @in */, bool& isEnabled /* @out */) - { - uint32_t rc = Core::ERROR_NONE; -#if 0 //FIXME - const GPtrArray *devices = nm_client_get_devices(client); - NMDevice *device = NULL; - - for (guint i = 0; i < devices->len; ++i) { - device = NM_DEVICE(g_ptr_array_index(devices, i)); - - // Get the device details - const char *name = nm_device_get_iface(device); - - // Check if the device name matches - if (g_strcmp0(name, interface.c_str()) == 0) { - nm_device_set_managed(device, false); - - NMLOG_TRACE("Interface %s status set to disabled", - interface.c_str()); - } - } - - // Cleanup - if(device) - g_clear_object(&device); -#endif - return rc; - } - - /* @brief Get IP Address Of the Interface */ - uint32_t NetworkManagerImplementation::GetIPSettings(const string& interface /* @in */, const string& ipversion /* @in */, IPAddressInfo& result /* @out */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - GError *error = NULL; - NMActiveConnection *conn = NULL; - NMIPConfig *ip4_config = NULL; - NMIPConfig *ip6_config = NULL; - const gchar *gateway = NULL; - char **dns_arr = NULL; - NMDhcpConfig *dhcp4_config = NULL; - NMDhcpConfig *dhcp6_config = NULL; - const char* dhcpserver; - NMSettingConnection *settings; - NMIPAddress *address; - - const GPtrArray *connections = nm_client_get_active_connections(client); - - for (guint i = 0; i < connections->len; i++) { - NMActiveConnection *connection = NM_ACTIVE_CONNECTION(connections->pdata[i]); - settings = nm_connection_get_setting_connection(NM_CONNECTION(nm_active_connection_get_connection(connection))); - - /* Check if the interface name matches */ - if (g_strcmp0(nm_setting_connection_get_interface_name(settings), interface.c_str()) == 0) { - conn = connection; - break; - } - } - if (conn == NULL) { - NMLOG_TRACE("Error getting primary connection: %s", error->message); - g_error_free(error); - return rc; - } - - if(0 == strcmp(ipversion.c_str(), "IPv4")) - { - ip4_config = nm_active_connection_get_ip4_config(conn); - if (ip4_config != NULL) { - const GPtrArray *p; - int i; - p = nm_ip_config_get_addresses(ip4_config); - for (i = 0; i < p->len; i++) { - address = static_cast(p->pdata[i]); - } - gateway = nm_ip_config_get_gateway(ip4_config); - } - dns_arr = (char **)nm_ip_config_get_nameservers(ip4_config); - - dhcp4_config = nm_active_connection_get_dhcp4_config(conn); - dhcpserver = nm_dhcp_config_get_one_option (dhcp4_config, - "dhcp_server_identifier"); - - result.m_ipAddrType = ipversion.c_str(); - if(dhcpserver) - result.m_dhcpServer = dhcpserver; - result.m_v6LinkLocal = ""; - result.m_ipAddress = nm_ip_address_get_address(address); - result.m_prefix = nm_ip_address_get_prefix(address); - result.m_gateway = gateway; - if((*(&dns_arr[0]))!=NULL) - result.m_primaryDns = *(&dns_arr[0]); - if((*(&dns_arr[1]))!=NULL ) - result.m_secondaryDns = *(&dns_arr[1]); - - rc = Core::ERROR_NONE; - } - else if(0 == strcmp(ipversion.c_str(), "IPv6")) - { - NMIPAddress *a; - ip6_config = nm_active_connection_get_ip6_config(conn); - if (ip6_config != NULL) { - const GPtrArray *p; - int i; - p = nm_ip_config_get_addresses(ip6_config); - for (i = 0; i < p->len; i++) { - a = static_cast(p->pdata[i]); - result.m_ipAddress = nm_ip_address_get_address(a); - NMLOG_TRACE("\tinet6 %s/%d\n", nm_ip_address_get_address(a), nm_ip_address_get_prefix(a)); - } - gateway = nm_ip_config_get_gateway(ip6_config); - - dns_arr = (char **)nm_ip_config_get_nameservers(ip6_config); - - dhcp6_config = nm_active_connection_get_dhcp6_config(conn); - dhcpserver = nm_dhcp_config_get_one_option (dhcp6_config, - "dhcp_server_identifier"); - result.m_ipAddrType = ipversion.c_str(); - if(dhcpserver) - result.m_dhcpServer = dhcpserver; - result.m_v6LinkLocal = ""; - result.m_prefix = 0; - result.m_gateway = gateway; - if((*(&dns_arr[0]))!=NULL) - result.m_primaryDns = *(&dns_arr[0]); - if((*(&dns_arr[1]))!=NULL ) - result.m_secondaryDns = *(&dns_arr[1]); - } - rc = Core::ERROR_NONE; - } - return rc; - } - - - // Callback for nm_client_deactivate_connection_async - static void on_deactivate_complete(GObject *source_object, GAsyncResult *res, gpointer user_data) { - GError *error = NULL; - - // Check if the operation was successful - if (!nm_client_deactivate_connection_finish(NM_CLIENT(source_object), res, &error)) { - NMLOG_TRACE("Deactivating connection failed: %s", error->message); - g_error_free(error); - } else { - NMLOG_TRACE("Deactivating connection successful"); - } - } - - // Callback for nm_client_activate_connection_async - static void on_activate_complete(GObject *source_object, GAsyncResult *res, gpointer user_data) { - GError *error = NULL; - - // Check if the operation was successful - if (!nm_client_activate_connection_finish(NM_CLIENT(source_object), res, &error)) { - NMLOG_TRACE("Activating connection failed: %s", error->message); - g_error_free(error); - } else { - NMLOG_TRACE("Activating connection successful"); - } - - g_main_loop_quit((GMainLoop*)user_data); - } - - - /* @brief Set IP Address Of the Interface */ - uint32_t NetworkManagerImplementation::SetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, const IPAddressInfo& address /* @in */) - { - GMainLoop *g_loop; - g_loop = g_main_loop_new(NULL, FALSE); - uint32_t rc = Core::ERROR_NONE; - const GPtrArray *connections = nm_client_get_connections(client); - NMSettingIP4Config *s_ip4; - NMSettingIP6Config *s_ip6; - NMConnection *conn = NULL; - NMSettingConnection *settings; - NMRemoteConnection *remote_connection; - NMSetting *setting; - const char *uuid; - NMDevice *device = NULL; - const char *spec_object; - - for (guint i = 0; i < connections->len; i++) { - NMConnection *connection = NM_CONNECTION(connections->pdata[i]); - settings = nm_connection_get_setting_connection(connection); - - /* Check if the interface name matches */ - if (g_strcmp0(nm_setting_connection_get_interface_name(settings), interface.c_str()) == 0) { - conn = connection; - break; - } - } - if (!address.m_autoConfig) - { - if (strcasecmp("IPv4", ipversion.c_str()) == 0) - { - NMSettingIPConfig *ip4_config = nm_connection_get_setting_ip4_config(conn); - if (ip4_config == NULL) - { - ip4_config = (NMSettingIPConfig *)nm_setting_ip4_config_new(); - } - NMIPAddress *ipAddress; - setting = nm_connection_get_setting_by_name(conn, "ipv4"); - ipAddress = nm_ip_address_new(AF_INET, address.m_ipAddress.c_str(), address.m_prefix, NULL); - nm_setting_ip_config_clear_addresses(ip4_config); - nm_setting_ip_config_add_address(NM_SETTING_IP_CONFIG(setting), ipAddress); - nm_setting_ip_config_clear_dns(ip4_config); - nm_setting_ip_config_add_dns(ip4_config, address.m_primaryDns.c_str()); - nm_setting_ip_config_add_dns(ip4_config, address.m_secondaryDns.c_str()); - - g_object_set(G_OBJECT(ip4_config), - NM_SETTING_IP_CONFIG_GATEWAY, address.m_gateway.c_str(), - NM_SETTING_IP_CONFIG_NEVER_DEFAULT, - FALSE, - NULL); - } - else - { - //FIXME : Add IPv6 support here - printf("Setting IPv6 is not supported at this point in time. This is just a place holder\n"); - rc = Core::ERROR_NOT_SUPPORTED; - } - } - else - { - if (strcmp("IPv4", ipversion.c_str()) == 0) - { - s_ip4 = (NMSettingIP4Config *)nm_setting_ip4_config_new(); - g_object_set(G_OBJECT(s_ip4), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); - nm_connection_add_setting(conn, NM_SETTING(s_ip4)); - } - else - { - s_ip6 = (NMSettingIP6Config *)nm_setting_ip6_config_new(); - g_object_set(G_OBJECT(s_ip6), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); - nm_connection_add_setting(conn, NM_SETTING(s_ip6)); - } - } - device = nm_client_get_device_by_iface(client, interface.c_str()); - uuid = nm_connection_get_uuid(conn); - remote_connection = nm_client_get_connection_by_uuid(client, uuid); - NMActiveConnection *active_connection = NULL; - - const GPtrArray *acv_connections = nm_client_get_active_connections(client); - for (guint i = 0; i < acv_connections->len; i++) { - NMActiveConnection *connection1 = NM_ACTIVE_CONNECTION(acv_connections->pdata[i]); - settings = nm_connection_get_setting_connection(NM_CONNECTION(nm_active_connection_get_connection(connection1))); - - /* Check if the interface name matches */ - if (g_strcmp0(nm_setting_connection_get_interface_name(settings), interface.c_str()) == 0) { - active_connection = connection1; - break; - } - } - - spec_object = nm_object_get_path(NM_OBJECT(active_connection)); - nm_remote_connection_commit_changes(remote_connection, false, NULL, NULL); - nm_client_deactivate_connection_async(client, active_connection, NULL, on_deactivate_complete, NULL); - nm_client_activate_connection_async(client, conn, device, spec_object, NULL, on_activate_complete, g_loop); - g_main_loop_run(g_loop); - return rc; - } - - uint32_t NetworkManagerImplementation::StartWiFiScan(const WiFiFrequency frequency /* @in */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - nmEvent->setwifiScanOptions(false, true); - if(wifi->wifiScanRequest(frequency)) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::StopWiFiScan(void) - { - uint32_t rc = Core::ERROR_NONE; - // TODO explore wpa_supplicant stop - nmEvent->setwifiScanOptions(true); // This will stop periodic posting of onAvailableSSID event - NMLOG_INFO ("StopWiFiScan is success"); - return rc; - } - - uint32_t NetworkManagerImplementation::GetKnownSSIDs(IStringIterator*& ssids /* @out */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - // TODO Fix the RPC waring [Process.cpp:78](Dispatch)<1>: We still have living object [1] - std::list ssidList; - if(wifi->getKnownSSIDs(ssidList)) - { - if (!ssidList.empty()) - { - ssids = Core::Service::Create(ssidList); - rc = Core::ERROR_NONE; - } - else - { - NMLOG_INFO("known ssids not found !"); - rc = Core::ERROR_GENERAL; - } - } - - return rc; - } - - uint32_t NetworkManagerImplementation::AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) - { - uint32_t rc = Core::ERROR_GENERAL; - if(wifi->addToKnownSSIDs(ssid)) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::RemoveKnownSSID(const string& ssid /* @in */) - { - uint32_t rc = Core::ERROR_GENERAL; - if(wifi->removeKnownSSID(ssid)) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::WiFiConnect(const WiFiConnectTo& ssid /* @in */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - if(wifi->wifiConnect(ssid)) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::WiFiDisconnect(void) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - if(wifi->wifiDisconnect()) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - if(wifi->wifiConnectedSSIDInfo(ssidInfo)) - rc = Core::ERROR_NONE; - return rc; - } - - uint32_t NetworkManagerImplementation::GetWiFiSignalStrength(string& ssid /* @out */, string& signalStrength /* @out */, WiFiSignalQuality& quality /* @out */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - - WiFiSSIDInfo ssidInfo; - if(wifi->wifiConnectedSSIDInfo(ssidInfo)) - { - ssid = ssidInfo.m_ssid; - signalStrength = ssidInfo.m_signalStrength; - - float signalStrengthFloat = 0.0f; - if(!signalStrength.empty()) - signalStrengthFloat = std::stof(signalStrength.c_str()); - - if (signalStrengthFloat == 0) - quality = WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED; - else if (signalStrengthFloat >= signalStrengthThresholdExcellent && signalStrengthFloat < 0) - quality = WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT; - else if (signalStrengthFloat >= signalStrengthThresholdGood && signalStrengthFloat < signalStrengthThresholdExcellent) - quality = WiFiSignalQuality::WIFI_SIGNAL_GOOD; - else if (signalStrengthFloat >= signalStrengthThresholdFair && signalStrengthFloat < signalStrengthThresholdGood) - quality = WiFiSignalQuality::WIFI_SIGNAL_FAIR; - else - quality = WiFiSignalQuality::WIFI_SIGNAL_WEAK; - - NMLOG_INFO ("GetWiFiSignalStrength success"); - - rc = Core::ERROR_NONE; - } - return rc; - } - - uint32_t NetworkManagerImplementation::GetWifiState(WiFiState &state) - { - uint32_t rc = Core::ERROR_NONE; - if(wifi->isWifiConnected()) - state = Exchange::INetworkManager::WIFI_STATE_CONNECTED; - else - state = Exchange::INetworkManager::WIFI_STATE_DISCONNECTED; - return rc; - } - - uint32_t NetworkManagerImplementation::StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - return rc; - } - - uint32_t NetworkManagerImplementation::StopWPS(void) - { - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - return rc; - } - - } -} diff --git a/NetworkManagerGnomeUtils.cpp b/NetworkManagerGnomeUtils.cpp deleted file mode 100644 index 7b562dc85..000000000 --- a/NetworkManagerGnomeUtils.cpp +++ /dev/null @@ -1,284 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "Module.h" - -#include "NetworkManagerLogger.h" -#include "NetworkManagerGnomeUtils.h" -#include "NetworkManagerImplementation.h" -#include "INetworkManager.h" - -namespace WPEFramework -{ - namespace Plugin - { - uint8_t nmUtils::wifiSecurityModeFromAp(guint32 flags, guint32 wpaFlags, guint32 rsnFlags) - { - uint8_t security = Exchange::INetworkManager::WIFI_SECURITY_NONE; - if ((flags == NM_802_11_AP_FLAGS_NONE) && (wpaFlags == NM_802_11_AP_SEC_NONE) && (rsnFlags == NM_802_11_AP_SEC_NONE)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_NONE; - } - else if( (flags & NM_802_11_AP_FLAGS_PRIVACY) && ((wpaFlags & NM_802_11_AP_SEC_PAIR_WEP40) || (rsnFlags & NM_802_11_AP_SEC_PAIR_WEP40)) ) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WEP_64; - } - else if( (flags & NM_802_11_AP_FLAGS_PRIVACY) && ((wpaFlags & NM_802_11_AP_SEC_PAIR_WEP104) || (rsnFlags & NM_802_11_AP_SEC_PAIR_WEP104)) ) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WEP_128; - } - else if((wpaFlags & NM_802_11_AP_SEC_PAIR_TKIP) || (rsnFlags & NM_802_11_AP_SEC_PAIR_TKIP)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK_TKIP; - } - else if((wpaFlags & NM_802_11_AP_SEC_PAIR_CCMP) || (rsnFlags & NM_802_11_AP_SEC_PAIR_CCMP)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK_AES; - } - else if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) && (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_WPA2_ENTERPRISE; - } - else if(rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_WPA2_PSK; - } - else if((wpaFlags & NM_802_11_AP_SEC_GROUP_CCMP) || (rsnFlags & NM_802_11_AP_SEC_GROUP_CCMP)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA2_PSK_AES; - } - else if((wpaFlags & NM_802_11_AP_SEC_GROUP_TKIP) || (rsnFlags & NM_802_11_AP_SEC_GROUP_TKIP)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA2_PSK_TKIP; - } - else if((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE) || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) - { - security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA3_SAE; - } - else - NMLOG_WARNING("security mode not defined (flag: %d, wpaFlags: %d, rsnFlags: %d)", flags, wpaFlags, rsnFlags); - return security; - } - - std::string nmUtils::getSecurityModeString(guint32 flags, guint32 wpaFlags, guint32 rsnFlags) - { - switch(flags) - { - case NM_802_11_AP_FLAGS_NONE: - NMLOG_TRACE("ap type : point has no special capabilities"); - break; - case NM_802_11_AP_FLAGS_PRIVACY: - NMLOG_TRACE("ap type : access point requires authentication and encryption"); - break; - case NM_802_11_AP_FLAGS_WPS: - NMLOG_TRACE("ap type : access point supports some WPS method"); - break; - case NM_802_11_AP_FLAGS_WPS_PBC: - NMLOG_TRACE("ap type : access point supports push-button WPS"); - break; - case NM_802_11_AP_FLAGS_WPS_PIN: - NMLOG_TRACE("ap type : access point supports PIN-based WPS"); - break; - default: - NMLOG_ERROR("ap type : 802.11 flags unknown!"); - } - - std::string securityStr; - - if (!(flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags != NM_802_11_AP_SEC_NONE) && (rsnFlags != NM_802_11_AP_SEC_NONE)) - securityStr += ("Encrypted: "); - - if ((flags & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) - && (rsnFlags == NM_802_11_AP_SEC_NONE)) - securityStr += ("WEP "); - if (wpaFlags != NM_802_11_AP_SEC_NONE) - securityStr += ("WPA "); - if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - securityStr += ("WPA2 "); - } - if (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { - securityStr += ("WPA3 "); - } - if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE) - || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { - securityStr += ("OWE "); - } - if ((wpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { - securityStr += ("802.1X "); - } - - if (securityStr.empty()) - { - securityStr = "None"; - return securityStr; - } - - uint32_t flags[2] = { wpaFlags, rsnFlags }; - securityStr += "[ WPA Flags: "; - - for (int i = 0; i < 2; ++i) - { - if (flags[i] & NM_802_11_AP_SEC_PAIR_WEP40) - securityStr += "pair_wep40 "; - if (flags[i] & NM_802_11_AP_SEC_PAIR_WEP104) - securityStr += "pair_wep104 "; - if (flags[i] & NM_802_11_AP_SEC_PAIR_TKIP) - securityStr += "pair_tkip "; - if (flags[i] & NM_802_11_AP_SEC_PAIR_CCMP) - securityStr += "pair_ccmp "; - if (flags[i] & NM_802_11_AP_SEC_GROUP_WEP40) - securityStr += "group_wep40 "; - if (flags[i] & NM_802_11_AP_SEC_GROUP_WEP104) - securityStr += "group_wep104 "; - if (flags[i] & NM_802_11_AP_SEC_GROUP_TKIP) - securityStr += "group_tkip "; - if (flags[i] & NM_802_11_AP_SEC_GROUP_CCMP) - securityStr += "group_ccmp "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_PSK) - securityStr += "psk "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_802_1X) - securityStr += "802.1X "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_SAE) - securityStr += "sae "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_OWE) - securityStr += "owe "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM) - securityStr += "owe_transition_mode "; - if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192) - securityStr += "wpa-eap-suite-b-192 "; - - if (i == 0) { - securityStr += "] [ RSN Flags: "; - } - } - securityStr +="]"; - return securityStr; - } - - std::string nmUtils::wifiFrequencyFromAp(guint32 apFreq) - { - std:string freq; - if (apFreq >= 2400 && apFreq < 5000) - freq = "2.4"; - else if (apFreq >= 5000 && apFreq < 6000) - freq = "5"; - else if (apFreq >= 6000) - freq = "6"; - else - freq = "Not available"; - - return freq; - } - - JsonObject nmUtils::apToJsonObject(NMAccessPoint *ap) - { - GError *error = NULL; - GBytes *ssid = NULL; - int strength = 0; - std::string freq; - int security; - guint32 flags, wpaFlags, rsnFlags, apFreq; - JsonObject ssidObj; - if(ap == nullptr) - return ssidObj; - ssid = nm_access_point_get_ssid(ap); - if (ssid) - { - char *ssidStr = nullptr; - ssidStr = nm_utils_ssid_to_utf8((const guint8*)g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); - string ssidString(ssidStr); - ssidObj["ssid"] = ssidString; - } - else - ssidObj["ssid"] = "---"; // hidden ssid TODO modify - strength = nm_access_point_get_strength(ap); - apFreq = nm_access_point_get_frequency(ap); - flags = nm_access_point_get_flags(ap); - wpaFlags = nm_access_point_get_wpa_flags(ap); - rsnFlags = nm_access_point_get_rsn_flags(ap); - freq = nmUtils::wifiFrequencyFromAp(apFreq); - security = nmUtils::wifiSecurityModeFromAp(flags, wpaFlags, rsnFlags); - - ssidObj["security"] = security; - ssidObj["signalStrength"] = strength; - ssidObj["frequency"] = freq; - - return ssidObj; - } - - void nmUtils::printActiveSSIDsOnly(NMDeviceWifi *wifiDevice) - { - if(!NM_IS_DEVICE_WIFI(wifiDevice)) - { - NMLOG_ERROR("Not a wifi object "); - return; - } - const GPtrArray *accessPointsArray = nm_device_wifi_get_access_points(wifiDevice); - for (guint i = 0; i < accessPointsArray->len; i++) - { - NMAccessPoint *ap = NULL; - GBytes *ssidGByte = NULL; - std::string ssid; - - ap = (NMAccessPoint*)accessPointsArray->pdata[i]; - ssidGByte = nm_access_point_get_ssid(ap); - if(ssidGByte) - { - char* ssidStr = NULL; - gsize len; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssidGByte, &len)); - ssidStr = nm_utils_ssid_to_utf8(ssidData, len); - if(ssidStr != NULL) { - std::string ssidTmp(ssidStr, len); - ssid = ssidTmp; - } - else - ssid = "---"; - } - else - ssid = "---"; - - NMLOG_INFO("ssid: %s", ssid.c_str()); - } - } - - - uint32_t nmUtils::GetInterfacesName(string &wifiInterface, string ðernetInterface) { - string line; - uint32_t rc = Core::ERROR_GENERAL; - - ifstream file("/etc/device.properties"); - if (!file.is_open()) { - NMLOG_WARNING("/etc/device.properties opening file Error "); - return rc; - } - - while (std::getline(file, line)) { - // Remove newline character if present - if (!line.empty() && line.back() == '\n') { - line.pop_back(); - } - - istringstream iss(line); - string token; - getline(iss, token, '='); - - if (token == "WIFI_INTERFACE") { - std::getline(iss, wifiInterface, '='); - } else if (token == "ETHERNET_INTERFACE") { - std::getline(iss, ethernetInterface, '='); - } - } - file.close(); - return Core::ERROR_NONE; - } - - } // Plugin -} // WPEFramework diff --git a/NetworkManagerGnomeWIFI.cpp b/NetworkManagerGnomeWIFI.cpp deleted file mode 100644 index 8ea90346c..000000000 --- a/NetworkManagerGnomeWIFI.cpp +++ /dev/null @@ -1,884 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include -#include -#include -#include - -#include -#include -#include -#include "NetworkManagerLogger.h" -#include "INetworkManager.h" -#include "NetworkManagerGnomeWIFI.h" -#include "NetworkManagerGnomeUtils.h" - -namespace WPEFramework -{ - namespace Plugin - { - - wifiManager* wifiManager::getInstance() - { - static wifiManager instance; - return &instance; - } - - wifiManager::~wifiManager() { - NMLOG_INFO("~wifiManager"); - g_main_context_pop_thread_default(nmContext); - if(loop != NULL) - g_main_loop_unref(loop); - if(client != NULL) - g_object_unref(client); - } - - wifiManager::wifiManager() : client(nullptr), loop(nullptr), createNewConnection(false) { - nmContext = g_main_context_new(); - g_main_context_push_thread_default(nmContext); - loop = g_main_loop_new(nmContext, FALSE); - } - - bool wifiManager::createClientNewConnection() - { - GError *error = NULL; - if(client != nullptr) - { - g_object_unref(client); - client = nullptr; - } - - client = nm_client_new(NULL, &error); - if (!client || !loop) { - NMLOG_ERROR("Could not connect to NetworkManager: %s.", error->message); - g_error_free(error); - return false; - } - return true; - } - - bool wifiManager::quit(NMDevice *wifiNMDevice) - { - if (wifiNMDevice && wifiDeviceStateGsignal > 0) { - g_signal_handler_disconnect(wifiNMDevice, wifiDeviceStateGsignal); - wifiDeviceStateGsignal = 0; - } - - if(!g_main_loop_is_running(loop)) { - NMLOG_ERROR("g_main_loop_is not running"); - return false; - } - - g_main_loop_quit(loop); - return false; - } - - static gboolean gmainLoopTimoutCB(gpointer user_data) - { - wifiManager *_wifiManager = (static_cast(user_data)); - NMLOG_WARNING("GmainLoop ERROR_TIMEDOUT"); - _wifiManager->isSuccess = false; - g_main_loop_quit(_wifiManager->loop); - return true; - } - - bool wifiManager::wait(GMainLoop *loop, int timeOutMs) - { - if(g_main_loop_is_running(loop)) { - NMLOG_WARNING("g_main_loop_is running"); - return false; - } - source = g_timeout_source_new(10000); // 10000ms interval - g_source_set_callback(source, (GSourceFunc)gmainLoopTimoutCB, this, NULL); - g_source_attach(source, NULL); - g_main_loop_run(loop); - if(source != nullptr) { - if(g_source_is_destroyed(source)) { - NMLOG_WARNING("Source has been destroyed"); - } - else { - g_source_destroy(source); - } - g_source_unref(source); - } - return true; - } - - NMDevice* wifiManager::getNmDevice() - { - NMDevice *wifiDevice = NULL; - - GPtrArray *devices = const_cast(nm_client_get_devices(client)); - if (devices == NULL) { - NMLOG_ERROR("Failed to get device list."); - return wifiDevice; - } - - for (guint j = 0; j < devices->len; j++) { - NMDevice *device = NM_DEVICE(devices->pdata[j]); - if (nm_device_get_device_type(device) == NM_DEVICE_TYPE_WIFI) - { - wifiDevice = device; - //NMLOG_TRACE("Wireless Device found ifce : %s !", nm_device_get_iface (wifiDevice)); - break; - } - } - - if (wifiDevice == NULL || !NM_IS_DEVICE_WIFI(wifiDevice)) - { - NMLOG_ERROR("Wireless Device not found !"); - } - - return wifiDevice; - } - - bool static getConnectedSSID(NMDeviceWifi *wifiDevice, std::string& ssidin) - { - GBytes *ssid; - NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(wifiDevice); - if(activeAP == NULL) { - return false; - } - - ssid = nm_access_point_get_ssid(activeAP); - gsize size; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssid, &size)); - std::string ssidTmp(reinterpret_cast(ssidData), size); - ssidin = ssidTmp; - NMLOG_INFO("connected ssid: %s", ssidin.c_str()); - return true; - } - - static void getApInfo(NMAccessPoint *AccessPoint, Exchange::INetworkManager::WiFiSSIDInfo &wifiInfo) - { - guint32 flags, wpaFlags, rsnFlags, freq, bitrate; - guint8 strength; - GBytes *ssid; - const char *hwaddr; - NM80211Mode mode; - /* Get AP properties */ - flags = nm_access_point_get_flags(AccessPoint); - wpaFlags = nm_access_point_get_wpa_flags(AccessPoint); - rsnFlags = nm_access_point_get_rsn_flags(AccessPoint); - ssid = nm_access_point_get_ssid(AccessPoint); - hwaddr = nm_access_point_get_bssid(AccessPoint); - freq = nm_access_point_get_frequency(AccessPoint); - mode = nm_access_point_get_mode(AccessPoint); - bitrate = nm_access_point_get_max_bitrate(AccessPoint); - strength = nm_access_point_get_strength(AccessPoint); - - /* Convert to strings */ - if (ssid) { - gsize size; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssid, &size)); - std::string ssidTmp(reinterpret_cast(ssidData), size); - wifiInfo.m_ssid = ssidTmp; - NMLOG_INFO("ssid: %s", wifiInfo.m_ssid.c_str()); - } - else - { - wifiInfo.m_ssid = "-----"; - NMLOG_TRACE("ssid: %s", wifiInfo.m_ssid.c_str()); - } - - wifiInfo.m_bssid = (hwaddr != nullptr) ? hwaddr : "-----"; - NMLOG_TRACE("bssid: %s", wifiInfo.m_bssid.c_str()); - - if (freq >= 2400 && freq < 5000) { - wifiInfo.m_frequency = Exchange::INetworkManager::WiFiFrequency::WIFI_FREQUENCY_2_4_GHZ; - NMLOG_TRACE("freq: WIFI_FREQUENCY_2_4_GHZ"); - } - else if (freq >= 5000 && freq < 6000) { - wifiInfo.m_frequency = Exchange::INetworkManager::WiFiFrequency::WIFI_FREQUENCY_5_GHZ; - NMLOG_TRACE("freq: WIFI_FREQUENCY_5_GHZ"); - } - else if (freq >= 6000) { - wifiInfo.m_frequency = Exchange::INetworkManager::WiFiFrequency::WIFI_FREQUENCY_6_GHZ; - NMLOG_TRACE("freq: WIFI_FREQUENCY_6_GHZ"); - } - else { - wifiInfo.m_frequency = Exchange::INetworkManager::WiFiFrequency::WIFI_FREQUENCY_WHATEVER; - NMLOG_TRACE("freq: No available !"); - } - - wifiInfo.m_rate = std::to_string(bitrate); - NMLOG_TRACE("bitrate : %s kbit/s", wifiInfo.m_rate.c_str()); - //TODO signal strenght to dBm - wifiInfo.m_signalStrength = std::to_string(static_cast(strength)); - NMLOG_TRACE("sterngth: %s %%", wifiInfo.m_signalStrength.c_str()); - wifiInfo.m_securityMode = static_cast(nmUtils::wifiSecurityModeFromAp(flags, wpaFlags, rsnFlags)); - NMLOG_TRACE("security %s", nmUtils::getSecurityModeString(flags, wpaFlags, rsnFlags).c_str()); - NMLOG_TRACE("Mode: %s", mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc": mode == NM_802_11_MODE_INFRA ? "Infrastructure": "Unknown"); - } - - bool wifiManager::isWifiConnected() - { - if(!createClientNewConnection()) - return false; - - NMDeviceWifi *wifiDevice = NM_DEVICE_WIFI(getNmDevice()); - if(wifiDevice == NULL) { - NMLOG_TRACE("NMDeviceWifi * NULL !"); - return false; - } - - NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(wifiDevice); - if(activeAP == NULL) { - NMLOG_ERROR("No active access point found !"); - return false; - } - else - NMLOG_TRACE("active access point found !"); - return true; - } - - bool wifiManager::wifiConnectedSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo) - { - if(!createClientNewConnection()) - return false; - - NMDeviceWifi *wifiDevice = NM_DEVICE_WIFI(getNmDevice()); - if(wifiDevice == NULL) { - NMLOG_TRACE("NMDeviceWifi * NULL !"); - return false; - } - - NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(wifiDevice); - if(activeAP == NULL) { - NMLOG_ERROR("No active access point found !"); - return false; - } - else - NMLOG_TRACE("active access point found !"); - - getApInfo(activeAP, ssidinfo); - return true; - } - - static void wifiDisconnectCb(GObject *object, GAsyncResult *result, gpointer user_data) - { - NMDevice *device = NM_DEVICE(object); - GError *error = NULL; - wifiManager *_wifiManager = (static_cast(user_data)); - - NMLOG_TRACE("Disconnecting... "); - _wifiManager->isSuccess = true; - if (!nm_device_disconnect_finish(device, result, &error)) { - if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - return; - - NMLOG_ERROR("Device '%s' (%s) disconnecting failed: %s", - nm_device_get_iface(device), - nm_object_get_path(NM_OBJECT(device)), - error->message); - g_error_free(error); - _wifiManager->quit(device); - _wifiManager->isSuccess = false; - } - _wifiManager->quit(device); - } - - bool wifiManager::wifiDisconnect() - { - if(!createClientNewConnection()) - return false; - - NMDevice *wifiNMDevice = getNmDevice(); - if(wifiNMDevice == NULL) { - NMLOG_TRACE("NMDeviceWifi NULL !"); - return false; - } - - nm_device_disconnect_async(wifiNMDevice, NULL, wifiDisconnectCb, this); - wait(loop); - NMLOG_TRACE("Exit"); - return isSuccess; - } - - static NMAccessPoint *checkSSIDAvailable(NMDevice *device, const GPtrArray *aps, const char *ssid) - { - NMAccessPoint *AccessPoint = NULL; - aps = nm_device_wifi_get_access_points(NM_DEVICE_WIFI(device)); - for (guint i = 0; i < aps->len; i++) - { - NMAccessPoint *candidate_ap = static_cast(g_ptr_array_index(aps, i)); - if (ssid) - { - GBytes *ssidGBytes; - ssidGBytes = nm_access_point_get_ssid(candidate_ap); - if (!ssidGBytes) - continue; - gsize size; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssidGBytes, &size)); - std::string ssidstr(reinterpret_cast(ssidData), size); - //g_bytes_unref(ssidGBytes); - // NMLOG_TRACE("ssid < %s >", ssidstr.c_str()); - if (strcmp(ssid, ssidstr.c_str()) == 0) - { - AccessPoint = candidate_ap; - break; - } - } - } - - return AccessPoint; - } - - static void wifiConnectCb(GObject *client, GAsyncResult *result, gpointer user_data) - { - GError *error = NULL; - wifiManager *_wifiManager = (static_cast(user_data)); - - if (_wifiManager->createNewConnection) { - NMLOG_TRACE("nm_client_add_and_activate_connection_finish"); - nm_client_add_and_activate_connection_finish(NM_CLIENT(_wifiManager->client), result, &error); - _wifiManager->isSuccess = true; - } - else { - NMLOG_TRACE("nm_client_activate_connection_finish "); - nm_client_activate_connection_finish(NM_CLIENT(_wifiManager->client), result, &error); - _wifiManager->isSuccess = true; - } - - if (error) { - _wifiManager->isSuccess = false; - if (_wifiManager->createNewConnection) { - NMLOG_ERROR("Failed to add/activate new connection: %s", error->message); - } else { - NMLOG_ERROR("Failed to activate connection: %s", error->message); - } - } - - g_main_loop_quit(_wifiManager->loop); - } - - static void wifiConnectionUpdate(GObject *rmObject, GAsyncResult *res, gpointer user_data) - { - NMRemoteConnection *remote_con = NM_REMOTE_CONNECTION(rmObject); - wifiManager *_wifiManager = (static_cast(user_data)); - GVariant *ret = NULL; - GError *error = NULL; - - ret = nm_remote_connection_update2_finish(remote_con, res, &error); - - if (!ret) { - NMLOG_ERROR("Error: %s.", error->message); - g_error_free(error); - _wifiManager->isSuccess = false; - _wifiManager->quit(NULL); - return; - } - _wifiManager->createNewConnection = false; // no need to create new connection - nm_client_activate_connection_async( - _wifiManager->client, NM_CONNECTION(remote_con), _wifiManager->wifidevice, _wifiManager->objectPath, NULL, wifiConnectCb, _wifiManager); - } - - bool wifiManager::wifiConnect(Exchange::INetworkManager::WiFiConnectTo wifiData) - { - const char *ssid_in = wifiData.m_ssid.c_str(); - const char* password_in = wifiData.m_passphrase.c_str(); - NMAccessPoint *AccessPoint = NULL; - GPtrArray *allaps = NULL; - const char *conName = ssid_in; - NMConnection *connection = NULL; - NMSettingConnection *sConnection = NULL; - NMSetting8021x *s8021X = NULL; - NMSettingWireless *sWireless = NULL; - NMSettingWirelessSecurity *sSecurity = NULL; - NM80211ApFlags apFlags; - NM80211ApSecurityFlags apWpaFlags; - NM80211ApSecurityFlags apRsnFlags; - const char *ifname = NULL; - const GPtrArray *availableConnections; - bool SSIDmatch = false; - Exchange::INetworkManager::WiFiSSIDInfo apinfo; - - if(!createClientNewConnection()) - return false; - - if (strlen(ssid_in) > 32) - { - NMLOG_WARNING("ssid length grater than 32"); - return false; - } - - NMDevice *device = NULL; - device = getNmDevice(); - if(device == NULL) - return false; - wifidevice = device; - - std::string activeSSID; - if(getConnectedSSID(NM_DEVICE_WIFI(wifidevice), activeSSID)) - { - if(strcmp(ssid_in, activeSSID.c_str()) == 0) - { - NMLOG_WARNING("ssid already connected !"); - return true; - } - else - { - NMLOG_WARNING("wifi already connected with %s AP", activeSSID.c_str()); - } - } - //NMLOG_TRACE("Wireless Device found ifce : %s !", ifname = nm_device_get_iface(device)); - AccessPoint = checkSSIDAvailable(device, allaps, ssid_in); - // TODO Scann hidden ssid also for lnf - if(AccessPoint == NULL) { - NMLOG_WARNING("No network with SSID '%s' found !", ssid_in); - // TODO send SSID NO AVAILABLE event - return false; - } - - getApInfo(AccessPoint, apinfo); - - availableConnections = nm_device_get_available_connections(device); - for (guint i = 0; i < availableConnections->len; i++) - { - NMConnection *currentConnection = static_cast(g_ptr_array_index(availableConnections, i)); - const char *id = nm_connection_get_id(NM_CONNECTION(currentConnection)); - - if (conName) { - if (!id || strcmp(id, conName)) - continue; - - SSIDmatch = TRUE; - } - - if (nm_access_point_connection_valid(AccessPoint, NM_CONNECTION(currentConnection))) { - connection = g_object_ref(currentConnection); - NMLOG_TRACE("Connection '%s' exists !", conName); - break; - } - } - - if (SSIDmatch && !connection) - { - NMLOG_ERROR("Connection '%s' exists but properties don't match", conName); - //TODO Remove Connection - return false; - } - - if (!connection) - { - NMLOG_TRACE("creating new connection '%s' ", conName); - connection = nm_simple_connection_new(); - if (conName) { - sConnection = (NMSettingConnection *) nm_setting_connection_new(); - nm_connection_add_setting(connection, NM_SETTING(sConnection)); - const char *uuid = nm_utils_uuid_generate();; - - g_object_set(G_OBJECT(sConnection), - NM_SETTING_CONNECTION_UUID, - uuid, - NM_SETTING_CONNECTION_ID, - conName, - NM_SETTING_CONNECTION_TYPE, - "802-11-wireless", - NULL); - } - - sWireless = (NMSettingWireless *)nm_setting_wireless_new(); - GBytes *ssid = g_bytes_new(ssid_in, strlen(ssid_in)); - g_object_set(G_OBJECT(sWireless), - NM_SETTING_WIRELESS_SSID, - ssid, - NULL); - //g_bytes_unref(ssid); - /* For lnf network need to include - * - * 'bssid' parameter is used to restrict the connection only to the BSSID - * g_object_set(s_wifi, NM_SETTING_WIRELESS_BSSID, bssid, NULL); - * g_object_set(s_wifi, NM_SETTING_WIRELESS_SSID, ssid, NM_SETTING_WIRELESS_HIDDEN, hidden, NULL); - */ - nm_connection_add_setting(connection, NM_SETTING(sWireless)); - } - - apFlags = nm_access_point_get_flags(AccessPoint); - apWpaFlags = nm_access_point_get_wpa_flags(AccessPoint); - apRsnFlags = nm_access_point_get_rsn_flags(AccessPoint); - - // check ap flag ty securti we supporting - if(apFlags != NM_802_11_AP_FLAGS_NONE && strlen(password_in) < 1 ) - { - NMLOG_ERROR("This ap(%s) security need password please add password!", ssid_in); - return false; - } - - if ( (apRsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE) || (apRsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { - - NMLOG_ERROR("Ap wifi security OWE"); - return false; - } - - if( (apWpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) || (apRsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) ) - { - GError *error = NULL; - NMLOG_INFO("Ap securtity mode is 802.1X"); - - NMLOG_TRACE("802.1x Identity : %s", wifiData.m_identity.c_str()); - NMLOG_TRACE("802.1x CA cert path : %s", wifiData.m_caCert.c_str()); - NMLOG_TRACE("802.1x Client cert path : %s", wifiData.m_clientCert.c_str()); - NMLOG_TRACE("802.1x Private key path : %s", wifiData.m_privateKey.c_str()); - NMLOG_TRACE("802.1x Private key psswd : %s", wifiData.m_privateKeyPasswd.c_str()); - - s8021X = (NMSetting8021x *) nm_setting_802_1x_new(); - nm_connection_add_setting(connection, NM_SETTING(s8021X)); - - g_object_set(s8021X, NM_SETTING_802_1X_IDENTITY, wifiData.m_identity.c_str(), NULL); - nm_setting_802_1x_add_eap_method(s8021X, "tls"); - if(!wifiData.m_caCert.empty() && !nm_setting_802_1x_set_ca_cert(s8021X, - wifiData.m_caCert.c_str(), - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, - &error)) - { - NMLOG_ERROR("ca certificate add failed: %s", error->message); - g_error_free(error); - return false; - } - - if(!wifiData.m_clientCert.empty() && !nm_setting_802_1x_set_client_cert(s8021X, - wifiData.m_clientCert.c_str(), - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, - &error)) - { - NMLOG_ERROR("client certificate add failed: %s", error->message); - g_error_free(error); - return false; - } - - if(!wifiData.m_privateKey.empty() && !nm_setting_802_1x_set_private_key(s8021X, - wifiData.m_privateKey.c_str(), - wifiData.m_privateKeyPasswd.c_str(), - NM_SETTING_802_1X_CK_SCHEME_PATH, - NULL, - &error)) - { - NMLOG_ERROR("client private key add failed: %s", error->message); - g_error_free(error); - return false; - } - - sSecurity = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); - nm_connection_add_setting(connection, NM_SETTING(sSecurity)); - g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"wpa-eap", NULL); - } - else if ((apFlags & NM_802_11_AP_FLAGS_PRIVACY) || (apWpaFlags != NM_802_11_AP_SEC_NONE )|| (apRsnFlags != NM_802_11_AP_SEC_NONE )) - { - NMLOG_INFO("%s ap securtity mode (%s) supported !", ssid_in, nmUtils::getSecurityModeString(apFlags,apWpaFlags,apRsnFlags).c_str()); - - if (password_in) - { - sSecurity = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); - nm_connection_add_setting(connection, NM_SETTING(sSecurity)); - - if (apWpaFlags == NM_802_11_AP_SEC_NONE && apRsnFlags == NM_802_11_AP_SEC_NONE) - { - nm_setting_wireless_security_set_wep_key(sSecurity, 0, password_in); - NMLOG_ERROR("wifi security WEP mode not supported ! need to add wep-key-type"); - return false; - } - else if ((apWpaFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) - || (apRsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) || (apRsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) { - - g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"wpa-psk", NULL); - g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_PSK, password_in, NULL); - } - } - else - { - NMLOG_ERROR("This AccessPoint(%s) need password please add password!", ssid_in); - return false; - } - } - else - { - /* for open network every flag value will be zero */ - if (apFlags == NM_802_11_AP_FLAGS_NONE && apWpaFlags == NM_802_11_AP_SEC_NONE && apRsnFlags == NM_802_11_AP_SEC_NONE) { - NMLOG_INFO("open network no password requied"); - } - else { - NMLOG_ERROR("wifi security mode not supported !"); - return false; - } - } - - objectPath = nm_object_get_path(NM_OBJECT(AccessPoint)); - GVariant *nmDbusConnection = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); - if (NM_IS_REMOTE_CONNECTION(connection)) - { - nm_remote_connection_update2(NM_REMOTE_CONNECTION(connection), - nmDbusConnection, - NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT, // autoconnect right away - NULL, - NULL, - wifiConnectionUpdate, - this); - } - else - { - createNewConnection = true; - nm_client_add_and_activate_connection_async(client, connection, device, objectPath, NULL, wifiConnectCb, this); - } - - wait(loop); - return isSuccess; - } - - static void addToKnownSSIDsCb(GObject *client, GAsyncResult *result, gpointer user_data) - { - - GError *error = NULL; - wifiManager *_wifiManager = (static_cast(user_data)); - //NMRemoteConnection *connection = NM_REMOTE_CONNECTION(s); - if (!nm_client_add_connection_finish(NM_CLIENT(client), result, &error)) { - NMLOG_ERROR ("AddToKnownSSIDs Failed"); - _wifiManager->isSuccess = false; - } - else - { - NMLOG_TRACE ("AddToKnownSSIDs is success"); - _wifiManager->isSuccess = true; - } - - g_main_loop_quit(_wifiManager->loop); - } - - bool wifiManager::addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo ssidinfo) - { - if(!createClientNewConnection()) - return false; - - NMSettingWirelessSecurity *nmSettingsWifiSec; - NMSettingWireless *nmSettingsWifi; - const char *uuid = nm_utils_uuid_generate(); - - nmSettingsWifiSec = (NMSettingWirelessSecurity *)nm_setting_wireless_security_new(); - NMSettingConnection *nmConnSec = (NMSettingConnection *)nm_setting_connection_new(); - g_object_set(G_OBJECT(nmConnSec), - NM_SETTING_CONNECTION_UUID, - uuid, - NM_SETTING_CONNECTION_ID, - ssidinfo.m_ssid.c_str(), - NM_SETTING_CONNECTION_TYPE, - "802-11-wireless", - NULL); - NMConnection *connection = nm_simple_connection_new(); - nm_connection_add_setting(connection, NM_SETTING(nmConnSec)); - nmSettingsWifi = (NMSettingWireless *)nm_setting_wireless_new(); - GString *ssidStr = g_string_new(ssidinfo.m_ssid.c_str()); - g_object_set(G_OBJECT(nmSettingsWifi), NM_SETTING_WIRELESS_SSID, ssidStr, NULL); - - nm_connection_add_setting(connection, NM_SETTING(nmSettingsWifi)); - nmSettingsWifiSec = (NMSettingWirelessSecurity *)nm_setting_wireless_security_new(); - // TODO chek different securtity mode and portocol and add settings - switch(ssidinfo.m_securityMode) - { - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK_AES: - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_WPA2_PSK: - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK_TKIP: - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA2_PSK_AES: - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA2_PSK_TKIP: - case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA3_SAE: - { - g_object_set(G_OBJECT(nmSettingsWifiSec), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"wpa-psk", NULL); - if(!ssidinfo.m_passphrase.empty()) - g_object_set(G_OBJECT(nmSettingsWifiSec), NM_SETTING_WIRELESS_SECURITY_PSK, ssidinfo.m_passphrase.c_str(), NULL); - break; - } - case Exchange::INetworkManager::WIFI_SECURITY_NONE: - NMLOG_INFO("open wifi network configuration"); - break; - default: - { - NMLOG_WARNING("connection wifi securtity type not supported"); - return false; - } - } - - nm_connection_add_setting(connection, NM_SETTING(nmSettingsWifiSec)); - nm_client_add_connection_async(client, connection, true, NULL, addToKnownSSIDsCb, this); - //wait(loop); - // TODO change to GmainLoooprun - g_main_loop_unref(loop); - return isSuccess; - } - - static void removeKnownSSIDCb(GObject *client, GAsyncResult *result, gpointer user_data) - { - GError *error = NULL; - wifiManager *_wifiManager = (static_cast(user_data)); - NMRemoteConnection *connection = NM_REMOTE_CONNECTION(client); - if (!nm_remote_connection_delete_finish(connection, result, &error)) { - NMLOG_ERROR("RemoveKnownSSID failed %s", error->message); - _wifiManager->isSuccess = false; - } - else - { - NMLOG_INFO ("RemoveKnownSSID is success"); - _wifiManager->isSuccess = true; - } - - g_main_loop_quit(_wifiManager->loop); - } - - bool wifiManager::removeKnownSSID(const string& ssid) - { - - if(!createClientNewConnection()) - return false; - - if(ssid.empty()) - return false; - isSuccess = false; - - NMRemoteConnection* remoteConnection; - const GPtrArray* connections = nm_client_get_connections(client); - - for (guint i = 0; i < connections->len; i++) - { - remoteConnection = NM_REMOTE_CONNECTION(connections->pdata[i]); - NMConnection *connection = NM_CONNECTION(connections->pdata[i]); - if (NM_IS_SETTING_WIRELESS(nm_connection_get_setting_wireless(connection))) - { - GBytes *ssidBytes = nm_setting_wireless_get_ssid(nm_connection_get_setting_wireless(connection)); - if (ssidBytes) - { - gsize ssidSize; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssidBytes, &ssidSize)); - std::string ssidstr(reinterpret_cast(ssidData), ssidSize); - if (ssid == ssidstr) - { - //nm_remote_connection_delete_async(remoteConnection, NULL, removeKnownSSIDCb, this); - // TODO add async - GError *error = NULL; - nm_remote_connection_delete(remoteConnection, NULL, &error); - if (error) - { - NMLOG_ERROR("RemoveKnownSSID failed %s", error->message); - g_error_free(error); - } - else - { - NMLOG_INFO("RemoveKnownSSID is success %s", ssid.c_str()); - isSuccess = true; - } - break; // if remove all connection with same ssid not to break - } - } - } - } - - return isSuccess; - } - - bool wifiManager::getKnownSSIDs(std::list& ssids) - { - if(!createClientNewConnection()) - return false; - const GPtrArray *connections = nm_client_get_connections(client); - std::string ssidPrint; - for (guint i = 0; i < connections->len; i++) - { - NMConnection *connection = NM_CONNECTION(connections->pdata[i]); - - if (NM_IS_SETTING_WIRELESS(nm_connection_get_setting_wireless(connection))) - { - GBytes *ssidBytes = nm_setting_wireless_get_ssid(nm_connection_get_setting_wireless(connection)); - if (ssidBytes) - { - gsize ssidSize; - const guint8 *ssidData = static_cast(g_bytes_get_data(ssidBytes, &ssidSize)); - std::string ssidstr(reinterpret_cast(ssidData), ssidSize); - if (!ssidstr.empty()) - { - ssids.push_back(ssidstr); - ssidPrint += ssidstr; - ssidPrint += ", "; - } - } - } - } - if (!ssids.empty()) - { - NMLOG_TRACE("known wifi connections are %s", ssidPrint.c_str()); - return true; - } - - return false; - } - - static void wifiScanCb(GObject *object, GAsyncResult *result, gpointer user_data) - { - GError *error = NULL; - wifiManager *_wifiManager = (static_cast(user_data)); - if(nm_device_wifi_request_scan_finish(NM_DEVICE_WIFI(object), result, &error)) { - NMLOG_TRACE("Scanning success"); - _wifiManager->isSuccess = true; - } - else - { - NMLOG_ERROR("Scanning Failed"); - _wifiManager->isSuccess = false; - } - if (error) { - NMLOG_ERROR("Scanning Failed Error: %s.", error->message); - _wifiManager->isSuccess = false; - g_error_free(error); - } - - g_main_loop_quit(_wifiManager->loop); - } - - bool wifiManager::wifiScanRequest(const Exchange::INetworkManager::WiFiFrequency frequency, std::string ssidReq) - { - if(!createClientNewConnection()) - return false; - NMDeviceWifi *wifiDevice = NM_DEVICE_WIFI(getNmDevice()); - if(wifiDevice == NULL) { - NMLOG_TRACE("NMDeviceWifi * NULL !"); - return false; - } - isSuccess = false; - if(!ssidReq.empty()) - { - NMLOG_TRACE("staring wifi scanning .. %s", ssidReq.c_str()); - GVariantBuilder builder, array_builder; - GVariant *options; - g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); - g_variant_builder_init(&array_builder, G_VARIANT_TYPE("aay")); - g_variant_builder_add(&array_builder, "@ay", - g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (const guint8 *) ssidReq.c_str(), ssidReq.length(), 1) - ); - g_variant_builder_add(&builder, "{sv}", "ssids", g_variant_builder_end(&array_builder)); - g_variant_builder_add(&builder, "{sv}", "hidden", g_variant_new_boolean(TRUE)); - options = g_variant_builder_end(&builder); - nm_device_wifi_request_scan_options_async(NM_DEVICE_WIFI(wifiDevice), options, NULL, wifiScanCb, this); - } - else { - NMLOG_TRACE("staring normal wifi scanning"); - nm_device_wifi_request_scan_async(NM_DEVICE_WIFI(wifiDevice), NULL, wifiScanCb, this); - } - wait(loop); - return isSuccess; - } - } // namespace Plugin -} // namespace WPEFramework diff --git a/NetworkManagerGnomeWIFI.h b/NetworkManagerGnomeWIFI.h deleted file mode 100644 index 0c4c4db03..000000000 --- a/NetworkManagerGnomeWIFI.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "NetworkManagerLogger.h" -#include "INetworkManager.h" -#include -#include -#include -#include -#include - -namespace WPEFramework -{ - namespace Plugin - { - class wifiManager - { - public: - static wifiManager* getInstance(); - bool isWifiConnected(); - bool wifiDisconnect(); - bool wifiConnectedSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo); - bool wifiConnect(Exchange::INetworkManager::WiFiConnectTo wifiData); - bool wifiScanRequest(const Exchange::INetworkManager::WiFiFrequency frequency, std::string ssidReq = ""); - bool getKnownSSIDs(std::list& ssids); - bool addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo ssidinfo); - bool removeKnownSSID(const string& ssid); - bool quit(NMDevice *wifiNMDevice); - bool wait(GMainLoop *loop, int timeOutMs = 10000); // default maximium set as 10 sec - private: - NMDevice *getNmDevice(); - - private: - wifiManager(); - ~wifiManager(); - - wifiManager(wifiManager const&) = delete; - void operator=(wifiManager const&) = delete; - - bool createClientNewConnection(); - - public: - NMClient *client; - GMainLoop *loop; - gboolean createNewConnection; - GMainContext *nmContext = nullptr; - const char* objectPath; - NMDevice *wifidevice; - GSource *source; - guint wifiDeviceStateGsignal = 0; - bool isSuccess = false; - }; - } -} diff --git a/NetworkManagerImplementation.cpp b/NetworkManagerImplementation.cpp deleted file mode 100644 index 5208b16a0..000000000 --- a/NetworkManagerImplementation.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2022 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#include "NetworkManagerImplementation.h" -#include "NetworkManagerConnectivity.h" -#include "WiFiSignalStrengthMonitor.h" - -using namespace WPEFramework; -using namespace WPEFramework::Plugin; -using namespace NetworkManagerLogger; - -#define CIDR_NETMASK_IP_LEN 32 - -namespace WPEFramework -{ - namespace Plugin - { - SERVICE_REGISTRATION(NetworkManagerImplementation, NETWORKMANAGER_MAJOR_VERSION, NETWORKMANAGER_MINOR_VERSION, NETWORKMANAGER_PATCH_VERSION); - - NetworkManagerImplementation::NetworkManagerImplementation() - : _notificationCallbacks({}) - { - /* Initialize Network Manager */ - NetworkManagerLogger::Init(); - - LOG_ENTRY_FUNCTION(); - /* Name says it all */ - platform_init(); - - /* Initialize STUN Endpoints */ - m_stunEndPoint = "stun.l.google.com"; - m_stunPort = 19302; - m_stunBindTimeout = 30; - m_stunCacheTimeout = 0; - m_defaultInterface = ""; - m_publicIP = ""; - } - - NetworkManagerImplementation::~NetworkManagerImplementation() - { - LOG_ENTRY_FUNCTION(); - if(m_registrationThread.joinable()) - { - m_registrationThread.join(); - } - } - - /** - * Register a notification callback - */ - uint32_t NetworkManagerImplementation::Register(INetworkManager::INotification *notification) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - - // Make sure we can't register the same notification callback multiple times - if (std::find(_notificationCallbacks.begin(), _notificationCallbacks.end(), notification) == _notificationCallbacks.end()) { - _notificationCallbacks.push_back(notification); - notification->AddRef(); - } - - _notificationLock.Unlock(); - - return Core::ERROR_NONE; - } - - /** - * Unregister a notification callback - */ - uint32_t NetworkManagerImplementation::Unregister(INetworkManager::INotification *notification) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - - // Make sure we can't register the same notification callback multiple times - auto itr = std::find(_notificationCallbacks.begin(), _notificationCallbacks.end(), notification); - if (itr != _notificationCallbacks.end()) { - (*itr)->Release(); - _notificationCallbacks.erase(itr); - } - - _notificationLock.Unlock(); - - return Core::ERROR_NONE; - } - - uint32_t NetworkManagerImplementation::Configure(const string& configLine /* @in */, NMLogging& logLevel /* @out */) - { - if(configLine.empty()) - { - NMLOG_FATAL("config line : is empty !"); - return(Core::ERROR_GENERAL); - } - - NMLOG_TRACE("config line : %s", configLine.c_str()); - - Config config; - if(config.FromString(configLine)) - { - /* stun configuration copy */ - m_stunEndPoint = config.stun.stunEndpoint.Value(); - m_stunPort = config.stun.port.Value(); - m_stunBindTimeout = config.stun.interval.Value(); - - NMLOG_TRACE("config : stun endpoint %s", m_stunEndPoint.c_str()); - NMLOG_TRACE("config : stun port %d", m_stunPort); - NMLOG_TRACE("config : stun interval %d", m_stunBindTimeout); - - NMLOG_TRACE("config : loglevel %d", config.loglevel.Value()); - logLevel = static_cast (config.loglevel.Value()); - // configure loglevel in libWPEFrameworkNetworkManagerImplementation.so - NetworkManagerLogger::SetLevel(static_cast (logLevel)); - - /* load connectivity monitor endpoints */ - std::vector connectEndpts; - if(!config.connectivityConf.endpoint_1.Value().empty()) { - NMLOG_TRACE("config : connectivity enpt 1 %s", config.connectivityConf.endpoint_1.Value().c_str()); - connectEndpts.push_back(config.connectivityConf.endpoint_1.Value().c_str()); - } - if(!config.connectivityConf.endpoint_2.Value().empty()) { - NMLOG_TRACE("config : connectivity enpt 2 %s", config.connectivityConf.endpoint_2.Value().c_str()); - connectEndpts.push_back(config.connectivityConf.endpoint_2.Value().c_str()); - } - if(!config.connectivityConf.endpoint_3.Value().empty()) { - NMLOG_TRACE("config : connectivity enpt 3 %s", config.connectivityConf.endpoint_3.Value().c_str()); - connectEndpts.push_back(config.connectivityConf.endpoint_3.Value().c_str()); - } - if(!config.connectivityConf.endpoint_4.Value().empty()) { - NMLOG_TRACE("config : connectivity enpt 4 %s", config.connectivityConf.endpoint_4.Value().c_str()); - connectEndpts.push_back(config.connectivityConf.endpoint_4.Value().c_str()); - } - if(!config.connectivityConf.endpoint_5.Value().empty()) { - NMLOG_TRACE("config : connectivity enpt 5 %s", config.connectivityConf.endpoint_5.Value().c_str()); - connectEndpts.push_back(config.connectivityConf.endpoint_5.Value().c_str()); - } - - connectivityMonitor.setConnectivityMonitorEndpoints(connectEndpts); - } - else - NMLOG_ERROR("Plugin configuration read error !"); - - return(Core::ERROR_NONE); - } - - /* @brief Get STUN Endpoint to be used for identifying Public IP */ - uint32_t NetworkManagerImplementation::GetStunEndpoint (string &endPoint /* @out */, uint32_t& port /* @out */, uint32_t& bindTimeout /* @out */, uint32_t& cacheTimeout /* @out */) const - { - LOG_ENTRY_FUNCTION(); - endPoint = m_stunEndPoint; - port = m_stunPort; - bindTimeout = m_stunBindTimeout; - cacheTimeout = m_stunCacheTimeout; - return Core::ERROR_NONE; - } - - /* @brief Set STUN Endpoint to be used to identify Public IP */ - uint32_t NetworkManagerImplementation::SetStunEndpoint (string const endPoint /* @in */, const uint32_t port /* @in */, const uint32_t bindTimeout /* @in */, const uint32_t cacheTimeout /* @in */) - { - LOG_ENTRY_FUNCTION(); - if (!endPoint.empty()) - m_stunEndPoint = endPoint; - if (port != 0) - m_stunPort = port; - - m_stunBindTimeout = bindTimeout; - m_stunCacheTimeout = cacheTimeout; - return Core::ERROR_NONE; - } - - /* @brief Get ConnectivityTest Endpoints */ - uint32_t NetworkManagerImplementation::GetConnectivityTestEndpoints(IStringIterator*& endPoints/* @out */) const - { - LOG_ENTRY_FUNCTION(); - std::vector tmpEndPoints = connectivityMonitor.getConnectivityMonitorEndpoints(); - endPoints = (Core::Service::Create(tmpEndPoints)); - - return Core::ERROR_NONE; - } - - /* @brief Set ConnectivityTest Endpoints */ - uint32_t NetworkManagerImplementation::SetConnectivityTestEndpoints(IStringIterator* const endPoints /* @in */) - { - LOG_ENTRY_FUNCTION(); - std::vector tmpEndPoints; - if(endPoints) - { - string endPoint{}; - while (endPoints->Next(endPoint) == true) - { - tmpEndPoints.push_back(endPoint); - } - connectivityMonitor.setConnectivityMonitorEndpoints(tmpEndPoints); - } - return Core::ERROR_NONE; - } - - /* @brief Get Internet Connectivty Status */ - uint32_t NetworkManagerImplementation::IsConnectedToInternet(const string &ipversion /* @in */, InternetStatus &result /* @out */) - { - LOG_ENTRY_FUNCTION(); - nsm_internetState isconnected; - nsm_ipversion tmpVersion = NSM_IPRESOLVE_WHATEVER; - if(0 == strcasecmp("IPv4", ipversion.c_str())) - tmpVersion = NSM_IPRESOLVE_V4; - else if(0 == strcasecmp("IPv6", ipversion.c_str())) - tmpVersion = NSM_IPRESOLVE_V6; - - isconnected = connectivityMonitor.getInternetState(tmpVersion); - if (FULLY_CONNECTED == isconnected) - result = INTERNET_FULLY_CONNECTED; - else if (CAPTIVE_PORTAL == isconnected) - result = INTERNET_CAPTIVE_PORTAL; - else if (LIMITED_INTERNET == isconnected) - result = INTERNET_LIMITED; - else - result = INTERNET_NOT_AVAILABLE; - - return Core::ERROR_NONE; - } - - /* @brief Get Authentication URL if the device is behind Captive Portal */ - uint32_t NetworkManagerImplementation::GetCaptivePortalURI(string &endPoints/* @out */) const - { - LOG_ENTRY_FUNCTION(); - endPoints = connectivityMonitor.getCaptivePortalURI(); - return Core::ERROR_NONE; - } - - /* @brief Start The Internet Connectivity Monitoring */ - uint32_t NetworkManagerImplementation::StartConnectivityMonitoring(const uint32_t interval/* @in */) - { - LOG_ENTRY_FUNCTION(); - if (connectivityMonitor.startContinuousConnectivityMonitor(interval)) - return Core::ERROR_NONE; - else - return Core::ERROR_GENERAL; - } - - /* @brief Stop The Internet Connectivity Monitoring */ - uint32_t NetworkManagerImplementation::StopConnectivityMonitoring(void) const - { - LOG_ENTRY_FUNCTION(); - if (connectivityMonitor.stopContinuousConnectivityMonitor()) - return Core::ERROR_NONE; - else - return Core::ERROR_GENERAL; - } - - /* @brief Get the Public IP used for external world communication */ - uint32_t NetworkManagerImplementation::GetPublicIP (const string &ipversion /* @in */, string& ipAddress /* @out */) - { - LOG_ENTRY_FUNCTION(); - stun::bind_result result; - bool isIPv6 = (0 == strcasecmp("IPv6", ipversion.c_str())); - stun::protocol proto (isIPv6 ? stun::protocol::af_inet6 : stun::protocol::af_inet); - if(stunClient.bind(m_stunEndPoint, m_stunPort, m_defaultInterface, proto, m_stunBindTimeout, m_stunCacheTimeout, result)) - { - ipAddress = result.public_ip; - return Core::ERROR_NONE; - } - else - { - return Core::ERROR_GENERAL; - } - } - - /* @brief Set the network manager plugin log level */ - uint32_t NetworkManagerImplementation::SetLogLevel(const NMLogging& logLevel /* @in */) - { - NetworkManagerLogger::SetLevel((LogLevel)logLevel); - return Core::ERROR_NONE; - } - - /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ - uint32_t NetworkManagerImplementation::Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const uint16_t timeOutInSeconds /* @in */, const string guid /* @in */, string& response /* @out */) - { - char cmd[100] = ""; - if(0 == strcasecmp("IPv6", ipversion.c_str())) - { - snprintf(cmd, sizeof(cmd), "ping6 -c %d -W %d '%s' 2>&1", noOfRequest, timeOutInSeconds, endpoint.c_str()); - } - else - { - snprintf(cmd, sizeof(cmd), "ping -c %d -W %d '%s' 2>&1", noOfRequest, timeOutInSeconds, endpoint.c_str()); - } - - NMLOG_INFO ("The Command is %s", cmd); - string commandToExecute(cmd); - executeExternally(NETMGR_PING, commandToExecute, response); - - return Core::ERROR_NONE; - } - - /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ - uint32_t NetworkManagerImplementation::Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const string guid /* @in */, string& response /* @out */) - { - char cmd[256] = ""; - string tempResult = ""; - if(0 == strcasecmp("IPv6", ipversion.c_str())) - { - snprintf(cmd, 256, "traceroute6 -w 3 -m 6 -q %d %s 64 2>&1", noOfRequest, endpoint.c_str()); - } - else - { - snprintf(cmd, 256, "traceroute -w 3 -m 6 -q %d %s 52 2>&1", noOfRequest, endpoint.c_str()); - } - - NMLOG_INFO ("The Command is %s", cmd); - string commandToExecute(cmd); - executeExternally(NETMGR_TRACE, commandToExecute, tempResult); - - JsonObject temp; - temp["target"] = endpoint; - temp["results"] = tempResult; - temp.ToString(response); - - return Core::ERROR_NONE; - } - - void NetworkManagerImplementation::executeExternally(NetworkEvents event, const string commandToExecute, string& response) - { - FILE *pipe = NULL; - string output{}; - char buffer[1024]; - JsonObject pingResult; - int exitStatus; - - pipe = popen(commandToExecute.c_str(), "r"); - if (pipe == NULL) - { - NMLOG_INFO ("%s: failed to open file '%s' for read mode with result: %s", __FUNCTION__, commandToExecute.c_str(), strerror(errno)); - return; - } - - if (NETMGR_PING == event) - { - while (!feof(pipe) && fgets(buffer, 1024, pipe) != NULL) - { - // remove newline from buffer - buffer[strcspn(buffer, "\n")] = '\0'; - string line(buffer); - - if( line.find( "packet" ) != string::npos ) - { - //Example: 10 packets transmitted, 10 packets received, 0% packet loss - stringstream ss( line ); - int transCount; - ss >> transCount; - pingResult["packetsTransmitted"] = transCount; - - string token; - getline( ss, token, ',' ); - getline( ss, token, ',' ); - stringstream ss2( token ); - int rxCount; - ss2 >> rxCount; - pingResult["packetsReceived"] = rxCount; - - getline( ss, token, ',' ); - string prefix = token.substr(0, token.find("%")); - pingResult["packetLoss"] = prefix.c_str(); - - } - else if( line.find( "min/avg/max" ) != string::npos ) - { - //Example: round-trip min/avg/max = 17.038/18.310/20.197 ms - stringstream ss( line ); - string fullpath; - getline( ss, fullpath, '=' ); - getline( ss, fullpath, '=' ); - - string prefix; - int index = fullpath.find("/"); - if (index >= 0) - { - prefix = fullpath.substr(0, fullpath.find("/")); - pingResult["tripMin"] = prefix.c_str(); - } - - index = fullpath.find("/"); - if (index >= 0) - { - fullpath = fullpath.substr(index + 1, fullpath.length()); - prefix = fullpath.substr(0, fullpath.find("/")); - pingResult["tripAvg"] = prefix.c_str(); - } - - index = fullpath.find("/"); - if (index >= 0) - { - fullpath = fullpath.substr(index + 1, fullpath.length()); - prefix = fullpath.substr(0, fullpath.find("/")); - pingResult["tripMax"] = prefix.c_str(); - } - - index = fullpath.find("/"); - if (index >= 0) - { - fullpath = fullpath.substr(index + 1, fullpath.length()); - pingResult["tripStdDev"] = fullpath.c_str(); - } - } - else if( line.find( "bad" ) != string::npos ) - { - pingResult["success"] = false; - pingResult["error"] = "Bad Address"; - } - } - exitStatus = pclose(pipe); - // Check the exit status to determine if the command was successful - if (WIFEXITED(exitStatus) && WEXITSTATUS(exitStatus) == 0) { - pingResult["success"] = true; - pingResult["error"] = ""; - } else { - pingResult["success"] = false; - pingResult["error"] = "Could not ping endpoint"; - } - - pingResult.ToString(response); - NMLOG_INFO("Response is, %s", response.c_str()); - } - else if (NETMGR_TRACE == event) - { - - // We return the entire output of the trace command but since this contains newlines it is not valid as - // a json value so we will parse the output into an array of strings, one element for each line. - JsonArray list; - while (!feof(pipe) && fgets(buffer, 1024, pipe) != NULL) - { - // remove newline from buffer - buffer[strcspn(buffer, "\n")] = ' '; - string line(buffer); - list.Add(line); - } - - pclose(pipe); - list.ToString(response); - NMLOG_INFO("Response is, %s", response.c_str()); - } - return; - } - - // WiFi Specific Methods - /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ - uint32_t NetworkManagerImplementation::GetSupportedSecurityModes(ISecurityModeIterator*& securityModes /* @out */) const - { - LOG_ENTRY_FUNCTION(); - std::vector modeInfo { - {WIFI_SECURITY_NONE, "WIFI_SECURITY_NONE"}, - {WIFI_SECURITY_WEP_64, "WIFI_SECURITY_WEP_64"}, - {WIFI_SECURITY_WEP_128, "WIFI_SECURITY_WEP_128"}, - {WIFI_SECURITY_WPA_PSK_TKIP, "WIFI_SECURITY_WPA_PSK_TKIP"}, - {WIFI_SECURITY_WPA_PSK_AES, "WIFI_SECURITY_WPA_PSK_AES"}, - {WIFI_SECURITY_WPA2_PSK_TKIP, "WIFI_SECURITY_WPA2_PSK_TKIP"}, - {WIFI_SECURITY_WPA2_PSK_AES, "WIFI_SECURITY_WPA2_PSK_AES"}, - {WIFI_SECURITY_WPA_ENTERPRISE_TKIP, "WIFI_SECURITY_WPA_ENTERPRISE_TKIP"}, - {WIFI_SECURITY_WPA_ENTERPRISE_AES, "WIFI_SECURITY_WPA_ENTERPRISE_AES"}, - {WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, "WIFI_SECURITY_WPA2_ENTERPRISE_TKIP"}, - {WIFI_SECURITY_WPA2_ENTERPRISE_AES, "WIFI_SECURITY_WPA2_ENTERPRISE_AES"}, - {WIFI_SECURITY_WPA_WPA2_PSK, "WIFI_SECURITY_WPA_WPA2_PSK"}, - {WIFI_SECURITY_WPA_WPA2_ENTERPRISE, "WIFI_SECURITY_WPA_WPA2_ENTERPRISE"}, - {WIFI_SECURITY_WPA3_PSK_AES, "WIFI_SECURITY_WPA3_PSK_AES"}, - {WIFI_SECURITY_WPA3_SAE, "WIFI_SECURITY_WPA3_SAE"} - }; - - using Implementation = RPC::IteratorType; - securityModes = Core::Service::Create(modeInfo); - - return Core::ERROR_NONE; - } - - void NetworkManagerImplementation::ReportInterfaceStateChangedEvent(INetworkManager::InterfaceState state, string interface) - { - LOG_ENTRY_FUNCTION(); - if(Exchange::INetworkManager::INTERFACE_LINK_DOWN == state) { - // Start the connectivity monitor with 'false' to indicate the interface is down. - // The monitor will automatically exit after the retry attempts are completed, posting a 'noInternet' event. - connectivityMonitor.startConnectivityMonitor(false); - } - - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onInterfaceStateChange(state, interface); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportIPAddressChangedEvent(const string& interface, bool isAcquired, bool isIPv6, const string& ipAddress) - { - LOG_ENTRY_FUNCTION(); - if (isAcquired) { - // Start the connectivity monitor with 'true' to indicate the interface is up. - // The monitor will conntinoue even after no internet retry completed, Exit when fully connectd. - connectivityMonitor.startConnectivityMonitor(true); - } - - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onIPAddressChange(interface, isAcquired, isIPv6, ipAddress); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportActiveInterfaceChangedEvent(const string prevActiveInterface, const string currentActiveinterface) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onActiveInterfaceChange(prevActiveInterface, currentActiveinterface); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportInternetStatusChangedEvent(const InternetStatus oldState, const InternetStatus newstate) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onInternetStatusChange(oldState, newstate); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportAvailableSSIDsEvent(const string jsonOfWiFiScanResults) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - NMLOG_INFO("scan result is, %s", jsonOfWiFiScanResults.c_str()); - for (const auto callback : _notificationCallbacks) { - callback->onAvailableSSIDs(jsonOfWiFiScanResults); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportWiFiStateChangedEvent(const INetworkManager::WiFiState state) - { - LOG_ENTRY_FUNCTION(); - /* start signal strength monitor when wifi connected */ - if(INetworkManager::WiFiState::WIFI_STATE_CONNECTED == state) - m_wifiSignalMonitor.startWiFiSignalStrengthMonitor(DEFAULT_WIFI_SIGNAL_TEST_INTERVAL_SEC); - - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onWiFiStateChange(state); - } - _notificationLock.Unlock(); - } - - void NetworkManagerImplementation::ReportWiFiSignalStrengthChangedEvent(const string ssid, const string signalLevel, const WiFiSignalQuality signalQuality) - { - LOG_ENTRY_FUNCTION(); - _notificationLock.Lock(); - for (const auto callback : _notificationCallbacks) { - callback->onWiFiSignalStrengthChange(ssid, signalLevel, signalQuality); - - } - _notificationLock.Unlock(); - } - } -} diff --git a/NetworkManagerImplementation.h b/NetworkManagerImplementation.h deleted file mode 100644 index 9c86b6922..000000000 --- a/NetworkManagerImplementation.h +++ /dev/null @@ -1,248 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2022 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#pragma once - - -#include "Module.h" -#include -#include -#include -#include - -using namespace std; - -//#include -#include "INetworkManager.h" -#include "NetworkManagerLogger.h" -#include "WiFiSignalStrengthMonitor.h" -#include "NetworkManagerConnectivity.h" -#include "NetworkManagerStunClient.h" - -#define LOG_ENTRY_FUNCTION() { NMLOG_TRACE("Entering=%s", __FUNCTION__ ); } - -namespace WPEFramework -{ - namespace Plugin - { - class NetworkManagerImplementation : public Exchange::INetworkManager - { - enum NetworkEvents { - NETMGR_PING, - NETMGR_TRACE, - }; - - class Config : public Core::JSON::Container { - private: - Config(const Config&); - Config& operator=(const Config&); - - public: - class ConnectivityConf : public Core::JSON::Container { - public: - ConnectivityConf& operator=(const ConnectivityConf&) = delete; - - ConnectivityConf() - : Core::JSON::Container() - , endpoint_1(_T("http://clients3.google.com/generate_204")) - , endpoint_2(_T("")) - , endpoint_3(_T("")) - , endpoint_4(_T("")) - , endpoint_5(_T("")) - , ConnectivityCheckInterval(6) - { - Add(_T("endpoint_1"), &endpoint_1); - Add(_T("endpoint_2"), &endpoint_2); - Add(_T("endpoint_3"), &endpoint_3); - Add(_T("endpoint_4"), &endpoint_4); - Add(_T("endpoint_5"), &endpoint_5); - Add(_T("interval"), &ConnectivityCheckInterval); - } - ~ConnectivityConf() override = default; - - public: - /* connectivity configuration */ - Core::JSON::String endpoint_1; - Core::JSON::String endpoint_2; - Core::JSON::String endpoint_3; - Core::JSON::String endpoint_4; - Core::JSON::String endpoint_5; - Core::JSON::DecUInt32 ConnectivityCheckInterval; - }; - - class Stun : public Core::JSON::Container { - public: - Stun& operator=(const Stun&) = delete; - - Stun() - : Core::JSON::Container() - , stunEndpoint(_T("stun.l.google.com")) - , port(19302) - , interval(30) - { - Add(_T("endpoint"), &stunEndpoint); - Add(_T("port"), &port); - Add(_T("interval"), &interval); - } - ~Stun() override = default; - - public: - /* stun configuration */ - Core::JSON::String stunEndpoint; - Core::JSON::DecUInt32 port; - Core::JSON::DecUInt32 interval; - }; - - public: - Config() - : Core::JSON::Container() - { - Add(_T("connectivity"), &connectivityConf); - Add(_T("stun"), &stun); - Add(_T("loglevel"), &loglevel); - } - ~Config() override = default; - - public: - ConnectivityConf connectivityConf; - Stun stun; - Core::JSON::DecUInt32 loglevel; - }; - - public: - NetworkManagerImplementation(); - ~NetworkManagerImplementation() override; - - // Do not allow copy/move constructors - NetworkManagerImplementation(const NetworkManagerImplementation &) = delete; - NetworkManagerImplementation &operator=(const NetworkManagerImplementation &) = delete; - - BEGIN_INTERFACE_MAP(NetworkManagerImplementation) - INTERFACE_ENTRY(Exchange::INetworkManager) - END_INTERFACE_MAP - - // Handle Notification registration/removal - uint32_t Register(INetworkManager::INotification *notification) override; - uint32_t Unregister(INetworkManager::INotification *notification) override; - - public: - // Below Control APIs will work with RDK or GNome NW. - /* @brief Get all the Available Interfaces */ - uint32_t GetAvailableInterfaces (IInterfaceDetailsIterator*& interfaces/* @out */) override; - - /* @brief Get the active Interface used for external world communication */ - uint32_t GetPrimaryInterface (string& interface /* @out */) override; - /* @brief Set the active Interface used for external world communication */ - uint32_t SetPrimaryInterface (const string& interface/* @in */) override; - - /* @brief Enable/Disable the given interface */ - uint32_t SetInterfaceState(const string& interface/* @in */, const bool& isEnabled/* @in */) override; - /* @brief Get the state of given interface */ - uint32_t GetInterfaceState(const string& interface/* @in */, bool& isEnabled/* @out */) override; - - /* @brief Get IP Address Of the Interface */ - uint32_t GetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, IPAddressInfo& result /* @out */) override; - /* @brief Set IP Address Of the Interface */ - uint32_t SetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, const IPAddressInfo& address /* @in */) override; - - // WiFi Specific Methods - /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ - uint32_t StartWiFiScan(const WiFiFrequency frequency /* @in */); - uint32_t StopWiFiScan(void) override; - - uint32_t GetKnownSSIDs(IStringIterator*& ssids /* @out */) override; - uint32_t AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) override; - uint32_t RemoveKnownSSID(const string& ssid /* @in */) override; - - uint32_t WiFiConnect(const WiFiConnectTo& ssid /* @in */) override; - uint32_t WiFiDisconnect(void) override; - uint32_t GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) override; - - uint32_t StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) override; - uint32_t StopWPS(void) override; - uint32_t GetWifiState(WiFiState &state) override; - uint32_t GetWiFiSignalStrength(string& ssid /* @out */, string& signalStrength /* @out */, WiFiSignalQuality& quality /* @out */) override; - - uint32_t SetStunEndpoint (string const endPoint /* @in */, const uint32_t port /* @in */, const uint32_t bindTimeout /* @in */, const uint32_t cacheTimeout /* @in */) override; - uint32_t GetStunEndpoint (string &endPoint /* @out */, uint32_t& port /* @out */, uint32_t& bindTimeout /* @out */, uint32_t& cacheTimeout /* @out */) const override; - - /* @brief Get ConnectivityTest Endpoints */ - uint32_t GetConnectivityTestEndpoints(IStringIterator*& endPoints/* @out */) const override; - /* @brief Set ConnectivityTest Endpoints */ - uint32_t SetConnectivityTestEndpoints(IStringIterator* const endPoints /* @in */) override; - - /* @brief Get Internet Connectivty Status */ - uint32_t IsConnectedToInternet(const string &ipversion /* @in */, InternetStatus &result /* @out */) override; - /* @brief Get Authentication URL if the device is behind Captive Portal */ - uint32_t GetCaptivePortalURI(string &endPoints/* @out */) const override; - - /* @brief Start The Internet Connectivity Monitoring */ - uint32_t StartConnectivityMonitoring(const uint32_t interval/* @in */) override; - /* @brief Stop The Internet Connectivity Monitoring */ - uint32_t StopConnectivityMonitoring(void) const override; - - /* @brief Get the Public IP used for external world communication */ - uint32_t GetPublicIP (const string &ipversion /* @in */, string& ipAddress /* @out */) override; - - /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ - uint32_t Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const uint16_t timeOutInSeconds /* @in */, const string guid /* @in */, string& response /* @out */) override; - - /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ - uint32_t Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const string guid /* @in */, string& response /* @out */) override; - - uint32_t GetSupportedSecurityModes(ISecurityModeIterator*& securityModes /* @out */) const override; - - /* @brief Set the network manager plugin log level */ - uint32_t SetLogLevel(const NMLogging& logLevel /* @in */) override; - - /* @brief configure network manager plugin */ - uint32_t Configure(const string& configLine /* @in */, NMLogging& logLevel /* @out */) override; - - /* Events */ - void ReportInterfaceStateChangedEvent(INetworkManager::InterfaceState state, string interface); - void ReportIPAddressChangedEvent(const string& interface, bool isAcquired, bool isIPv6, const string& ipAddress); - void ReportActiveInterfaceChangedEvent(const string prevActiveInterface, const string currentActiveinterface); - void ReportInternetStatusChangedEvent(const InternetStatus oldState, const InternetStatus newstate); - void ReportAvailableSSIDsEvent(const string jsonOfWiFiScanResults); - void ReportWiFiStateChangedEvent(const INetworkManager::WiFiState state); - void ReportWiFiSignalStrengthChangedEvent(const string ssid , const string signalLevel , const WiFiSignalQuality signalQuality); - - private: - void platform_init(); - void retryIarmEventRegistration(); - void threadEventRegistration(); - void executeExternally(NetworkEvents event, const string commandToExecute, string& response); - - private: - std::list _notificationCallbacks; - Core::CriticalSection _notificationLock; - string m_defaultInterface; - string m_publicIP; - stun::client stunClient; - string m_stunEndPoint; - uint16_t m_stunPort; - uint16_t m_stunBindTimeout; - uint16_t m_stunCacheTimeout; - std::thread m_registrationThread; - public: - WiFiSignalStrengthMonitor m_wifiSignalMonitor; - mutable ConnectivityMonitor connectivityMonitor; - }; - } -} diff --git a/NetworkManagerJsonRpc.cpp b/NetworkManagerJsonRpc.cpp deleted file mode 100644 index 01c5b76ad..000000000 --- a/NetworkManagerJsonRpc.cpp +++ /dev/null @@ -1,1006 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2022 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#include "NetworkManager.h" - -#define LOGINFOMETHOD() { std::string json; parameters.ToString(json); NMLOG_TRACE("params=%s", json.c_str() ); } -#define LOGTRACEMETHODFIN() { std::string json; response.ToString(json); NMLOG_TRACE("response=%s", json.c_str() ); } - -using namespace NetworkManagerLogger; - -namespace WPEFramework -{ - namespace Plugin - { - /** - * Hook up all our JSON RPC methods - * - * Each method definition comprises of: - * * Input parameters - * * Output parameters - * * Method name - * * Function that implements that method - */ - void NetworkManager::RegisterAllMethods() - { - Register("SetLogLevel", &NetworkManager::SetLogLevel, this); - Register("GetAvailableInterfaces", &NetworkManager::GetAvailableInterfaces, this); - Register("GetPrimaryInterface", &NetworkManager::GetPrimaryInterface, this); - Register("SetPrimaryInterface", &NetworkManager::SetPrimaryInterface, this); - Register("SetInterfaceState", &NetworkManager::SetInterfaceState, this); - Register("GetInterfaceState", &NetworkManager::GetInterfaceState, this); - Register("GetIPSettings", &NetworkManager::GetIPSettings, this); - Register("SetIPSettings", &NetworkManager::SetIPSettings, this); - Register("GetStunEndpoint", &NetworkManager::GetStunEndpoint, this); - Register("SetStunEndpoint", &NetworkManager::SetStunEndpoint, this); - Register("GetConnectivityTestEndpoints", &NetworkManager::GetConnectivityTestEndpoints, this); - Register("SetConnectivityTestEndpoints", &NetworkManager::SetConnectivityTestEndpoints, this); - Register("IsConnectedToInternet", &NetworkManager::IsConnectedToInternet, this); - Register("GetCaptivePortalURI", &NetworkManager::GetCaptivePortalURI, this); - Register("StartConnectivityMonitoring", &NetworkManager::StartConnectivityMonitoring, this); - Register("StopConnectivityMonitoring", &NetworkManager::StopConnectivityMonitoring, this); - Register("GetPublicIP", &NetworkManager::GetPublicIP, this); - Register("Ping", &NetworkManager::Ping, this); - Register("Trace", &NetworkManager::Trace, this); - Register("StartWiFiScan", &NetworkManager::StartWiFiScan, this); - Register("StopWiFiScan", &NetworkManager::StopWiFiScan, this); - Register("GetKnownSSIDs", &NetworkManager::GetKnownSSIDs, this); - Register("AddToKnownSSIDs", &NetworkManager::AddToKnownSSIDs, this); - Register("RemoveKnownSSID", &NetworkManager::RemoveKnownSSID, this); - Register("WiFiConnect", &NetworkManager::WiFiConnect, this); - Register("WiFiDisconnect", &NetworkManager::WiFiDisconnect, this); - Register("GetConnectedSSID", &NetworkManager::GetConnectedSSID, this); - Register("StartWPS", &NetworkManager::StartWPS, this); - Register("StopWPS", &NetworkManager::StopWPS, this); - Register("GetWifiState", &NetworkManager::GetWifiState, this); - Register("GetWiFiSignalStrength", &NetworkManager::GetWiFiSignalStrength, this); - Register("GetSupportedSecurityModes", &NetworkManager::GetSupportedSecurityModes, this); - } - - /** - * Unregister all our JSON-RPC methods - */ - void NetworkManager::UnregisterAllMethods() - { - Unregister("SetLogLevel"); - Unregister("GetAvailableInterfaces"); - Unregister("GetPrimaryInterface"); - Unregister("SetPrimaryInterface"); - Unregister("SetInterfaceState"); - Unregister("GetInterfaceState"); - Unregister("GetIPSettings"); - Unregister("SetIPSettings"); - Unregister("GetStunEndpoint"); - Unregister("SetStunEndpoint"); - Unregister("GetConnectivityTestEndpoints"); - Unregister("SetConnectivityTestEndpoints"); - Unregister("IsConnectedToInternet"); - Unregister("GetCaptivePortalURI"); - Unregister("StartConnectivityMonitoring"); - Unregister("StopConnectivityMonitoring"); - Unregister("GetPublicIP"); - Unregister("Ping"); - Unregister("Trace"); - Unregister("StartWiFiScan"); - Unregister("StopWiFiScan"); - Unregister("GetKnownSSIDs"); - Unregister("AddToKnownSSIDs"); - Unregister("RemoveKnownSSID"); - Unregister("WiFiConnect"); - Unregister("WiFiDisconnect"); - Unregister("GetConnectedSSID"); - Unregister("StartWPS"); - Unregister("StopWPS"); - Unregister("GetWifiState"); - Unregister("GetWiFiSignalStrength"); - Unregister("GetSupportedSecurityModes"); - } - - uint32_t NetworkManager::SetLogLevel (const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - - uint32_t rc = Core::ERROR_GENERAL; - LogLevel level = INFO_LEVEL; - if (parameters.HasLabel("logLevel")) - { - level = static_cast (parameters["logLevel"].Number()); - - NetworkManagerLogger::SetLevel(level); - - const Exchange::INetworkManager::NMLogging log = static_cast (level); - if (_NetworkManager) - rc = _NetworkManager->SetLogLevel(log); - else - rc = Core::ERROR_UNAVAILABLE; - } - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetAvailableInterfaces (const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::IInterfaceDetailsIterator* interfaces = NULL; - if (_NetworkManager) - rc = _NetworkManager->GetAvailableInterfaces(interfaces); - else - rc = Core::ERROR_UNAVAILABLE; - - if (interfaces) - { - NMLOG_TRACE("received response"); - JsonArray array; - Exchange::INetworkManager::InterfaceDetails entry{}; - while (interfaces->Next(entry) == true) { - JsonObject each; - each[_T("type")] = entry.m_type; - each[_T("name")] = entry.m_name; - each[_T("mac")] = entry.m_mac; - each[_T("isEnabled")] = entry.m_isEnabled; - each[_T("isConnected")] = entry.m_isConnected; - - array.Add(JsonValue(each)); - } - - interfaces->Release(); - NMLOG_TRACE("Sending Success"); - response["interfaces"] = array; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetPrimaryInterface (const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string interface; - if (_NetworkManager) - rc = _NetworkManager->GetPrimaryInterface(interface); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["interface"] = interface; - m_defaultInterface = interface; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::SetPrimaryInterface (const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string interface = parameters["interface"].String(); - if (_NetworkManager) - rc = _NetworkManager->SetPrimaryInterface(interface); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::SetInterfaceState(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string interface = parameters["interface"].String(); - bool enabled = parameters["enabled"].Boolean(); - if (_NetworkManager) - rc = _NetworkManager->SetInterfaceState(interface, enabled); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetInterfaceState(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - bool isEnabled = false; - string interface = parameters["interface"].String(); - - if (_NetworkManager) - rc = _NetworkManager->GetInterfaceState(interface, isEnabled); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["enabled"] = isEnabled; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetIPSettings (const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string interface = ""; - string ipversion = ""; - Exchange::INetworkManager::IPAddressInfo result{}; - - if (parameters.HasLabel("interface")) - interface = parameters["interface"].String(); - if (parameters.HasLabel("ipversion")) - ipversion = parameters["ipversion"].String(); - if (_NetworkManager) - rc = _NetworkManager->GetIPSettings(interface, ipversion, result); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["interface"] = interface; - if(result.m_ipAddrType == "IPV6" || result.m_ipAddrType == "IPV4") - result.m_ipAddrType[2] = tolower(result.m_ipAddrType[2]); - response["ipversion"] = result.m_ipAddrType; - response["autoconfig"] = result.m_autoConfig; - response["ipaddress"] = result.m_ipAddress; - response["prefix"] = result.m_prefix; - response["gateway"] = result.m_gateway; - response["dhcpserver"] = result.m_dhcpServer; - if(!result.m_v6LinkLocal.empty()) - response["v6LinkLocal"] = result.m_v6LinkLocal; - response["primarydns"] = result.m_primaryDns; - response["secondarydns"] = result.m_secondaryDns; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::SetIPSettings(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::IPAddressInfo result{}; - string interface = ""; - string ipversion = ""; - - if (parameters.HasLabel("interface")) - interface = parameters["interface"].String(); - if (parameters.HasLabel("ipversion")) - ipversion = parameters["ipversion"].String(); - result.m_autoConfig = parameters["autoconfig"].Boolean(); - if (!result.m_autoConfig) - { - result.m_ipAddress = parameters["ipaddress"]; - result.m_prefix = parameters["prefix"].Number(); - result.m_gateway = parameters["gateway"]; - result.m_primaryDns = parameters["primarydns"]; - result.m_secondaryDns = parameters["secondarydns"]; - } - - if (_NetworkManager) - rc = _NetworkManager->SetIPSettings(interface, ipversion, result); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetStunEndpoint(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string endPoint; - uint32_t port; - uint32_t bindTimeout; - uint32_t cacheTimeout; - - if (_NetworkManager) - rc = _NetworkManager->GetStunEndpoint(endPoint, port, bindTimeout, cacheTimeout); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["endPoint"] = endPoint; - response["port"] = port; - response["bindTimeout"] = bindTimeout; - response["cacheTimeout"] = cacheTimeout; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::SetStunEndpoint(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string endPoint = parameters["endPoint"].String(); - uint32_t port = parameters["port"].Number(); - uint32_t bindTimeout = parameters["bindTimeout"].Number(); - uint32_t cacheTimeout = parameters["cacheTimeout"].Number(); - - if (_NetworkManager) - rc = _NetworkManager->SetStunEndpoint(endPoint, port, bindTimeout, cacheTimeout); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::IStringIterator* endpoints = NULL; - - - if (_NetworkManager) - rc = _NetworkManager->GetConnectivityTestEndpoints(endpoints); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - if (endpoints) - { - JsonArray array; - string endPoint{}; - while (endpoints->Next(endPoint) == true) - { - array.Add(endPoint); - } - endpoints->Release(); - response["endpoints"] = array; - response["success"] = true; - } - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::SetConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - ::WPEFramework::RPC::IIteratorType* endpointsIter{}; - JsonArray array = parameters["endpoints"].Array(); - - if (0 == array.Length() || 5 < array.Length()) - { - NMLOG_TRACE("minimum of 1 to maximum of 5 Urls are allowed"); - return rc; - } - - std::vector endpoints; - JsonArray::Iterator index(array.Elements()); - while (index.Next() == true) - { - if (Core::JSON::Variant::type::STRING == index.Current().Content()) - { - endpoints.push_back(index.Current().String().c_str()); - } - else - { - NMLOG_TRACE("Unexpected variant type"); - return rc; - } - } - endpointsIter = (Core::Service::Create(endpoints)); - - if (_NetworkManager) - rc = _NetworkManager->SetConnectivityTestEndpoints(endpointsIter); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - - if (endpointsIter) - endpointsIter->Release(); - - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::IsConnectedToInternet(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string ipversion = parameters["ipversion"].String(); - Exchange::INetworkManager::InternetStatus result; - - - if (_NetworkManager) - rc = _NetworkManager->IsConnectedToInternet(ipversion, result); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["isConnectedToInternet"] = (Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED == result); - response["internetState"] = static_cast (result); - switch (result) - { - case Exchange::INetworkManager::InternetStatus::INTERNET_LIMITED: - response["status"] = string("LIMITED_INTERNET"); - break; - case Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL: - response["status"] = string("CAPTIVE_PORTAL"); - break; - case Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED: - response["status"] = string("FULLY_CONNECTED"); - // when fully connected to internet set Network subsystem - //setInternetSubsystem(); - break; - default: - response["status"] = string("NO_INTERNET"); - break; - } - - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetCaptivePortalURI(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string endPoint; - if (_NetworkManager) - rc = _NetworkManager->GetCaptivePortalURI(endPoint); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["uri"] = endPoint; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StartConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - uint32_t interval = parameters["interval"].Number(); - - NMLOG_TRACE("connectivity interval = %d", interval); - if (_NetworkManager) - rc = _NetworkManager->StartConnectivityMonitoring(interval); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StopConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (_NetworkManager) - rc = _NetworkManager->StopConnectivityMonitoring(); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetPublicIP(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string ipAddress{}; - string ipversion = "IPv4"; - if (parameters.HasLabel("ipversion")) - ipversion = parameters["ipversion"].String(); - - if ((!m_publicIPAddress.empty()) && (m_publicIPAddressType == ipversion)) - { - rc = Core::ERROR_NONE; - ipAddress = m_publicIPAddress; - } - else - { - if (_NetworkManager) - rc = _NetworkManager->GetPublicIP(ipversion, ipAddress); - else - rc = Core::ERROR_UNAVAILABLE; - } - - if (Core::ERROR_NONE == rc) - { - response["publicIP"] = ipAddress; - response["ipversion"] = ipversion; - response["success"] = true; - - m_publicIPAddress = ipAddress; - m_publicIPAddressType = ipversion; - PublishToThunderAboutInternet(); - } - LOGTRACEMETHODFIN(); - return rc; - } - - void NetworkManager::PublishToThunderAboutInternet() - { - NMLOG_TRACE("No public IP persisted yet; Update the data"); - if (m_publicIPAddress.empty()) - { - JsonObject input, output; - GetPublicIP(input, output); - } - - if (!m_publicIPAddress.empty()) - { - PluginHost::ISubSystem* subSystem = _service->SubSystems(); - - if (subSystem != nullptr) - { - const PluginHost::ISubSystem::IInternet* internet(subSystem->Get()); - if (nullptr == internet) - { - subSystem->Set(PluginHost::ISubSystem::INTERNET, this); - NMLOG_INFO("Set INTERNET ISubSystem"); - } - - subSystem->Release(); - } - } - } - - uint32_t NetworkManager::Ping(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - string result{}; - uint32_t rc = Core::ERROR_GENERAL; - if (parameters.HasLabel("endpoint")) - { - string endpoint{}; - string guid{}; - string ipversion{"IPv4"}; - uint32_t noOfRequest = 3; - uint16_t timeOutInSeconds = 5; - - endpoint = parameters["endpoint"].String(); - - if (parameters.HasLabel("ipversion")) - ipversion = parameters["ipversion"].String(); - - if (parameters.HasLabel("noOfRequest")) - noOfRequest = parameters["noOfRequest"].Number(); - - if (parameters.HasLabel("timeout")) - timeOutInSeconds = parameters["timeout"].Number(); - - if (parameters.HasLabel("guid")) - guid = parameters["guid"].String(); - - if (_NetworkManager) - rc = _NetworkManager->Ping(ipversion, endpoint, noOfRequest, timeOutInSeconds, guid, result); - else - rc = Core::ERROR_UNAVAILABLE; - } - - if (Core::ERROR_NONE == rc) - { - JsonObject reply; - reply.FromString(result); - response = reply; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::Trace(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string result{}; - const string ipversion = parameters["ipversion"].String(); - const string endpoint = parameters["endpoint"].String(); - const uint32_t noOfRequest = parameters["packets"].Number(); - const string guid = parameters["guid"].String(); - - if (_NetworkManager) - rc = _NetworkManager->Trace(ipversion, endpoint, noOfRequest, guid, result); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - JsonObject reply; - reply.FromString(result); - reply["success"] = true; - response = reply; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StartWiFiScan(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - const Exchange::INetworkManager::WiFiFrequency frequency = static_cast (parameters["frequency"].Number()); - - if (_NetworkManager) - rc = _NetworkManager->StartWiFiScan(frequency); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StopWiFiScan(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (_NetworkManager) - rc = _NetworkManager->StopWiFiScan(); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetKnownSSIDs(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - JsonArray ssids; - ::WPEFramework::RPC::IIteratorType* _ssids{}; - - if (_NetworkManager) - rc = _NetworkManager->GetKnownSSIDs(_ssids); - - if (Core::ERROR_NONE == rc) - { - ASSERT(_ssids != nullptr); - - if (_ssids != nullptr) - { - string _resultItem_{}; - while (_ssids->Next(_resultItem_) == true) - { - ssids.Add() = _resultItem_; - } - _ssids->Release(); - } - response["ssids"] = ssids; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::AddToKnownSSIDs(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::WiFiConnectTo ssid{}; - - if (parameters.HasLabel("ssid") && parameters.HasLabel("passphrase")) - { - ssid.m_ssid = parameters["ssid"].String(); - ssid.m_passphrase = parameters["passphrase"].String(); - ssid.m_securityMode = static_cast (parameters["securityMode"].Number()); - - if (_NetworkManager) - rc = _NetworkManager->AddToKnownSSIDs(ssid); - else - rc = Core::ERROR_UNAVAILABLE; - } - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::RemoveKnownSSID(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string ssid{}; - - if (parameters.HasLabel("ssid")) - { - ssid = parameters["ssid"].String(); - if (_NetworkManager) - rc = _NetworkManager->RemoveKnownSSID(ssid); - else - rc = Core::ERROR_UNAVAILABLE; - } - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::WiFiConnect(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::WiFiConnectTo ssid{}; - - if (parameters.HasLabel("ssid")) - ssid.m_ssid = parameters["ssid"].String(); - if (parameters.HasLabel("passphrase")) - ssid.m_passphrase = parameters["passphrase"].String(); - if (parameters.HasLabel("securityMode")) - ssid.m_securityMode = static_cast (parameters["securityMode"].Number()); - //TODO Check Security modes - if (parameters.HasLabel("identity")) - ssid.m_identity = parameters["identity"].String(); - if (parameters.HasLabel("caCert")) - ssid.m_caCert = parameters["caCert"].String(); - if (parameters.HasLabel("clientCert")) - ssid.m_clientCert = parameters["clientCert"].String(); - if (parameters.HasLabel("privateKey")) - ssid.m_privateKey = parameters["privateKey"].String(); - if (parameters.HasLabel("privateKeyPasswd")) - ssid.m_privateKeyPasswd = parameters["privateKeyPasswd"].String(); - if (parameters.HasLabel("persistSSIDInfo")) - ssid.m_persistSSIDInfo = parameters["persistSSIDInfo"].Boolean(); - else - ssid.m_persistSSIDInfo = true; - if (_NetworkManager) - rc = _NetworkManager->WiFiConnect(ssid); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::WiFiDisconnect(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (_NetworkManager) - rc = _NetworkManager->WiFiDisconnect(); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetConnectedSSID(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; - - if (_NetworkManager) - rc = _NetworkManager->GetConnectedSSID(ssidInfo); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["ssid"] = ssidInfo.m_ssid; - response["bssid"] = ssidInfo.m_bssid; - response["securityMode"] = static_cast (ssidInfo.m_securityMode); - response["signalStrength"] = ssidInfo.m_signalStrength; - response["frequency"] = ssidInfo.m_frequency; - response["rate"] = ssidInfo.m_rate; - response["noise"] = ssidInfo.m_noise; - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StartWPS(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string wps_pin{}; - Exchange::INetworkManager::WiFiWPS method; - - if (parameters.HasLabel("method")) - { - method = static_cast (parameters["method"].Number()); - } - else - method = Exchange::INetworkManager::WIFI_WPS_PBC; - - if ((Exchange::INetworkManager::WIFI_WPS_PIN == method) && parameters.HasLabel("wps_pin")) - { - wps_pin = parameters["wps_pin"].String(); - } - - if (_NetworkManager) - rc = _NetworkManager->StartWPS(method, wps_pin); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::StopWPS(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - - if (_NetworkManager) - rc = _NetworkManager->StopWPS(); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetWifiState(const JsonObject& parameters, JsonObject& response) - { - Exchange::INetworkManager::WiFiState state; - uint32_t rc = Core::ERROR_GENERAL; - - LOGINFOMETHOD(); - if (_NetworkManager) - rc = _NetworkManager->GetWifiState(state); - else - rc = Core::ERROR_UNAVAILABLE; - if (Core::ERROR_NONE == rc) - { - response["state"] = static_cast (state); - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetWiFiSignalStrength(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - string ssid{}; - string signalStrength{}; - Exchange::INetworkManager::WiFiSignalQuality quality; - - if (_NetworkManager) - rc = _NetworkManager->GetWiFiSignalStrength(ssid, signalStrength, quality); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - response["ssid"] = ssid; - response["signalStrength"] = signalStrength; - response["quality"] = static_cast (quality); - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - - uint32_t NetworkManager::GetSupportedSecurityModes(const JsonObject& parameters, JsonObject& response) - { - LOGINFOMETHOD(); - uint32_t rc = Core::ERROR_GENERAL; - Exchange::INetworkManager::ISecurityModeIterator* securityModes{}; - - if (_NetworkManager) - rc = _NetworkManager->GetSupportedSecurityModes(securityModes); - else - rc = Core::ERROR_UNAVAILABLE; - - if (Core::ERROR_NONE == rc) - { - if (securityModes != nullptr) - { - JsonObject modes{}; - Exchange::INetworkManager::WIFISecurityModeInfo _resultItem_{}; - while (securityModes->Next(_resultItem_) == true) - { - response.Set(_resultItem_.m_securityModeText.c_str(), JsonValue(_resultItem_.m_securityMode)); - } - securityModes->Release(); - } - response["success"] = true; - } - LOGTRACEMETHODFIN(); - return rc; - } - } -} diff --git a/NetworkManagerLogger.cpp b/NetworkManagerLogger.cpp deleted file mode 100644 index fe67d812f..000000000 --- a/NetworkManagerLogger.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "NetworkManagerLogger.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef USE_RDK_LOGGER -#include "rdk_debug.h" -#endif - -namespace NetworkManagerLogger { - static LogLevel gDefaultLogLevel = TRACE_LEVEL; - - const char* methodName(const std::string& prettyFunction) - { - size_t colons = prettyFunction.find("::"); - size_t begin = prettyFunction.substr(0,colons).rfind(" ") + 1; - size_t end = prettyFunction.rfind("(") - begin; - - return prettyFunction.substr(begin,end).c_str(); - } - - void Init() - { -#ifdef USE_RDK_LOGGER - rdk_logger_init(0 == access("/opt/debug.ini", R_OK) ? "/opt/debug.ini" : "/etc/debug.ini"); -#endif - } - - void logPrint(LogLevel level, const char* file, const char* func, int line, const char* format, ...) - { - size_t n = 0; - const short kFormatMessageSize = 1024; - char formattedLog[kFormatMessageSize] = {0}; - - va_list args; - - va_start(args, format); - n = vsnprintf(formattedLog, (kFormatMessageSize - 1), format, args); - va_end(args); - - if (n > (kFormatMessageSize - 1)) - { - formattedLog[kFormatMessageSize - 4] = '.'; - formattedLog[kFormatMessageSize - 3] = '.'; - formattedLog[kFormatMessageSize - 2] = '.'; - } - formattedLog[kFormatMessageSize - 1] = '\0'; -#ifdef USE_RDK_LOGGER - RDK_LOG((int)level, "LOG.RDK.NETSRVMGER", "%s\n", formattedLog); -#else - const char* levelMap[] = {"Fatal", "Error", "Warning", "Info", "Verbose", "Trace"}; - struct timeval tv; - struct tm* lt; - - if (gDefaultLogLevel < level) - return; - - gettimeofday(&tv, NULL); - lt = localtime(&tv.tv_sec); - - printf("%.2d:%.2d:%.2d.%.6lld %-10s %s:%d : %s\n", lt->tm_hour, lt->tm_min, lt->tm_sec, (long long int)tv.tv_usec, levelMap[level], basename(file), line, formattedLog); - fflush(stdout); -#endif - } - - void SetLevel(LogLevel level) - { - gDefaultLogLevel = level; - NMLOG_INFO("NetworkManager logLevel:%d", level); -#ifdef USE_RDK_LOGGER - // TODO : Inform RDKLogger to change the log level -#endif - } -} // namespace NetworkManagerLogger diff --git a/NetworkManagerPlugin.json b/NetworkManagerPlugin.json deleted file mode 100644 index 388367f4b..000000000 --- a/NetworkManagerPlugin.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/rdkcentral/rdkservices/main/Tools/json_generator/schemas/plugin.schema.json", - "info": { - "title": "NetworkManager Plugin", - "class": "NetworkManager", - "callsign": "org.rdk.NetworkManager", - "locator": "libWPEFrameworkNetworkManager.so", - "status": "production", - "description": "A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device.", - "version": "0.1.0" - }, - "interface": { - "$ref": "NetworkManager.json#" - } -} diff --git a/README.md b/README.md index e7138355f..dbe077017 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,8 @@ The documentation for legacy [Network](https://github.com/rdkcentral/rdkservices ## Release ## There are 2 active branches namely `develop` and `main`. As the name conveys, `develop` branch is for active development and for contributions. -The Plugin workflow verifies every PR and also the team verifies the plugin on the RPi and RDK Device +The Plugin workflow verifies every PR and also the team verifies the plugin on the RPi and RDK Device. The verified changes are taken to `main` branch and releases will be made from there ## Questions? ## -If you have any questions or concerns reach out to [Jacob Gladish](mailto:Jacob_Gladish@cable.comcast.com) - and [Karunakaran Amirthalingam](mailto:karunakaran_amirthalingam@comcast.com). +If you have any questions or concerns reach out to [Jacob Gladish](mailto:Jacob_Gladish@cable.comcast.com) and [Karunakaran Amirthalingam](mailto:karunakaran_amirthalingam@comcast.com). diff --git a/WiFiSignalStrengthMonitor.cpp b/WiFiSignalStrengthMonitor.cpp deleted file mode 100644 index 0230095b5..000000000 --- a/WiFiSignalStrengthMonitor.cpp +++ /dev/null @@ -1,141 +0,0 @@ -#include -#include -#include -#include -#include "NetworkManagerLogger.h" -#include "NetworkManagerImplementation.h" -#include "WiFiSignalStrengthMonitor.h" - -#define BUFFER_SIZE 512 -#define rssid_command "wpa_cli signal_poll" -#define ssid_command "wpa_cli status" - -namespace WPEFramework -{ - namespace Plugin - { - static const float signalStrengthThresholdExcellent = -50.0f; - static const float signalStrengthThresholdGood = -60.0f; - static const float signalStrengthThresholdFair = -67.0f; - extern NetworkManagerImplementation* _instance; - - std::string WiFiSignalStrengthMonitor::retrieveValues(const char *command, const char* keyword, char *output_buffer, size_t output_buffer_size) - { - std::string key, value; - std::string keystr = ""; - - FILE *fp = popen(command, "r"); - if (!fp) - { - NMLOG_ERROR("Failed in getting output from command %s \n",command); - return keystr; - } - - while ((!feof(fp)) && (fgets(output_buffer, output_buffer_size, fp) != NULL)) - { - std::istringstream mystream(output_buffer); - if(std::getline(std::getline(mystream, key, '=') >> std::ws, value)) - if (key == keyword) { - keystr = value; - break; - } - } - pclose(fp); - - return keystr; - } - - void WiFiSignalStrengthMonitor::getSignalData(std::string &ssid, Exchange::INetworkManager::WiFiSignalQuality &quality, std::string &strengthOut) - { - float signalStrengthOut = 0.0f; - char buff[BUFFER_SIZE] = {'\0'}; - - ssid = retrieveValues(ssid_command, "ssid", buff, sizeof (buff)); - if (ssid.empty()) - { - NMLOG_ERROR("ssid is empty"); - quality = Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED; - return; - } - - string signalStrength = retrieveValues(rssid_command, "RSSI", buff, sizeof (buff)); - if (!signalStrength.empty()) { - signalStrengthOut = std::stof(signalStrength.c_str()); - strengthOut = signalStrength; - } - else - NMLOG_ERROR("signalStrength is empty"); - - NMLOG_TRACE("SSID = %s Signal Strength %f db", ssid.c_str(), signalStrengthOut); - if (signalStrengthOut == 0.0f) - { - quality = Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED; - } - else if (signalStrengthOut >= signalStrengthThresholdExcellent && signalStrengthOut < 0) - { - quality = Exchange::INetworkManager::WIFI_SIGNAL_EXCELLENT; - } - else if (signalStrengthOut >= signalStrengthThresholdGood && signalStrengthOut < signalStrengthThresholdExcellent) - { - quality = Exchange::INetworkManager::WIFI_SIGNAL_GOOD; - } - else if (signalStrengthOut >= signalStrengthThresholdFair && signalStrengthOut < signalStrengthThresholdGood) - { - quality = Exchange::INetworkManager::WIFI_SIGNAL_FAIR; - } - else - { - quality = Exchange::INetworkManager::WIFI_SIGNAL_WEAK; - }; - } - - void WiFiSignalStrengthMonitor::startWiFiSignalStrengthMonitor(int interval) - { - stopThread = false; - if (isRunning) { - NMLOG_INFO("WiFiSignalStrengthMonitor Thread is already running."); - return; - } - isRunning = true; - monitorThread = std::thread(&WiFiSignalStrengthMonitor::monitorThreadFunction, this, interval); - monitorThread.detach(); - std::thread::id threadId = monitorThread.get_id(); - NMLOG_INFO("Thread started with interval: %d seconds", interval); - } - - void WiFiSignalStrengthMonitor::monitorThreadFunction(int interval) - { - static Exchange::INetworkManager::WiFiSignalQuality oldSignalQuality = Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED; - NMLOG_INFO("WiFiSignalStrengthMonitor thread started !"); - while (!stopThread) - { - string ssid = ""; - string signalStrength; - Exchange::INetworkManager::WiFiSignalQuality newSignalQuality; - if (_instance != nullptr) - { - NMLOG_TRACE("checking WiFi signal strength"); - getSignalData(ssid, newSignalQuality, signalStrength); - if(oldSignalQuality != newSignalQuality) - { - NMLOG_INFO("Notifying WiFiSignalStrengthChangedEvent %s", signalStrength.c_str()); - oldSignalQuality = newSignalQuality; - _instance->ReportWiFiSignalStrengthChangedEvent(ssid, signalStrength, newSignalQuality); - } - - if(newSignalQuality == Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED) - { - NMLOG_WARNING("WiFiSignalStrengthChanged to disconnect - WiFiSignalStrengthMonitor exiting"); - stopThread= false; - break; // Let the thread exit naturally - } - } - else - NMLOG_FATAL("NetworkManagerImplementation pointer error !"); - // Wait for the specified interval or until notified to stop - std::this_thread::sleep_for(std::chrono::seconds(interval)); - } - isRunning = false; - } - } -} diff --git a/WiFiSignalStrengthMonitor.h b/WiFiSignalStrengthMonitor.h deleted file mode 100644 index 56929aaeb..000000000 --- a/WiFiSignalStrengthMonitor.h +++ /dev/null @@ -1,50 +0,0 @@ -/** -* If not stated otherwise in this file or this component's LICENSE -* file the following copyright and licenses apply: -* -* Copyright 2020 RDK Management -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -**/ - -#pragma once - -#include -#include -#include -#include -#include -#include "Module.h" -#include "NetworkManagerLogger.h" - -#define DEFAULT_WIFI_SIGNAL_TEST_INTERVAL_SEC 60 -namespace WPEFramework -{ - namespace Plugin - { - class WiFiSignalStrengthMonitor - { - public: - WiFiSignalStrengthMonitor():isRunning(false) {} - ~WiFiSignalStrengthMonitor(){ NMLOG_INFO("~WiFiSignalStrengthMonitor"); } - void startWiFiSignalStrengthMonitor(int interval); - void getSignalData(std::string &ssid, Exchange::INetworkManager::WiFiSignalQuality &quality, std::string &strengthOut); - private: - std::string retrieveValues(const char *command, const char* keyword, char *output_buffer, size_t output_buffer_size); - std::thread monitorThread; - std::atomic stopThread; - std::atomic isRunning; - void monitorThreadFunction(int interval); - }; - } -} diff --git a/build_dependencies.sh b/build_dependencies.sh new file mode 100644 index 000000000..8aad62f39 --- /dev/null +++ b/build_dependencies.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# +# build_dependencies.sh - Install system packages and build the Thunder +# ecosystem (ThunderTools, Thunder, ThunderInterfaces) plus the mock +# dependency files required to build networkmanager for Coverity. +# +# This is the first half of the old cov_build.sh: everything that prepares +# the environment. The component itself is built by cov_build.sh, which is +# run afterwards from the same workspace. +# +# Usage: +# ./build_dependencies.sh +# +# Override any of the environment variables below on the command line, e.g.: +# THUNDER_REF=R4.4.3 ./build_dependencies.sh +# + +# Re-exec under bash if started with a non-bash shell (e.g. `sh build_dependencies.sh`, +# where sh is dash). This script relies on bash features such as `pipefail` +# and ${BASH_SOURCE[0]}, which dash does not support. +if [ -z "${BASH_VERSION:-}" ]; then + exec bash "$0" "$@" +fi + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Configuration (mirrors the workflow `env:` block) +# --------------------------------------------------------------------------- +BUILD_TYPE="${BUILD_TYPE:-Debug}" +THUNDER_REF="${THUNDER_REF:-R4.4.3}" +THUNDERTOOLS_REF="${THUNDERTOOLS_REF:-${THUNDER_REF}}" +INTERFACES_REF="${INTERFACES_REF:-${THUNDER_REF}}" + +# Root workspace directory. In CI this is ${{ github.workspace }}. +# The Thunder repositories and install tree live inside the networkmanager +# checkout so the layout is self-contained and shared with cov_build.sh. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE="${WORKSPACE:-${SCRIPT_DIR}}" +NETWORKMANAGER_DIR="${NETWORKMANAGER_DIR:-${SCRIPT_DIR}}" +INSTALL_DIR="${WORKSPACE}/install/usr" +MODULE_PATH="${WORKSPACE}/install/tools/cmake" + +NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + +log() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; } + +# Use sudo only when it exists (CI build containers usually run as root and +# do not ship sudo). Otherwise run the command directly. +if command -v sudo >/dev/null 2>&1; then + SUDO="sudo" +else + SUDO="" +fi + +# --------------------------------------------------------------------------- +# Install system packages +# --------------------------------------------------------------------------- +log "Installing system packages" +${SUDO} apt-get update +${SUDO} apt-get install -y \ + build-essential \ + cmake \ + pkg-config \ + libglib2.0-dev \ + libnm-dev \ + libcurl4-openssl-dev \ + lcov \ + ninja-build + +# --------------------------------------------------------------------------- +# Install Python dependencies +# --------------------------------------------------------------------------- +log "Installing Python dependencies" +if command -v pip >/dev/null 2>&1; then + pip install --break-system-packages jsonref || pip install jsonref || true +elif command -v pip3 >/dev/null 2>&1; then + pip3 install --break-system-packages jsonref || pip3 install jsonref || true +else + echo "pip not found; skipping jsonref install" +fi + +# --------------------------------------------------------------------------- +# Clone Thunder repositories +# --------------------------------------------------------------------------- +clone_repo() { + # clone_repo + local url="$1" path="$2" ref="$3" + if [ ! -d "${path}/.git" ]; then + log "Cloning ${url} (${ref}) into ${path}" + git clone --branch "${ref}" "${url}" "${path}" + else + log "Repository ${path} already present; fetching ${ref}" + git -C "${path}" fetch origin "${ref}" + git -C "${path}" checkout "${ref}" + fi +} + +clone_repo "https://github.com/rdkcentral/ThunderTools" \ + "${WORKSPACE}/ThunderTools" "${THUNDERTOOLS_REF}" +clone_repo "https://github.com/rdkcentral/Thunder" \ + "${WORKSPACE}/Thunder" "${THUNDER_REF}" +clone_repo "https://github.com/rdkcentral/ThunderInterfaces" \ + "${WORKSPACE}/ThunderInterfaces" "${INTERFACES_REF}" + +# --------------------------------------------------------------------------- +# Apply Thunder SubscribeStub patch +# --------------------------------------------------------------------------- +log "Applying Thunder SubscribeStub patch" +( + cd "${WORKSPACE}/Thunder" + git apply "${NETWORKMANAGER_DIR}/tests/patches/thunder/SubscribeStub.patch" 2>/dev/null \ + || echo "Patch already applied or not needed" +) + +# --------------------------------------------------------------------------- +# Build ThunderTools +# --------------------------------------------------------------------------- +log "Building ThunderTools" +cmake \ + -S "${WORKSPACE}/ThunderTools" \ + -B "${WORKSPACE}/build/ThunderTools" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \ + -DCMAKE_MODULE_PATH="${MODULE_PATH}" \ + -DGENERIC_CMAKE_MODULE_PATH="${MODULE_PATH}" +cmake --build "${WORKSPACE}/build/ThunderTools" --target install -j"${NPROC}" + +# --------------------------------------------------------------------------- +# Build Thunder +# --------------------------------------------------------------------------- +log "Building Thunder" +cmake \ + -S "${WORKSPACE}/Thunder" \ + -B "${WORKSPACE}/build/Thunder" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \ + -DCMAKE_MODULE_PATH="${MODULE_PATH}" \ + -DBUILD_TYPE="${BUILD_TYPE}" \ + -DBINDING=127.0.0.1 \ + -DPORT=9998 +cmake --build "${WORKSPACE}/build/Thunder" --target install -j"${NPROC}" + +# --------------------------------------------------------------------------- +# Build ThunderInterfaces +# --------------------------------------------------------------------------- +log "Building ThunderInterfaces" +cmake \ + -S "${WORKSPACE}/ThunderInterfaces" \ + -B "${WORKSPACE}/build/ThunderInterfaces" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \ + -DCMAKE_MODULE_PATH="${MODULE_PATH}" +cmake --build "${WORKSPACE}/build/ThunderInterfaces" --target install -j"${NPROC}" + +# --------------------------------------------------------------------------- +# Install IPowerManager header into the Thunder interfaces include path +# --------------------------------------------------------------------------- +log "Installing IPowerManager header" +IFACE_DIR="$(find "${INSTALL_DIR}/include" -maxdepth 2 -name "interfaces" -type d | head -1)" +if [ -z "${IFACE_DIR}" ] || [ ! -d "${IFACE_DIR}" ]; then + echo "Error: Thunder interfaces include dir not found under ${INSTALL_DIR}/include" >&2 + exit 1 +fi +cp "${NETWORKMANAGER_DIR}/tests/mocks/thunder/IPowerManager.h" "${IFACE_DIR}/" + +# --------------------------------------------------------------------------- +# Generate dependency files +# --------------------------------------------------------------------------- +log "Generating /etc/device.properties" +${SUDO} tee /etc/device.properties >/dev/null <<'EOF' +ETHERNET_INTERFACE=eth0 +WIFI_INTERFACE=wlan0 +DEFAULT_HOSTNAME=rdk_test_device +EOF + +# --------------------------------------------------------------------------- +# Generate IARM headers/stubs +# --------------------------------------------------------------------------- +log "Generating IARM headers" +mkdir -p "${INSTALL_DIR}/lib" +printf 'void __nm_cov_stub(void){}\n' | ${CC:-cc} -fPIC -shared -x c - -o "${INSTALL_DIR}/lib/libIARMBus.so" +printf 'void __nm_cov_stub(void){}\n' | ${CC:-cc} -fPIC -shared -x c - -o "${INSTALL_DIR}/lib/libmfrlib.so" +mkdir -p "${INSTALL_DIR}/include/rdk/iarmbus" +mkdir -p "${INSTALL_DIR}/include/rdk/iarmmgrs-hal" +touch "${INSTALL_DIR}/include/rdk/iarmbus/libIARM.h" +touch "${INSTALL_DIR}/include/rdk/iarmmgrs-hal/mfrMgr.h" +( + cd "${NETWORKMANAGER_DIR}/tests" + mkdir -p headers/rdk/iarmbus + mkdir -p headers/rdk/iarmmgrs-hal + touch headers/rdk/iarmmgrs-hal/mfrMgr.h + touch headers/rdk/iarmbus/libIARM.h headers/rdk/iarmbus/libIBus.h +) + +log "Dependencies ready. Run cov_build.sh to build networkmanager." diff --git a/cmake/FindIARMBus.cmake b/cmake/FindIARMBus.cmake index 843e55c25..39a642aee 100644 --- a/cmake/FindIARMBus.cmake +++ b/cmake/FindIARMBus.cmake @@ -1,7 +1,8 @@ -# If not stated otherwise in this file or this component's license file the +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the # following copyright and licenses apply: # -# Copyright 2020 RDK Management +# Copyright 2023 RDK Management # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,6 +15,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +############################################################################# # - Try to find IARMBus # Once done this will define diff --git a/cmake/FindT2.cmake b/cmake/FindT2.cmake new file mode 100644 index 000000000..e26c834a2 --- /dev/null +++ b/cmake/FindT2.cmake @@ -0,0 +1,37 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2023 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# + +find_package(PkgConfig) + +find_library(T2_LIBRARIES NAMES telemetry_msgsender) +find_path(T2_INCLUDE_DIRS NAMES telemetry_busmessage_sender.h) + +set(T2_LIBRARIES ${T2_LIBRARIES} CACHE PATH "Path to telemetry library") +set(T2_INCLUDE_DIRS ${T2_INCLUDE_DIRS} ) +set(T2_INCLUDE_DIRS ${T2_INCLUDE_DIRS} CACHE PATH "Path to telemetry include") + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(T2 DEFAULT_MSG T2_INCLUDE_DIRS T2_LIBRARIES) + +mark_as_advanced( + T2_FOUND + T2_INCLUDE_DIRS + T2_LIBRARIES + T2_LIBRARY_DIRS + T2_FLAGS) diff --git a/cmake/Findrdklogger.cmake b/cmake/Findrdklogger.cmake new file mode 100644 index 000000000..267692be7 --- /dev/null +++ b/cmake/Findrdklogger.cmake @@ -0,0 +1,37 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2023 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# + +find_package(PkgConfig) + +find_library(RDKLOGGER_LIBRARIES NAMES rdkloggers) +find_path(RDKLOGGER_INCLUDE_DIRS NAMES rdk_debug.h) + +set(RDKLOGGER_LIBRARIES ${RDKLOGGER_LIBRARIES} CACHE PATH "Path to rdkLogger library") +set(RDKLOGGER_INCLUDE_DIRS ${RDKLOGGER_INCLUDE_DIRS} ) +set(RDKLOGGER_INCLUDE_DIRS ${RDKLOGGER_INCLUDE_DIRS} CACHE PATH "Path to rdkLogger include") + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(RDKLOGGER DEFAULT_MSG RDKLOGGER_INCLUDE_DIRS RDKLOGGER_LIBRARIES) + +mark_as_advanced( + RDKLOGGER_FOUND + RDKLOGGER_INCLUDE_DIRS + RDKLOGGER_LIBRARIES + RDKLOGGER_LIBRARY_DIRS + RDKLOGGER_FLAGS) diff --git a/conf/99-default-link-local b/conf/99-default-link-local new file mode 100644 index 000000000..41b7da7cd --- /dev/null +++ b/conf/99-default-link-local @@ -0,0 +1,3 @@ +[connection] +ipv4.link-local=2 +ipv4.dhcp-timeout=2147483647 diff --git a/cov_build.sh b/cov_build.sh new file mode 100644 index 000000000..72c2222f9 --- /dev/null +++ b/cov_build.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# +# cov_build.sh - Coverity-friendly build of networkmanager with the Gnome +# libnm proxy. +# +# This is the second half of the original cov_build.sh: it builds only the +# networkmanager component. The Thunder ecosystem and mock dependency files +# it relies on are produced by build_dependencies.sh, which must be run first +# from the same workspace. +# +# Usage: +# ./build_dependencies.sh +# ./cov_build.sh +# +# Override any of the environment variables below on the command line, e.g.: +# THUNDER_REF=R4.4.3 ./cov_build.sh +# + +# Re-exec under bash if started with a non-bash shell (e.g. `sh cov_build.sh`, +# where sh is dash). This script relies on bash features such as `pipefail` +# and ${BASH_SOURCE[0]}, which dash does not support. +if [ -z "${BASH_VERSION:-}" ]; then + exec bash "$0" "$@" +fi + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Configuration (must match build_dependencies.sh so the paths line up) +# --------------------------------------------------------------------------- +# Optional cross-compile toolchain file (empty for native builds). +TOOLCHAIN_FILE="${TOOLCHAIN_FILE:-}" + +# Root workspace directory. In CI this is ${{ github.workspace }}. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE="${WORKSPACE:-${SCRIPT_DIR}}" +NETWORKMANAGER_DIR="${NETWORKMANAGER_DIR:-${SCRIPT_DIR}}" +INSTALL_DIR="${WORKSPACE}/install/usr" +MODULE_PATH="${WORKSPACE}/install/tools/cmake" + +NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + +log() { printf '\n\033[1;34m==> %s\033[0m\n' "$*"; } + +# --------------------------------------------------------------------------- +# Build networkmanager with Gnome libnm Proxy +# --------------------------------------------------------------------------- +log "Building networkmanager with Gnome libnm Proxy" +NM_CXX_FLAGS=" -fprofile-arcs -ftest-coverage \ +-I ${NETWORKMANAGER_DIR}/tests/headers \ +-I ${NETWORKMANAGER_DIR}/tests/headers/rdk/iarmbus \ +-I ${NETWORKMANAGER_DIR}/tests/headers/rdk/iarmmgrs-hal \ +--include ${NETWORKMANAGER_DIR}/tests/mocks/Iarm.h \ +--include ${NETWORKMANAGER_DIR}/tests/mocks/mfrMgr.h " + +TOOLCHAIN_ARG=() +if [ -n "${TOOLCHAIN_FILE}" ]; then + TOOLCHAIN_ARG=(-DCMAKE_TOOLCHAIN_FILE="${TOOLCHAIN_FILE}") +fi + +cmake \ + -S "${NETWORKMANAGER_DIR}" \ + -B "${WORKSPACE}/build/networkmanager_libnm" \ + "${TOOLCHAIN_ARG[@]}" \ + -DCMAKE_INSTALL_PREFIX="${INSTALL_DIR}" \ + -DCMAKE_PREFIX_PATH="${INSTALL_DIR}" \ + -DCMAKE_MODULE_PATH="${MODULE_PATH}" \ + -DCMAKE_CXX_FLAGS="${NM_CXX_FLAGS}" \ + -DENABLE_GNOME_NETWORKMANAGER=ON \ + -DENABLE_LEGACY_PLUGINS=OFF \ + -DENABLE_UNIT_TESTING=ON \ + -DENABLE_PLUGIN_CLI=OFF \ + -DENABLE_MIGRATION_MFRMGR_SUPPORT=ON +cmake --build "${WORKSPACE}/build/networkmanager_libnm" --target install -j"${NPROC}" diff --git a/definition/CMakeLists.txt b/definition/CMakeLists.txt new file mode 100644 index 000000000..1bd873f20 --- /dev/null +++ b/definition/CMakeLists.txt @@ -0,0 +1,29 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Generate JsonRPC Documentation for NetworkManager.json") + +find_package(JsonGenerator REQUIRED) + +if(NOT GENERATOR_SEARCH_PATH) + set(GENERATOR_SEARCH_PATH ${CMAKE_SYSROOT}${CMAKE_INSTALL_PREFIX}/include/${NAMESPACE}) +endif() + +set(JSON_FILE "${CMAKE_CURRENT_SOURCE_DIR}/NetworkManager.json") +JsonGenerator(CODE INPUT ${JSON_FILE} OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH ${GENERATOR_SEARCH_PATH} CPPIFDIR "${CMAKE_CURRENT_SOURCE_DIR}" DOCS) + diff --git a/NetworkManager.json b/definition/NetworkManager.json similarity index 62% rename from NetworkManager.json rename to definition/NetworkManager.json index ed0bfba18..6cc483656 100644 --- a/NetworkManager.json +++ b/definition/NetworkManager.json @@ -1,19 +1,21 @@ { - "$schema": "https://raw.githubusercontent.com/rdkcentral/rdkservices/main/Tools/json_generator/schemas/interface.schema.json", + "$schema": "interface.schema.json", "jsonrpc": "2.0", "info": { "title": "NetworkManager Plugin", "class": "NetworkManager", "callsign": "org.rdk.NetworkManager", - "locator": "libWPEFrameworkNetworkManager.so", "status": "production", "description": "A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device.", - "version": "0.2.11" - }, - "common": { - "$ref": "../../common/common.json" + "sourcelocation": "https://github.com/rdkcentral/networkmanager/blob/main/definition/NetworkManager.json", + "version": "4.2.0" }, "definitions": { + "success": { + "summary": "Whether the request succeeded", + "type": "boolean", + "example": "true" + }, "interface": { "summary": "An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface", "type": "string", @@ -27,7 +29,7 @@ "autoconfig": { "summary": "`true` if DHCP is used, `false` if IP is configured manually", "type": "boolean", - "example": true + "example": false }, "dhcpserver": { "summary": "The DHCP Server address", @@ -44,10 +46,10 @@ "type": "integer", "example": 24 }, - "v6LinkLocal": { - "summary": "The v6LinkLocal", + "ula": { + "summary": "The IPv6 Unique Local Address", "type": "string", - "example": "192.168.1.1" + "example": "fd00:410:2016::" }, "gateway": { "summary": "The gateway address", @@ -67,30 +69,20 @@ "port":{ "summary": "STUN server port", "type": "integer", - "example": "3478" - }, - "endPoint":{ - "summary": "STUN server endPoint", - "type": "string", - "example": "stun.l.google.com" + "example": 3478 }, "endpoint":{ "summary": "The host name or IP address", "type": "string", "example": "45.57.221.20" }, - "bindTimeout":{ - "summary": "STUN server bind timeout", - "type": "integer", - "example": "30" - }, - "cacheTimeout":{ + "cacheLifetime":{ "summary": "STUN server cache timeout", "type": "integer", - "example": "0" + "example": 0 }, "state": { - "summary": "The internet state", + "summary": "The given State", "type": "integer", "example": 4 }, @@ -99,65 +91,127 @@ "type": "string", "example": "" }, - "noOfRequest": { - "summary": "The number of packets to send. Default is 15.", + "count": { + "summary": "The number of requests to send. Default is 1.", + "type": "integer", + "example": 10 + }, + "packets": { + "summary": "The number of packets to send. Default is 5.", "type": "integer", "example": 10 }, "timeout":{ "summary": "Timeout", "type": "integer", - "example": "30" + "example": 30 }, "ssid":{ - "summary": "The paired SSID", + "summary": "The WiFi SSID Name", "type": "string", - "example": "123412341234" + "example": "myHomeSSID" }, "bssid":{ - "summary": "The paired BSSID", + "summary": "The BSSID of given SSID", "type": "string", - "example": "ff:ff:ff:ff:ff:ff" + "example": "AA:BB:CC:DD:EE:FF" }, "passphrase": { "summary": "The access point password", "type": "string", "example": "password" }, - "securityMode": { - "summary": "The security mode. See `getSupportedSecurityModes`.", + "security":{ + "summary": "The security mode. See `GetSupportedSecurityModes`.", + "type": "integer", + "example": 2 + }, + "strength":{ + "summary": "The WiFi Signal RSSI value in dBm", + "type": "integer", + "example": -32 + }, + "noise":{ + "summary": "The WiFi Signal Noise detected in dBm", + "type": "integer", + "example": -96 + }, + "snr":{ + "summary": "Signal to Noise Ratio(SNR) in dBm", "type": "integer", - "example": 6 + "example": 74 }, - "signalStrength":{ - "summary": "The RSSI value in dBm", + "quality":{ + "summary": "WiFi Quality based on Signal to Noise Ratio (SNR)", "type": "string", - "example": "-27.000000" + "example": "Excellent" }, "frequency":{ "summary": "The supported frequency for this SSID in GHz", - "type": "string", - "example": "2.442000" + "type": "number", + "example": 2.4420 }, "errors": { "summary": "Error string of scan failure", "type": "string", "example": "" - }, - "empty_string": { - "summary": "Not supported", - "type": "string", - "example": "" } }, "methods": { + "SetLogLevel":{ + "summary": "Set Log level for more information. The possible set log level are as follows. \n* `0`: FATAL \n* `1`: ERROR \n* `2`: WARNING \n* `3`: INFO \n* `4`: DEBUG \n", + "params": { + "type": "object", + "properties": { + "level": { + "summary": "Set Log level to get more information", + "type": "integer", + "example": 1 + } + }, + "required": [ + "level" + ] + }, + "result": { + "type": "object", + "properties": { + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "success" + ] + } + }, + "GetLogLevel":{ + "summary": "Get Log level that is currently used.", + "result": { + "type": "object", + "properties": { + "level": { + "summary": "Get Log level to get more information", + "type": "integer", + "example": 1 + }, + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "level", + "success" + ] + } + }, "GetAvailableInterfaces":{ "summary": "Get device supported list of available interface including their state.", "result": { "type": "object", "properties": { - "Interfaces": { - "summary": "An interface", + "interfaces": { + "summary": "An interface details", "type":"array", "items": { "type":"object", @@ -177,12 +231,12 @@ "type": "string", "example": "AA:AA:AA:AA:AA:AA" }, - "isEnabled":{ + "enabled":{ "summary": "Whether the interface is currently enabled", "type": "boolean", "example": true }, - "isConnected":{ + "connected":{ "summary": "Whether the interface is currently connected", "type": "boolean", "example": true @@ -192,13 +246,13 @@ "type", "name", "mac", - "isEnabled", - "isConnected" + "enabled", + "connected" ] } }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -208,7 +262,7 @@ } }, "GetPrimaryInterface": { - "summary": "Gets the primary/default network interface for the device. The active network interface is defined as the one that can make requests to the external network. Returns one of the supported interfaces as per `GetAvailableInterfaces`, or an empty value which indicates that there is no default network interface.", + "summary": "Gets the primary/default network interface for the device. The active network interface is defined as the one that can make requests to the external network. Returns one of the supported interfaces as per `GetAvailableInterfaces`. If no active network is available, it returns empty string.", "result": { "type": "object", "properties": { @@ -221,18 +275,48 @@ ] } }, - "SetPrimaryInterface":{ - "summary": "Sets the primary/default interface for the device. This call fails if the interface is not enabled.", + "SetInterfaceState":{ + "summary": "Enable or disable the specified interface", "events":{ - "onActiveInterfaceChange" : "Triggered when device’s default interface changed.", - "onInterfaceStateChange" : "Triggered when interface’s state changed", - "onAddressChange" : "Triggered when the device connects to router.", - "onInternetStatusChange" : "Triggered when each IP address is lost or acquired." + "onInterfaceStateChange" : "Triggered when interface’s status changes to enabled." }, "params": { - "type":"object", + "type": "object", "properties": { - "interface": { + "interface": { + "summary": "Enable the specified interface", + "type": "string", + "example": "wlan0" + }, + "enabled":{ + "summary": "Whether the interface must be enabled or disabled", + "type": "boolean", + "example": true + } + }, + "required": [ + "interface", + "enabled" + ] + }, + "result": { + "type": "object", + "properties": { + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "success" + ] + } + }, + "GetInterfaceState":{ + "summary": "Gets the current Status of the specified interface", + "params": { + "type": "object", + "properties": { + "interface": { "$ref": "#/definitions/interface" } }, @@ -241,7 +325,21 @@ ] }, "result": { - "$ref": "#/common/result" + "type": "object", + "properties": { + "enabled":{ + "summary": "Whether the interface is enabled or disabled", + "type": "boolean", + "example": true + }, + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "enabled", + "success" + ] } }, "GetIPSettings":{ @@ -257,7 +355,6 @@ } }, "required": [ - "interface" ] }, "result": { @@ -284,8 +381,8 @@ "gateway": { "$ref": "#/definitions/gateway" }, - "v6LinkLocal": { - "$ref": "#/definitions/v6LinkLocal" + "ula": { + "$ref": "#/definitions/ula" }, "primarydns": { "$ref": "#/definitions/primarydns" @@ -294,7 +391,7 @@ "$ref": "#/definitions/secondarydns" }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -303,18 +400,18 @@ "autoconfig", "ipaddress", "prefix", - "v6LinkLocal", + "ula", "gateway", "primarydns", "secondarydns", - "success" + "success" ] } }, "SetIPSettings":{ - "summary": "Sets the IP settings for the given interface.", + "summary": "Sets the IP settings for the given interface. The `interface`, `ipversion`, and `autoconfig` parameters are mandatory. When `autoconfig` is `false`, the `ipaddress`, `prefix`, `gateway`, `primarydns`, and `secondarydns` parameters must also be provided.", "events":{ - "onAddressChange" : "Triggered when the device connects to router.", + "onIPAddressChange" : "Triggered when the device connects to router.", "onInternetStatusChange" : "Triggered when each IP address is lost or acquired." }, "params": { @@ -348,19 +445,14 @@ "required": [ "interface", "ipversion", - "autoconfig", - "ipaddress", - "prefix", - "gateway", - "primarydns", - "secondarydns" + "autoconfig" ] }, "result": { "type": "object", "properties": { "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -369,55 +461,55 @@ } }, "GetStunEndpoint": { - "summary": "Get the STUN Endpoint that is used to identify public IP of the device.", + "summary": "Get the STUN endpoint that is used to identify public IP of the device.", "result": { "type": "object", "properties": { - "endPoint": { - "$ref": "#/definitions/endPoint" + "endpoint": { + "$ref": "#/definitions/endpoint" }, "port": { "$ref": "#/definitions/port" }, - "bindTimeout": { - "$ref": "#/definitions/bindTimeout" + "timeout": { + "$ref": "#/definitions/timeout" }, - "cacheTimeout": { - "$ref": "#/definitions/cacheTimeout" + "cacheLifetime": { + "$ref": "#/definitions/cacheLifetime" }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "endPoint", + "endpoint", "port", - "bindTimeout", - "cacheTimeout", + "timeout", + "cacheLifetime", "success" ] } }, "SetStunEndpoint":{ - "summary": "Set the STUN Endpoint to be used to identify public IP of the device.", + "summary": "Set the STUN endpoint to be used to identify public IP of the device.", "params": { "type":"object", "properties": { - "endPoint": { - "$ref": "#/definitions/endPoint" + "endpoint": { + "$ref": "#/definitions/endpoint" }, "port": { "$ref": "#/definitions/port" }, - "bindTimeout": { - "$ref": "#/definitions/bindTimeout" + "timeout": { + "$ref": "#/definitions/timeout" }, - "cacheTimeout": { - "$ref": "#/definitions/cacheTimeout" + "cacheLifetime": { + "$ref": "#/definitions/cacheLifetime" } }, "required": [ - "endPoint", + "endpoint", "port" ] }, @@ -425,13 +517,9 @@ "type": "object", "properties": { "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } - }, - "required": [ - "public_ip", - "success" - ] + } } }, "GetConnectivityTestEndpoints":{ @@ -440,15 +528,16 @@ "type":"object", "properties": { "endpoints": { - "summary": "", + "summary": "A list of endpoint URLs used", "type": "array", "items": { + "summary": "The endpoint URL", "type": "string", "example": "http://clients3.google.com/generate_204" } }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -466,6 +555,7 @@ "summary": "A list of endpoints to test", "type": "array", "items": { + "summary": "The endpoint URL", "type": "string", "example": "http://clients3.google.com/generate_204" } @@ -476,37 +566,72 @@ ] }, "result": { - "$ref": "#/common/result" + "type": "object", + "properties": { + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "success" + ] } }, "IsConnectedToInternet":{ - "summary": "Seeks Whether the device has internet connectivity. This API might take up to 3s to validate internet connectivity.", + "summary": "Seeks whether the device has internet connectivity. This API might take up to 5s to validate internet connectivity. If an interface is provided, connectivity is validated through that specific network interface. If an IP version is specified, connectivity is checked using that IP protocol.", "params": { "type":"object", "properties": { "ipversion": { "$ref": "#/definitions/ipversion" + }, + "interface": { + "$ref": "#/definitions/interface" } - } + }, + "required": [ + ] }, "result": { "type": "object", "properties": { - "isConnectedToInternet": { + "ipversion": { + "$ref": "#/definitions/ipversion" + }, + "interface": { + "$ref": "#/definitions/interface" + }, + "connected": { "summary": "`true` if internet connectivity is detected, otherwise `false`", "type": "boolean", - "example": true + "example": false }, - "internetState": { - "$ref": "#/definitions/state" + "state": { + "summary": "Internet state", + "type": "integer", + "example": 1 + }, + "status": { + "summary": "Internet status", + "type": "string", + "example": "NO_INTERNET" + }, + "reason": { + "summary": "Reason for current status (present when status is NO_INTERNET)", + "type": "string", + "enum": ["NOT_CONFIGURED", "WAITING_INTERFACE", "WAITING_GATEWAY", "PROBE_FAILED"], + "example": "PROBE_FAILED" }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "isConnectedToInternet", - "internetState", + "ipversion", + "interface", + "connected", + "state", + "status", "success" ] } @@ -522,7 +647,7 @@ "example": "http://10.0.0.1/captiveportal.jst" }, "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -531,78 +656,42 @@ ] } }, - "StartConnectivityMonitoring":{ - "summary": "Enable a continuous monitoring of internet connectivity with heart beat interval thats given. If the monitoring is already happening, it will be restarted with new given interval.", - "events":{ - "onInternetStatusChange" : "Triggered when internet connection state changed." - }, - "params": { - "type":"object", - "properties": { - "interval": { - "summary": "Interval in sec.", - "type": "number", - "example": "30" - } - }, - "required": [ - "interval" - ] - }, - "result": { - "type": "object", - "properties": { - "success":{ - "$ref": "#/common/success" - } - }, - "required": [ - "success" - ] - } - }, - "StopConnectivityMonitoring":{ - "summary": "Stops the connectivity monitoring", - "result": { - "type": "object", - "properties": { - "success":{ - "$ref": "#/common/success" - } - }, - "required": [ - "success" - ] - } - }, - "GetPublicIP":{ + "GetPublicIP":{ "summary": "Gets the internet/public IP Address of the device.", "params": { "type":"object", "summary":"it allows empty parameter too", "properties": { + "interface":{ + "$ref": "#/definitions/interface" + }, "ipversion": { "$ref": "#/definitions/ipversion" } }, "required": [ - "ipversion" ] }, "result": { "type": "object", "properties": { - "publicIP":{ - "summary": "Returns an public ip of the device", - "type":"string", - "example": "69.136.49.95" + "interface":{ + "$ref": "#/definitions/interface" + }, + "ipversion": { + "$ref": "#/definitions/ipversion" + }, + "ipaddress": { + "$ref": "#/definitions/ipaddress" }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "publicIP", + "interface", + "ipversion", + "ipaddress", "success" ] } @@ -618,11 +707,13 @@ "ipversion": { "$ref": "#/definitions/ipversion" }, - "noOfRequest": { - "$ref": "#/definitions/noOfRequest" + "count": { + "$ref": "#/definitions/count" }, - "timeout": { - "$ref": "#/definitions/timeout" + "timeout":{ + "summary": "Timeout in seconds. Default is 1", + "type": "integer", + "example": 5 }, "guid": { "$ref": "#/definitions/guid" @@ -630,19 +721,14 @@ }, "required": [ "endpoint", - "ipversion", - "noOfRequest", - "timeout", - "guid" + "ipversion" ] }, "result": { "type": "object", "properties": { - "target": { - "summary": "The target IP address", - "type": "string", - "example": "45.57.221.20" + "endpoint":{ + "$ref": "#/definitions/endpoint" }, "packetsTransmitted": { "summary": "The number of packets sent", @@ -690,11 +776,11 @@ "example": "..." }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "target", + "endpoint", "packetsTransmitted", "packetsReceived", "packetLoss", @@ -717,13 +803,13 @@ "type":"object", "properties": { "endpoint":{ - "$ref": "#/definitions/endpoint" + "$ref": "#/definitions/endpoint" }, "ipversion": { "$ref": "#/definitions/ipversion" }, - "noOfRequest": { - "$ref": "#/definitions/noOfRequest" + "packets": { + "$ref": "#/definitions/packets" }, "guid": { "$ref": "#/definitions/guid" @@ -731,106 +817,78 @@ }, "required": [ "endpoint", - "ipversion", - "noOfRequest", - "guid" + "ipversion" ] }, "result": { "type": "object", "properties": { - "target": { - "summary": "The target IP address", - "type": "string", - "example": "45.57.221.20" - }, - "packetsTransmitted": { - "summary": "The number of packets sent", - "type": "integer", - "example": 10 - }, - "packetsReceived": { - "summary": "The number of packets received", - "type": "integer", - "example": 10 - }, - "packetLoss": { - "summary": "The number of packets lost", - "type": "string", - "example": "0.0" - }, - "tripMin": { - "summary": "The minimum amount of time to receive the packets", - "type": "string", - "example": "61.264" + "endpoint":{ + "$ref": "#/definitions/endpoint" }, - "tripAvg": { - "summary": "The average time to receive the packets", + "results": { + "summary": "The response of traceroute", "type": "string", - "example": "130.397" - }, - "tripMax": { - "summary": "The maximum amount of time to receive the packets", + "example": "..." + }, + "guid": { + "summary": "The globally unique identifier", "type": "string", - "example": "230.832" - }, - "tripStdDev": { - "summary": "The standard deviation for the trip", - "type": "string", - "example": "80.919" - }, - "error": { - "summary": "An error message", - "type": "string", - "example": "..." - }, - "guid": { - "summary": "The globally unique identifier", - "type": "string", - "example": "..." + "example": "..." }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "target", - "packetsTransmitted", - "packetsReceived", - "packetLoss", - "tripMin", - "tripAvg", - "tripMax", - "tripStdDev", - "error", + "endpoint", + "results", "guid", "success" ] } }, "StartWiFiScan":{ - "summary": "Initiates WiFi scaning. This method supports scanning for specific range of frequency like 2.4GHz only or 5GHz only or 6GHz only or ALL. When no input passed about the frequency to be scanned, it scans for all. It publishes 'onAvailableSSIDs' event upon completion.", + "summary": "Initiates WiFi scanning. This method supports scanning specific frequency bands (2.4GHz, 5GHz, 6GHz). When no input is passed for frequency, it scans all supported frequencies. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion.", "events": { "onAvailableSSIDs" : "Triggered when list of SSIDs is available after the scan completes." }, "params": { "type": "object", "properties": { - "frequency": { - "summary": "The frequency to scan. An empty or `null` value scans all frequencies. If a frequency is specified (2.4 or 5.0), then the results are only returned for matching frequencies.", - "type": "string", - "example": "" + "frequencies": { + "summary": "Frequency bands to scan. Omit this field or pass \"ALL\" to scan all frequencies.", + "type": "array", + "items": { + "summary": "The frequency to scan.", + "type": "string", + "enum": [ + "ALL", + "2.4", + "5", + "6" + ], + "example": "2.4" + } + }, + "ssids": { + "summary": "The list of SSIDs to be scanned.", + "type": "array", + "items": { + "summary": "The SSID to scan.", + "type": "string", + "example": "Xfinity Mobile" + } } }, "required": [ - "frequency" ] }, "result": { "type": "object", "properties": { "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -841,7 +899,15 @@ "StopWiFiScan":{ "summary": "Stops WiFi scanning. Any discovered SSIDs from the call to the `StartWiFiScan` method up to the point where this method is called are still returned as event.", "result": { - "$ref": "#/common/result" + "type": "object", + "properties": { + "success": { + "$ref": "#/definitions/success" + } + }, + "required": [ + "success" + ] } }, "GetKnownSSIDs":{ @@ -850,15 +916,16 @@ "type": "object", "properties": { "ssids": { - "summary": "Known SSIDS", + "summary": "A list of known SSIDs", "type": "array", "items": { + "summary": "The WiFi SSID Name", "type": "string", "example": "Xfinity_Guest" } }, "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -878,21 +945,21 @@ "passphrase": { "$ref": "#/definitions/passphrase" }, - "securityMode": { - "$ref": "#/definitions/securityMode" + "security": { + "$ref": "#/definitions/security" } }, "required": [ "ssid", "passphrase", - "securityMode" + "security" ] }, "result": { "type": "object", "properties": { "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -901,10 +968,10 @@ } }, "RemoveKnownSSID":{ - "summary": "Remove given SSID from saved SSIDs. This method just removes from the list and of the list is having only one entry thats being removed, it will initiate a disconnect.", + "summary": "Remove given SSID from saved SSIDs. This method just removes an entry from the list and of the list is having only one entry thats being removed, it will initiate a disconnect.", "events":{ "onWiFiStateChange" : "Triggered when Wifi state changes to DISCONNECTED", - "onAddressChange" : "Triggered when an IP Address is assigned or lost", + "onIPAddressChange" : "Triggered when an IP Address is assigned or lost", "onInternetStatusChange" : "Triggered when internet connection state changed" }, "params": { @@ -922,7 +989,37 @@ "type": "object", "properties": { "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" + } + }, + "required": [ + "success" + ] + } + }, + "ConnectToKnownSSID":{ + "summary": "Connects to a saved SSID. The `ssid` parameter is mandatory. Returns failure if `ssid` is not specified or not found in the saved SSIDs list.", + "events":{ + "onWiFiStateChange" : "Triggered when Wifi state changes to CONNECTING, CONNECTED .", + "onIPAddressChange" : "Triggered when an IP Address is assigned or lost", + "onInternetStatusChange" : "Triggered when internet connection state changed" + }, + "params": { + "type": "object", + "properties": { + "ssid": { + "$ref": "#/definitions/ssid" + } + }, + "required": [ + "ssid" + ] + }, + "result": { + "type": "object", + "properties": { + "success":{ + "$ref": "#/definitions/success" } }, "required": [ @@ -931,7 +1028,7 @@ } }, "WiFiConnect":{ - "summary": "Initiates request to connect to the specified SSID with the given passphrase. Passphrase can be `null` when the network security is `NONE`. When called with no arguments, this method attempts to connect to the saved SSID and password. See `AddToKnownSSIDs`.", + "summary": "Initiates request to connect to the specified SSID with the given passphrase. Passphrase can be `null` when the network security is `NONE`. The security mode is decided based on the highest security mode provided by the SSID. Also when called with no arguments, this method attempts to connect to the last connected SSID. See `AddToKnownSSIDs`.", "events":{ "onWiFiStateChange" : "Triggered when Wifi state changes to CONNECTING, CONNECTED ." }, @@ -944,32 +1041,97 @@ "passphrase": { "$ref": "#/definitions/passphrase" }, - "securityMode": { - "$ref": "#/definitions/securityMode" + "security": { + "summary": "Optional override to explicitly select the security mode; if omitted, the mode is decided based on the highest security mode provided by the SSID. See `GetSupportedSecurityModes` for valid values.", + "type": "integer", + "example": 2 + }, + "bssid": { + "summary": "BSSID to connect to. If specified, restricts connection to this BSSID only. Defaults to the best available BSSID for the SSID.", + "type": "string", + "example": "00:11:22:33:44:55" + }, + "frequency": { + "summary": "Frequency band: `1` - 2.4GHz, `2` - 5GHz. If specified, connects only to SSIDs on the given frequency band. Defaults to best available.", + "type": "integer", + "example": 2 + }, + "ca_cert": { + "summary": "The ca_cert to be used for EAP", + "type": "string", + "example": "..." + }, + "client_cert": { + "summary": "The client_cert to be used for EAP", + "type": "string", + "example": "..." + }, + "private_key": { + "summary": "The private_key to be used for EAP", + "type": "string", + "example": "..." + }, + "private_key_passwd": { + "summary": "The private_key_passwd to be used for EAP", + "type": "string", + "example": "..." + }, + "eap": { + "summary": "The EAP type to be used", + "type": "string", + "example": "TLS" + }, + "eap_identity": { + "summary": "The identity to be used for EAP", + "type": "string", + "example": "..." + }, + "eap_password": { + "summary": "The eap_password to be used for EAP", + "type": "string", + "example": "..." + }, + "eap_phase1": { + "summary": "The eap_phase1 to be used for EAP", + "type": "string", + "example": "..." + }, + "eap_phase2": { + "summary": "The eap_phase2 to be used for EAP", + "type": "string", + "example": "..." + }, + "persist": { + "summary": " To persist the SSID across reboots; similar to auto connect", + "type": "boolean", + "example": true + } + } + }, + "result": { + "type": "object", + "properties": { + "success": { + "$ref": "#/definitions/success" } }, "required": [ - "ssid", - "passphrase", - "securityMode" + "success" ] - }, - "result": { - "$ref": "#/common/result" } }, "WiFiDisconnect":{ "summary": "Disconnects from the currently connected SSID. A event will be posted upon completion", "events":{ - "onWIFIStateChange" : "Triggered when Wifi state changes to DISCONNECTED (only if currently connected).", - "onAddressChange" : "Triggered when an IP Address is assigned or lost", + "onWiFiStateChange" : "Triggered when Wifi state changes to DISCONNECTED (only if currently connected).", + "onIPAddressChange" : "Triggered when an IP Address is assigned or lost", "onInternetStatusChange" : "Triggered when internet connection state changed" }, "result": { "type": "object", "properties": { "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -988,29 +1150,27 @@ "bssid": { "$ref": "#/definitions/bssid" }, - "securityMode":{ + "security":{ "summary": "The security mode. See the `connect` method", "type": "string", - "example": "5" + "example": "2" }, - "signalStrength": { - "$ref": "#/definitions/signalStrength" + "strength": { + "$ref": "#/definitions/strength" }, "frequency": { "$ref": "#/definitions/frequency" }, "rate":{ "summary": "The physical data rate in Mbps", - "type": "string", - "example": "144.000000" + "type": "integer", + "example": 144 }, "noise":{ - "summary": "The average noise strength in dBm", - "type": "string", - "example": "-121.000000" + "$ref": "#/definitions/noise" }, "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -1018,8 +1178,8 @@ "bssid", "rate", "noise", - "securityMode", - "signalStrength", + "security", + "strength", "frequency", "success" ] @@ -1028,8 +1188,8 @@ "StartWPS":{ "summary": "Initiates a connection using Wifi Protected Setup (WPS). An existing connection will be disconnected before attempting to initiate a new connection. Failure in WPS pairing will trigger an error event.\n\nIf the `method` parameter is set to `SERIALIZED_PIN`, then RDK retrieves the serialized pin using the Manufacturer (MFR) API. If the `method` parameter is set to `PIN`, then RDK use the pin supplied as part of the request. If the `method` parameter is set to `PBC`, then RDK uses Push Button Configuration (PBC) to obtain the pin.", "events":{ - "onWIFIStateChange" : "Triggered when Wifi state changes to DISCONNECTED (only if currently connected), CONNECTING, CONNECTED.", - "onAddressChange" : "Triggered when an IP Address is assigned or lost", + "onWiFiStateChange" : "Triggered when Wifi state changes to DISCONNECTED (only if currently connected), CONNECTING, CONNECTED.", + "onIPAddressChange" : "Triggered when an IP Address is assigned or lost", "onInternetStatusChange" : "Triggered when internet connection state changed" }, "params": { @@ -1037,18 +1197,17 @@ "properties": { "method": { "summary": "The method used to obtain the pin (must be one of the following: PBC=0, PIN=1, SERIALIZED_PIN=2)", - "type": "integer", - "example": 1 + "type": "string", + "example": "PIN" }, - "wps_pin": { + "pin": { "summary": "A valid 8 digit WPS pin number. Use this parameter when the `method` parameter is set to `PIN`.", "type": "string", "example": "88888888" } }, "required": [ - "method", - "wps_pin" + "method" ] }, "result": { @@ -1060,7 +1219,7 @@ "example": "88888888" }, "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -1072,13 +1231,13 @@ "StopWPS":{ "summary": "Cancels the in-progress WPS pairing operation. The operation forcefully stops the in-progress pairing attempt and aborts the current scan. WPS pairing must be in-progress for the operation to succeed.", "events":{ - "onWIFIStateChange" : "Triggered when Wifi state changes to DISCONNECTED." + "onWiFiStateChange" : "Triggered when Wifi state changes to DISCONNECTED." }, "result": { "type": "object", "properties": { "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ @@ -1086,10 +1245,10 @@ ] } }, - "GetWiFiSignalStrength":{ - "summary": "Get WiFiSignalStrength of connected SSID.", + "GetWiFiSignalQuality":{ + "summary": "Get WiFi signal quality of currently connected SSID. The signal quality is identified based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are\n* 'Excellent' : More than 40 dBm\n* 'Good' : 40 dBm to 25 dBm\n* 'Fair' : 25 dBm to 18 dBm\n* 'Weak' : 18 dBm to 0 dBm\n* 'Disconnected' : 0 dBm\n", "events":{ - "onWiFiSignalStrengthChange" : "Triggered when Wifi signal strength switches between Excellent, Good, Fair, Weak." + "onWiFiSignalQualityChange" : "Triggered when Wifi signal strength switches between Excellent, Good, Fair, Weak." }, "result": { "type": "object", @@ -1097,22 +1256,28 @@ "ssid": { "$ref": "#/definitions/ssid" }, - "signalStrength": { - "$ref": "#/definitions/signalStrength" - }, "quality":{ - "summary": "Signal strength Quality", - "type": "integer", - "example": 123 + "$ref": "#/definitions/quality" + }, + "snr": { + "$ref": "#/definitions/snr" + }, + "strength": { + "$ref": "#/definitions/strength" + }, + "noise": { + "$ref": "#/definitions/noise" }, "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ "ssid", - "signalStrength", "quality", + "snr", + "strength", + "noise", "success" ] } @@ -1122,190 +1287,100 @@ "result": { "type": "object", "properties": { - "security_modes": { + "security": { "summary": "The supported security modes and its associated integer value.", "type": "object", "properties": { - "NET_WIFI_SECURITY_NONE": { + "NONE": { + "summary": "Security mode for open network", "type": "integer", "example": 0 }, - "NET_WIFI_SECURITY_WEP_64": { + "WPA_PSK": { + "summary": "Supports security mode WPA,WPA-PSK,WPA2-PSK, WPA3-Personal-Transition", "type": "integer", "example": 1 }, - "NET_WIFI_SECURITY_WEP_128": { + "SAE": { + "summary": "Supports security mode WPA3-Personal", "type": "integer", "example": 2 }, - "NET_WIFI_SECURITY_WPA_PSK_TKIP": { + "EAP": { + "summary": "Supports security mode WPA enterprise", "type": "integer", "example": 3 - }, - "NET_WIFI_SECURITY_WPA_PSK_AES": { - "type": "integer", - "example": 4 - }, - "NET_WIFI_SECURITY_WPA2_PSK_TKIP": { - "type": "integer", - "example": 5 - }, - "NET_WIFI_SECURITY_WPA2_PSK_AES": { - "type": "integer", - "example": 6 - }, - "NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP": { - "type": "integer", - "example": 7 - }, - "NET_WIFI_SECURITY_WPA_ENTERPRISE_AES": { - "type": "integer", - "example": 8 - }, - "NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP": { - "type": "integer", - "example": 9 - }, - "NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES": { - "type": "integer", - "example": 10 - }, - "NET_WIFI_SECURITY_WPA_WPA2_PSK": { - "type": "integer", - "example": 11 - }, - "NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE": { - "type": "integer", - "example": 12 - }, - "NET_WIFI_SECURITY_WPA3_PSK_AES": { - "type": "integer", - "example": 13 - }, - "NET_WIFI_SECURITY_WPA3_SAE": { - "type": "integer", - "example": 14 } }, - "required": [] + "required": [ + "NONE", + "WPA_PSK", + "SAE", + "EAP" + ] }, "success":{ - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ - "security_modes", + "security", "success" ] } }, - "SetLogLevel":{ - "summary": "Set Log level for more information. The possible set log level are as follows. \n* `0`: FATAL \n* `1`: ERROR \n* `2`: WARNING \n* `3`: INFO \n* `4`: VERBOSE \n* `5`: TRACE \n", - "params": { - "type": "object", - "properties": { - "loglevel": { - "summary": "Set Log level to get more information", - "type": "integer", - "example": 1 - } - }, - "required": [ - "loglevel" - ] - }, + "GetWifiState": { + "summary": "Returns the current Wifi State. The possible Wifi states are as follows. \n**Wifi States** \n* `0`: WIFI_STATE_UNINSTALLED - The device was in an installed state and was uninstalled; or, the device does not have a Wifi radio installed \n* `1`: WIFI_STATE_DISABLED - The device is installed but not yet enabled \n* `2`: WIFI_STATE_DISCONNECTED - The device is installed and enabled, but not yet connected to a network \n* `3`: WIFI_STATE_PAIRING - The device is in the process of pairing, but not yet connected to a network \n* `4`: WIFI_STATE_CONNECTING - The device is attempting to connect to a network \n* `5`: WIFI_STATE_CONNECTED - The device is successfully connected to a network \n* `6`: WIFI_STATE_SSID_NOT_FOUND - The requested SSID to connect is not found \n* `7`: WIFI_STATE_SSID_CHANGED - The device connected SSID is changed \n* `8`: WIFI_STATE_CONNECTION_LOST - The device network connection is lost \n* `9`: WIFI_STATE_CONNECTION_FAILED - The device connection got failed \n* `10`: WIFI_STATE_CONNECTION_INTERRUPTED - The device connection is interrupted \n* `11`: WIFI_STATE_INVALID_CREDENTIALS - The credentials provided to connect is not valid \n* `12`: WIFI_STATE_AUTHENTICATION_FAILED - Authentication process as a whole could not be successfully completed \n* `13`: WIFI_STATE_ERROR - The device has encountered an unrecoverable error with the Wifi adapter.", "result": { "type": "object", "properties": { - "success": { - "$ref": "#/common/success" - } - }, - "required": [ - "success" - ] - } - }, - "EnableInterface":{ - "summary": "Enable the specified interface", - "events":{ - "onInterfaceStateChange" : "Triggered when interface’s status changes to enabled." - }, - "params": { - "type": "object", - "properties": { - "interface": { - "summary": "Enable the specified interface", + "state": { + "$ref": "#/definitions/state" + }, + "status": { + "summary": "WiFi status", "type": "string", - "example": "wlan0" - } - }, - "required": [ - "interface" - ] - }, - "result": { - "type": "object", - "properties": { - "success": { - "$ref": "#/common/success" + "example": "WIFI_STATE_CONNECTED" + }, + "success":{ + "$ref": "#/definitions/success" } }, "required": [ + "state", + "status", "success" ] } }, - "DisableInterface":{ - "summary": "Disable the specified interface", - "events":{ - "onInterfaceStateChange" : "Triggered when interface’s status changes to disabled." - }, + "SetHostname": { + "summary": "To configure a custom DHCP hostname instead of the default (which is typically the default hostname).\n\nSetting host name will take effect upon reconnect; like, device reboot, wake-up from deepsleep, while connecting to new Wi-Fi connection, WiFi On/Off, or renewal of the DHCP lease.", "params": { "type": "object", "properties": { - "interface": { - "summary": "Disable the specified interface", + "hostname": { + "summary": "The hostname to be set for the device (maximum 32 characters)", "type": "string", - "example": "wlan0" + "example": "RDK-Device" } }, "required": [ - "interface" + "hostname" ] }, "result": { "type": "object", "properties": { "success": { - "$ref": "#/common/success" + "$ref": "#/definitions/success" } }, "required": [ "success" ] } - }, - "GetWifiState": { - "summary": "Returns the current Wifi State. The possible Wifi states are as follows. \n**Wifi States** \n* `0`: UNINSTALLED - The device was in an installed state and was uninstalled; or, the device does not have a Wifi radio installed \n* `1`: DISABLED - The device is installed but not yet enabled \n* `2`: DISCONNECTED - The device is installed and enabled, but not yet connected to a network \n* `3`: PAIRING - The device is in the process of pairing, but not yet connected to a network \n* `4`: CONNECTING - The device is attempting to connect to a network \n* `5`: CONNECTED - The device is successfully connected to a network \n* `6`: SSID_NOT_FOUND - The requested SSID to connect is not found \n* `7`: SSID_CHANGED - The device connected SSID is changed \n* `8`: CONNECTION_LOST - The device network connection is lost \n* `9`: CONNECTION_FAILED - The device connection got failed \n* `10`: CONNECTION_INTERRUPTED - The device connection is interrupted \n* `11`: INVALID_CREDENTIALS - The credentials provided to connect is not valid \n* `12`: AUTHENTICATION_FAILED - Authentication process as a whole could not be successfully completed \n* `13`: ERROR - The device has encountered an unrecoverable error with the Wifi adapter.", - "result": { - "type": "object", - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "success":{ - "$ref": "#/common/success" - } - }, - "required": [ - "state", - "success" - ] - } } }, - "events": { "onInterfaceStateChange":{ "summary": "Triggered when an interface state is changed. The possible states are \n* 'INTERFACE_ADDED' \n* 'INTERFACE_LINK_UP' \n* 'INTERFACE_LINK_DOWN' \n* 'INTERFACE_ACQUIRING_IP' \n* 'INTERFACE_REMOVED' \n* 'INTERFACE_DISABLED' \n", @@ -1313,21 +1388,27 @@ "type": "object", "properties": { "interface":{ - "$ref": "#/definitions/interface" + "$ref": "#/definitions/interface" }, "state":{ "summary": "Current state of the interface", + "type": "integer", + "example": 1 + }, + "status":{ + "summary": "Current status of the interface", "type": "string", - "example": "INTERFACE_ADDED" + "example": "INTERFACE_LINK_UP" } }, "required": [ "interface", - "state" + "state", + "status" ] } }, - "onAddressChange":{ + "onIPAddressChange":{ "summary": "Triggered when an IP Address is assigned or lost.", "params": { "type": "object", @@ -1335,15 +1416,11 @@ "interface":{ "$ref": "#/definitions/interface" }, - "isIPv6":{ - "summary": "It will be true if the IP address is IPv6", - "type": "boolean", - "example": false + "ipaddress": { + "$ref": "#/definitions/ipaddress" }, - "ipAddress":{ - "summary": "The IPv6 or IPv4 address for the interface", - "type": "string", - "example": "192.168.1.100" + "ipversion": { + "$ref": "#/definitions/ipversion" }, "status":{ "summary": "Whether IP address was acquired or lost (must be one of the following: 'ACQUIRED', 'LOST')", @@ -1357,31 +1434,61 @@ }, "required": [ "interface", - "isIPv6", - "ipAddress", + "ipaddress", + "ipversion", "status" ] } }, + "onRouteChange":{ + "summary": "Triggered when the default route changes and a new gateway/DNS becomes available for an interface.", + "params": { + "type": "object", + "properties": { + "interface":{ + "$ref": "#/definitions/interface" + }, + "ipversion": { + "$ref": "#/definitions/ipversion" + }, + "ipaddress": { + "$ref": "#/definitions/ipaddress" + }, + "gateway": { + "$ref": "#/definitions/gateway" + }, + "primarydns": { + "$ref": "#/definitions/primarydns" + } + }, + "required": [ + "interface", + "ipversion", + "ipaddress", + "gateway", + "primarydns" + ] + } + }, "onActiveInterfaceChange":{ - "summary": "Triggered when the primary/active interface changes, regardless if it's from a system operation or through the `SetPrimaryInterface` method.", + "summary": "Triggered when the primary/active interface changes", "params": { "type": "object", "properties": { - "oldInterfaceName":{ + "prevActiveInterface":{ "summary": "The previous interface that was changed", "type": "string", "example": "wlan0" }, - "newInterfaceName":{ + "activeInterface":{ "summary": "The current interface", "type": "string", "example": "eth0" } }, "required": [ - "oldInterfaceName", - "newInterfaceName" + "prevActiveInterface", + "activeInterface" ] } }, @@ -1390,32 +1497,43 @@ "params": { "type": "object", "properties": { - "PrevState":{ - "summary": "The privious internet connection state", + "prevState":{ + "summary": "The previous internet connection state", "type": "integer", - "example": 1 + "example": 3 }, - "PrevStatus":{ + "prevStatus":{ "summary": "The previous internet connection status", "type": "string", - "example": "NO_INTERNET" + "example": "FULLY_CONNECTED" }, "state":{ "summary": "The internet connection state", "type": "integer", - "example": 4 + "example": 1 }, "status":{ "summary": "The internet connection status", "type": "string", - "example": "FULLY_CONNECTED" + "example": "NO_INTERNET" + }, + "interface":{ + "summary": "The internet status change on default interface", + "type": "string", + "example": "wlan0" + }, + "reason":{ + "summary": "The ConnectivityCheckMgr reason when status is NO_INTERNET", + "type": "string", + "example": "PROBE_FAILED" } }, "required": [ - "PrevState", - "PrevStatus", + "prevState", + "prevStatus", "state", - "status" + "status", + "interface" ] } }, @@ -1431,30 +1549,30 @@ "type":"object", "properties": { "ssid":{ - "summary": "ssid", + "summary": "Discovered SSID", "type": "string", "example": "myAP-2.4" }, + "bssid":{ + "summary": "Discovered BSSID", + "type": "string", + "example": "00:11:22:33:44:55" + }, "security":{ - "summary": "security", - "type": "integer", - "example": 6 + "$ref": "#/definitions/security" }, - "signalStrength":{ - "summary": "signalStrength", - "type": "string", - "example": "-27.000000" + "strength":{ + "$ref": "#/definitions/strength" }, "frequency":{ - "summary": "frequency", - "type": "string", - "example": "2.442000" + "$ref": "#/definitions/frequency" } }, "required": [ "ssid", + "bssid", "security", - "signalStrength", + "strength", "frequency" ] } @@ -1463,46 +1581,60 @@ } }, "onWiFiStateChange":{ - "summary": "Triggered when WIFI connection state get changed. The possible states are, \n * '0' - 'WIFI_STATE_UNINSTALLED' \n * '1' - 'WIFI_STATE_DISABLED' \n * '2' - 'WIFI_STATE_DISCONNECTED' \n * '3' - 'WIFI_STATE_PAIRING' \n * '4' - 'WIFI_STATE_CONNECTING' \n * '5' - 'WIFI_STATE_CONNECTED' \n * '6' - 'WIFI_STATE_SSID_NOT_FOUND' \n * '7' - 'WIFI_STATE_SSID_CHANGED' \n * '8' - 'WIFI_STATE_CONNECTION_LOST' \n * '9' - 'WIFI_STATE_CONNECTION_FAILED' \n * '10'- 'WIFI_STATE_CONNECTION_INTERRUPTED' \n * '11' - 'WIFI_STATE_INVALID_CREDENTIALS' \n * '12' - 'WIFI_STATE_AUTHENTICATION_FAILED' \n * '13' - 'WIFI_STATE_ERROR' \n", + "summary": "Triggered when WIFI connection state get changed. The possible states are defined in `GetWifiState()`", "params": { "type": "object", "properties": { "state":{ - "summary": "onWiFiStateChange", + "summary": "WiFi State", "type": "integer", - "example": "5" + "example": 5 + }, + "status": { + "summary": "WiFi status", + "type": "string", + "example": "WIFI_STATE_CONNECTED" + }, + "ssid": { + "summary": "The SSID associated with the Wi-Fi profile causing the state transition. Disconnected state, contains the SSID associated with the connection that was disconnected. The SSID will be empty when WPS initiated and no AP found with WPS enabled.", + "type": "string", + "example": "myHomeSSID" } }, "required": [ - "state" + "state", + "status", + "ssid" ] } }, - "onWiFiSignalStrengthChange":{ - "summary": "Triggered when WIFI connection Signal Strength get changed.", + "onWiFiSignalQualityChange":{ + "summary": "Triggered when WIFI Signal quality changed which is decided based on SNR value which is defined in `GetWiFiSignalQuality`", "params": { "type": "object", "properties": { "ssid":{ - "summary": "Signal Strength changed SSID", - "type": "string", - "example": "home-new_123" - }, - "signalStrength":{ - "summary": "Signal Strength", - "type": "string", - "example": "-27.000000" + "$ref": "#/definitions/ssid" }, "quality":{ - "summary": "Signal quality", - "type": "string", - "example": "Excellent" + "$ref": "#/definitions/quality" + }, + "snr":{ + "$ref": "#/definitions/snr" + }, + "strength":{ + "$ref": "#/definitions/strength" + }, + "noise":{ + "$ref": "#/definitions/noise" } }, "required": [ "ssid", - "signalStrength", - "quality" + "quality", + "snr", + "strength", + "noise" ] } } diff --git a/docs/NetworkManagerPlugin.md b/docs/NetworkManagerPlugin.md index 9e09b777c..62c3269cc 100644 --- a/docs/NetworkManagerPlugin.md +++ b/docs/NetworkManagerPlugin.md @@ -2,101 +2,165 @@ # NetworkManager Plugin -**Version: [0.2.11]()** +**Version: 4.2.0** -A NetworkManager plugin for Thunder framework. +**Status: :black_circle::black_circle::black_circle:** + +org.rdk.NetworkManager interface for Thunder framework. + +(Defined by [NetworkManager.json](https://github.com/rdkcentral/networkmanager/blob/main/definition/NetworkManager.json)) ### Table of Contents -- [Abbreviation, Acronyms and Terms](#head.Abbreviation,_Acronyms_and_Terms) +- [Introduction](#head.Introduction) - [Description](#head.Description) -- [Configuration](#head.Configuration) -- [Interfaces](#head.Interfaces) - [Methods](#head.Methods) - [Notifications](#head.Notifications) - -# Abbreviation, Acronyms and Terms + +# Introduction -[[Refer to this link](userguide/aat.md)] + +## Scope - -# Description +This document describes purpose and functionality of the org.rdk.NetworkManager interface (version 4.2.0). It includes detailed specification about its methods provided and notifications sent. -A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device. + +## Case Sensitivity - The plugin is designed to be an out-of-process service to be handled by Thunder framework. For more information about the framework refer to [[Thunder](#ref.Thunder)]. +All identifiers of the interfaces described in this document are case-sensitive. Thus, unless stated otherwise, all keywords, entities, properties, relations and actions should be treated as such. - -# Configuration + +## Acronyms, Abbreviations and Terms -The table below lists configuration options of the plugin. +The table below provides and overview of acronyms used in this document and their definitions. -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| callsign | string | Plugin instance name (default: *org.rdk.NetworkManager*) | -| classname | string | Class name: *NetworkManager* | -| locator | string | Library name: *libWPEFrameworkNetworkManager.so* | -| autostart | boolean | Determines if the plugin shall be started automatically along with the framework | +| Acronym | Description | +| :-------- | :-------- | +| API | Application Programming Interface | +| HTTP | Hypertext Transfer Protocol | +| JSON | JavaScript Object Notation; a data interchange format | +| JSON-RPC | A remote procedure call protocol encoded in JSON | - -# Interfaces +The table below provides and overview of terms and abbreviations used in this document and their definitions. -This plugin implements the following interfaces: +| Term | Description | +| :-------- | :-------- | +| callsign | The name given to an instance of a plugin. One plugin can be instantiated multiple times, but each instance the instance name, callsign, must be unique. | -- [NetworkManager.json](https://github.com/rdkcentral/ThunderInterfaces/blob/master/interfaces/NetworkManager.json) + +## References + +| Ref ID | Description | +| :-------- | :-------- | +| [HTTP](http://www.w3.org/Protocols) | HTTP specification | +| [JSON-RPC](https://www.jsonrpc.org/specification) | JSON-RPC 2.0 specification | +| [JSON](http://www.json.org/) | JSON specification | +| [Thunder](https://github.com/WebPlatformForEmbedded/Thunder/blob/master/doc/WPE%20-%20API%20-%20WPEFramework.docx) | Thunder API Reference | + + +# Description + +A Unified `NetworkManager` plugin that allows you to manage Ethernet and Wifi interfaces on the device. # Methods -The following methods are provided by the org.rdk.NetworkManager plugin: +The following methods are provided by the org.rdk.NetworkManager interface: NetworkManager interface methods: | Method | Description | | :-------- | :-------- | +| [SetLogLevel](#method.SetLogLevel) | Set Log level for more information | +| [GetLogLevel](#method.GetLogLevel) | Get Log level that is currently used | | [GetAvailableInterfaces](#method.GetAvailableInterfaces) | Get device supported list of available interface including their state | | [GetPrimaryInterface](#method.GetPrimaryInterface) | Gets the primary/default network interface for the device | -| [SetPrimaryInterface](#method.SetPrimaryInterface) | Sets the primary/default interface for the device | -| [SetInterfaceState](#method.SetInterfaceState) | Enable the interface | -| [GetInterfaceState](#method.GetInterfaceState) | Disable the interface | +| [SetInterfaceState](#method.SetInterfaceState) | Enable or disable the specified interface | +| [GetInterfaceState](#method.GetInterfaceState) | Gets the current Status of the specified interface | | [GetIPSettings](#method.GetIPSettings) | Gets the IP setting for the given interface | | [SetIPSettings](#method.SetIPSettings) | Sets the IP settings for the given interface | -| [GetStunEndpoint](#method.GetStunEndpoint) | Get the STUN Endpoint that is used to identify public IP of the device | -| [SetStunEndpoint](#method.SetStunEndpoint) | Set the STUN Endpoint to be used to identify public IP of the device | +| [GetStunEndpoint](#method.GetStunEndpoint) | Get the STUN endpoint that is used to identify public IP of the device | +| [SetStunEndpoint](#method.SetStunEndpoint) | Set the STUN endpoint to be used to identify public IP of the device | | [GetConnectivityTestEndpoints](#method.GetConnectivityTestEndpoints) | Gets currently used test endpoints | | [SetConnectivityTestEndpoints](#method.SetConnectivityTestEndpoints) | This method used to set up to 5 endpoints for a connectivity test | -| [IsConnectedToInternet](#method.IsConnectedToInternet) | Seeks Whether the device has internet connectivity | +| [IsConnectedToInternet](#method.IsConnectedToInternet) | Seeks whether the device has internet connectivity | | [GetCaptivePortalURI](#method.GetCaptivePortalURI) | Gets the captive portal URI if connected to any captive portal network | -| [StartConnectivityMonitoring](#method.StartConnectivityMonitoring) | Enable a continuous monitoring of internet connectivity with heart beat interval thats given | -| [StopConnectivityMonitoring](#method.StopConnectivityMonitoring) | Stops the connectivity monitoring | | [GetPublicIP](#method.GetPublicIP) | Gets the internet/public IP Address of the device | | [Ping](#method.Ping) | Pings the specified endpoint with the specified number of packets | | [Trace](#method.Trace) | Traces the specified endpoint with the specified number of packets using `traceroute` | -| [StartWiFiScan](#method.StartWiFiScan) | Initiates WiFi scaning | +| [StartWiFiScan](#method.StartWiFiScan) | Initiates WiFi scanning | | [StopWiFiScan](#method.StopWiFiScan) | Stops WiFi scanning | | [GetKnownSSIDs](#method.GetKnownSSIDs) | Gets list of saved SSIDs | | [AddToKnownSSIDs](#method.AddToKnownSSIDs) | Saves the SSID, passphrase, and security mode for upcoming and future sessions | | [RemoveKnownSSID](#method.RemoveKnownSSID) | Remove given SSID from saved SSIDs | +| [ConnectToKnownSSID](#method.ConnectToKnownSSID) | Connects to a saved SSID | | [WiFiConnect](#method.WiFiConnect) | Initiates request to connect to the specified SSID with the given passphrase | | [WiFiDisconnect](#method.WiFiDisconnect) | Disconnects from the currently connected SSID | | [GetConnectedSSID](#method.GetConnectedSSID) | Returns the connected SSID information | | [StartWPS](#method.StartWPS) | Initiates a connection using Wifi Protected Setup (WPS) | | [StopWPS](#method.StopWPS) | Cancels the in-progress WPS pairing operation | -| [GetWiFiSignalStrength](#method.GetWiFiSignalStrength) | Get WiFiSignalStrength of connected SSID | +| [GetWiFiSignalQuality](#method.GetWiFiSignalQuality) | Get WiFi signal quality of currently connected SSID | | [GetSupportedSecurityModes](#method.GetSupportedSecurityModes) | Returns the Wifi security modes that the device supports | -| [SetLogLevel](#method.SetLogLevel) | Set Log level for more information | | [GetWifiState](#method.GetWifiState) | Returns the current Wifi State | +| [SetHostname](#method.SetHostname) | To configure a custom DHCP hostname instead of the default (which is typically the default hostname) | + +## *SetLogLevel [method](#head.Methods)* - -## *GetAvailableInterfaces [method](#head.Methods)* +Set Log level for more information. The possible set log level are as follows. +* `0`: FATAL +* `1`: ERROR +* `2`: WARNING +* `3`: INFO +* `4`: DEBUG +. -Get device supported list of available interface including their state. +### Parameters -### Events +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.level | integer | Set Log level to get more information | -No Events +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | object | | +| result.success | boolean | Whether the request succeeded | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetLogLevel", + "params": { + "level": 1 + } +} +``` + +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } +} +``` + + +## *GetLogLevel [method](#head.Methods)* + +Get Log level that is currently used. ### Parameters @@ -107,13 +171,7 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result?.Interfaces | array | *(optional)* An interface | -| result?.Interfaces[#] | object | *(optional)* | -| result?.Interfaces[#].type | string | Interface Type | -| result?.Interfaces[#].name | string | Interface Name. ex: eth0 or wlan0 | -| result?.Interfaces[#].mac | string | Interface MAC address | -| result?.Interfaces[#].isEnabled | boolean | Whether the interface is currently enabled | -| result?.Interfaces[#].isConnected | boolean | Whether the interface is currently connected | +| result.level | integer | Get Log level to get more information | | result.success | boolean | Whether the request succeeded | ### Example @@ -122,9 +180,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetAvailableInterfaces" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetLogLevel" } ``` @@ -132,31 +190,19 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "Interfaces": [ - { - "type": "ETHERNET", - "name": "eth0", - "mac": "AA:AA:AA:AA:AA:AA", - "isEnabled": true, - "isConnected": true - } - ], - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "level": 1, + "success": true + } } ``` - -## *GetPrimaryInterface [method](#head.Methods)* - -Gets the primary/default network interface for the device. The active network interface is defined as the one that can make requests to the external network. Returns one of the supported interfaces as per `GetAvailableInterfaces`, or an empty value which indicates that there is no default network interface. - -### Events + +## *GetAvailableInterfaces [method](#head.Methods)* -No Events +Get device supported list of available interface including their state. ### Parameters @@ -167,7 +213,14 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| result.interfaces | array | An interface details | +| result.interfaces[#] | object | | +| result.interfaces[#].type | string | Interface Type | +| result.interfaces[#].name | string | Interface Name. ex: eth0 or wlan0 | +| result.interfaces[#].mac | string | Interface MAC address | +| result.interfaces[#].enabled | boolean | Whether the interface is currently enabled | +| result.interfaces[#].connected | boolean | Whether the interface is currently connected | +| result.success | boolean | Whether the request succeeded | ### Example @@ -175,9 +228,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetPrimaryInterface" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetAvailableInterfaces" } ``` @@ -185,40 +238,38 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "interface": "wlan0" - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "interfaces": [ + { + "type": "ETHERNET", + "name": "eth0", + "mac": "AA:AA:AA:AA:AA:AA", + "enabled": true, + "connected": true + } + ], + "success": true + } } ``` - -## *SetPrimaryInterface [method](#head.Methods)* - -Sets the primary/default interface for the device. This call fails if the interface is not enabled. + +## *GetPrimaryInterface [method](#head.Methods)* -### Events +Gets the primary/default network interface for the device. The active network interface is defined as the one that can make requests to the external network. Returns one of the supported interfaces as per `GetAvailableInterfaces`. If no active network is available, it returns empty string. -| Event | Description | -| :-------- | :-------- | -| [onActiveInterfaceChange](#event.onActiveInterfaceChange) | Triggered when device’s default interface changed. | -| [onInterfaceStateChange](#event.onInterfaceStateChange) | Triggered when interface’s state changed | -| [onAddressChange](#event.onAddressChange) | Triggered when the device connects to router. | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when each IP address is lost or acquired. | ### Parameters -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| params | object | | -| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +This method takes no parameters. ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.success | boolean | Whether the request succeeded | +| result.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | ### Example @@ -226,12 +277,9 @@ Sets the primary/default interface for the device. This call fails if the interf ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetPrimaryInterface", - "params": { - "interface": "wlan0" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetPrimaryInterface" } ``` @@ -239,32 +287,28 @@ Sets the primary/default interface for the device. This call fails if the interf ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "interface": "wlan0" + } } ``` ## *SetInterfaceState [method](#head.Methods)* -Enable or Disable the specified interface. +Enable or disable the specified interface. -### Events - -| Event | Description | -| :-------- | :-------- | -| [onInterfaceStateChange](#event.onInterfaceStateChange) | Triggered when interface’s status changes to enabled or disabled. | +Also see: [onInterfaceStateChange](#event.onInterfaceStateChange) ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface in `GetAvailableInterfaces` | -| params.enabled | boolean | Set the state of the interface to be Enabled or Disabled | +| params.interface | string | Enable the specified interface | +| params.enabled | boolean | Whether the interface must be enabled or disabled | ### Result @@ -279,13 +323,13 @@ Enable or Disable the specified interface. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetInterfaceState", - "params": { - "interface": "wlan0", - "enabled": true - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetInterfaceState", + "params": { + "interface": "wlan0", + "enabled": true + } } ``` @@ -293,36 +337,32 @@ Enable or Disable the specified interface. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` ## *GetInterfaceState [method](#head.Methods)* -Disable the specified interface. - -### Events - -No Events +Gets the current Status of the specified interface. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface in `GetAvailableInterfaces` | +| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.isEnabled | boolean | Whether the Interface is enabled or disabled | +| result.enabled | boolean | Whether the interface is enabled or disabled | | result.success | boolean | Whether the request succeeded | ### Example @@ -331,12 +371,12 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetInterfaceState", - "params": { - "interface": "wlan0" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetInterfaceState", + "params": { + "interface": "wlan0" + } } ``` @@ -344,12 +384,12 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "isEnabled": true, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "enabled": true, + "success": true + } } ``` @@ -358,17 +398,13 @@ No Events Gets the IP setting for the given interface. -### Events - -No Events - ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | -| params?.ipversion | string | *(optional)* either IPv4 or IPv6 | +| params?.interface | string | *(optional)* An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| params?.ipversion | string | *(optional)* Either IPv4 or IPv6 | ### Result @@ -376,13 +412,13 @@ No Events | :-------- | :-------- | :-------- | | result | object | | | result.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | -| result.ipversion | string | either IPv4 or IPv6 | +| result.ipversion | string | Either IPv4 or IPv6 | | result.autoconfig | boolean | `true` if DHCP is used, `false` if IP is configured manually | | result?.dhcpserver | string | *(optional)* The DHCP Server address | | result.ipaddress | string | The IP address | | result.prefix | integer | The prefix number | | result.gateway | string | The gateway address | -| result.v6LinkLocal | string | The v6LinkLocal | +| result.ula | string | The IPv6 Unique Local Address | | result.primarydns | string | The primary DNS address | | result.secondarydns | string | The secondary DNS address | | result.success | boolean | Whether the request succeeded | @@ -393,13 +429,13 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetIPSettings", - "params": { - "interface": "wlan0", - "ipversion": "IPv4" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetIPSettings", + "params": { + "interface": "wlan0", + "ipversion": "IPv4" + } } ``` @@ -407,48 +443,44 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "interface": "wlan0", - "ipversion": "IPv4", - "autoconfig": true, - "dhcpserver": "192.168.1.1", - "ipaddress": "192.168.1.101", - "prefix": 24, - "gateway": "192.168.1.1", - "v6LinkLocal": "192.168.1.1", - "primarydns": "192.168.1.1", - "secondarydns": "192.168.1.2", - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "interface": "wlan0", + "ipversion": "IPv4", + "autoconfig": false, + "dhcpserver": "192.168.1.1", + "ipaddress": "192.168.1.101", + "prefix": 24, + "gateway": "192.168.1.1", + "ula": "fd00:410:2016::", + "primarydns": "192.168.1.1", + "secondarydns": "192.168.1.2", + "success": true + } } ``` ## *SetIPSettings [method](#head.Methods)* -Sets the IP settings for the given interface. +Sets the IP settings for the given interface. The `interface`, `ipversion`, and `autoconfig` parameters are mandatory. When `autoconfig` is `false`, the `ipaddress`, `prefix`, `gateway`, `primarydns`, and `secondarydns` parameters must also be provided. -### Events +Also see: [onIPAddressChange](#event.onIPAddressChange), [onInternetStatusChange](#event.onInternetStatusChange) -| Event | Description | -| :-------- | :-------- | -| [onAddressChange](#event.onAddressChange) | Triggered when the device connects to router. | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when each IP address is lost or acquired. | ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | | params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | -| params.ipversion | string | either IPv4 or IPv6 | +| params.ipversion | string | Either IPv4 or IPv6 | | params.autoconfig | boolean | `true` if DHCP is used, `false` if IP is configured manually | -| params.ipaddress | string | The IP address | -| params.prefix | integer | The prefix number | -| params.gateway | string | The gateway address | -| params.primarydns | string | The primary DNS address | -| params.secondarydns | string | The secondary DNS address | +| params?.ipaddress | string | *(optional)* The IP address | +| params?.prefix | integer | *(optional)* The prefix number | +| params?.gateway | string | *(optional)* The gateway address | +| params?.primarydns | string | *(optional)* The primary DNS address | +| params?.secondarydns | string | *(optional)* The secondary DNS address | ### Result @@ -463,19 +495,19 @@ Sets the IP settings for the given interface. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetIPSettings", - "params": { - "interface": "wlan0", - "ipversion": "IPv4", - "autoconfig": true, - "ipaddress": "192.168.1.101", - "prefix": 24, - "gateway": "192.168.1.1", - "primarydns": "192.168.1.1", - "secondarydns": "192.168.1.2" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetIPSettings", + "params": { + "interface": "wlan0", + "ipversion": "IPv4", + "autoconfig": false, + "ipaddress": "192.168.1.101", + "prefix": 24, + "gateway": "192.168.1.1", + "primarydns": "192.168.1.1", + "secondarydns": "192.168.1.2" + } } ``` @@ -483,22 +515,18 @@ Sets the IP settings for the given interface. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` ## *GetStunEndpoint [method](#head.Methods)* -Get the STUN Endpoint that is used to identify public IP of the device. - -### Events - -No Events +Get the STUN endpoint that is used to identify public IP of the device. ### Parameters @@ -509,10 +537,10 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.endPoint | string | STUN server endPoint | +| result.endpoint | string | The host name or IP address | | result.port | integer | STUN server port | -| result.bindTimeout | integer | STUN server bind timeout | -| result.cacheTimeout | integer | STUN server cache timeout | +| result.timeout | integer | Timeout | +| result.cacheLifetime | integer | STUN server cache timeout | | result.success | boolean | Whether the request succeeded | ### Example @@ -521,9 +549,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetStunEndpoint" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetStunEndpoint" } ``` @@ -531,36 +559,32 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "endPoint": "stun.l.google.com", - "port": 3478, - "bindTimeout": 30, - "cacheTimeout": 0, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "endpoint": "45.57.221.20", + "port": 3478, + "timeout": 30, + "cacheLifetime": 0, + "success": true + } } ``` ## *SetStunEndpoint [method](#head.Methods)* -Set the STUN Endpoint to be used to identify public IP of the device. - -### Events - -No Events +Set the STUN endpoint to be used to identify public IP of the device. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.endPoint | string | STUN server endPoint | +| params.endpoint | string | The host name or IP address | | params.port | integer | STUN server port | -| params?.bindTimeout | integer | *(optional)* STUN server bind timeout | -| params?.cacheTimeout | integer | *(optional)* STUN server cache timeout | +| params?.timeout | integer | *(optional)* Timeout | +| params?.cacheLifetime | integer | *(optional)* STUN server cache timeout | ### Result @@ -575,15 +599,15 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetStunEndpoint", - "params": { - "endPoint": "stun.l.google.com", - "port": 3478, - "bindTimeout": 30, - "cacheTimeout": 0 - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetStunEndpoint", + "params": { + "endpoint": "45.57.221.20", + "port": 3478, + "timeout": 30, + "cacheLifetime": 0 + } } ``` @@ -591,11 +615,11 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` @@ -604,10 +628,6 @@ No Events Gets currently used test endpoints. on success list out the connectivity test points connections. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -617,8 +637,8 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.endpoints | array | | -| result.endpoints[#] | string | | +| result.endpoints | array | A list of endpoint URLs used | +| result.endpoints[#] | string | The endpoint URL | | result.success | boolean | Whether the request succeeded | ### Example @@ -627,9 +647,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetConnectivityTestEndpoints" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetConnectivityTestEndpoints" } ``` @@ -637,14 +657,14 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "endpoints": [ - "http://clients3.google.com/generate_204" - ], - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "endpoints": [ + "http://clients3.google.com/generate_204" + ], + "success": true + } } ``` @@ -653,17 +673,13 @@ This method takes no parameters. This method used to set up to 5 endpoints for a connectivity test. Successful connections are verified with HTTP Status code 204 (No Content). -### Events - -No Events - ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | | params.endpoints | array | A list of endpoints to test | -| params.endpoints[#] | string | | +| params.endpoints[#] | string | The endpoint URL | ### Result @@ -678,14 +694,14 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetConnectivityTestEndpoints", - "params": { - "endpoints": [ - "http://clients3.google.com/generate_204" - ] - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetConnectivityTestEndpoints", + "params": { + "endpoints": [ + "http://clients3.google.com/generate_204" + ] + } } ``` @@ -693,37 +709,38 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` ## *IsConnectedToInternet [method](#head.Methods)* -Seeks Whether the device has internet connectivity. This API might take up to 3s to validate internet connectivity. - -### Events - -No Events +Seeks whether the device has internet connectivity. This API might take up to 5s to validate internet connectivity. If an interface is provided, connectivity is validated through that specific network interface. If an IP version is specified, connectivity is checked using that IP protocol. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.ipversion | string | either IPv4 or IPv6 | +| params?.ipversion | string | *(optional)* Either IPv4 or IPv6 | +| params?.interface | string | *(optional)* An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.isConnectedToInternet | boolean | `true` if internet connectivity is detected, otherwise `false` | -| result.internetState | integer | The internet state | +| result.ipversion | string | Either IPv4 or IPv6 | +| result.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| result.connected | boolean | `true` if internet connectivity is detected, otherwise `false` | +| result.state | integer | Internet state | +| result.status | string | Internet status | +| result?.reason | string | *(optional)* Reason for current status (present when status is NO_INTERNET) | | result.success | boolean | Whether the request succeeded | ### Example @@ -732,12 +749,13 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.IsConnectedToInternet", - "params": { - "ipversion": "IPv4" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.IsConnectedToInternet", + "params": { + "ipversion": "IPv4", + "interface": "wlan0" + } } ``` @@ -745,13 +763,17 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "isConnectedToInternet": true, - "internetState": 4, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "ipversion": "IPv4", + "interface": "wlan0", + "connected": false, + "state": 1, + "status": "NO_INTERNET", + "reason": "PROBE_FAILED", + "success": true + } } ``` @@ -760,10 +782,6 @@ No Events Gets the captive portal URI if connected to any captive portal network. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -782,61 +800,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetCaptivePortalURI" -} -``` - -#### Response - -```json -{ - "jsonrpc": "2.0", - "id": 42, - "result": { - "uri": "http://10.0.0.1/captiveportal.jst", - "success": true - } -} -``` - - -## *StartConnectivityMonitoring [method](#head.Methods)* - -Enable a continuous monitoring of internet connectivity with heart beat interval thats given. If the monitoring is already happening, it will be restarted with new given interval. - -### Events - -| Event | Description | -| :-------- | :-------- | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when internet connection state changed. | -### Parameters - -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| params | object | | -| params.interval | number | Interval in sec | - -### Result - -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| result | object | | -| result.success | boolean | Whether the request succeeded | - -### Example - -#### Request - -```json -{ - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StartConnectivityMonitoring", - "params": { - "interval": 30 - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetCaptivePortalURI" } ``` @@ -844,55 +810,12 @@ Enable a continuous monitoring of internet connectivity with heart beat interval ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } -} -``` - - -## *StopConnectivityMonitoring [method](#head.Methods)* - -Stops the connectivity monitoring. - -### Events - -No Events - -### Parameters - -This method takes no parameters. - -### Result - -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| result | object | | -| result.success | boolean | Whether the request succeeded | - -### Example - -#### Request - -```json -{ - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StopConnectivityMonitoring" -} -``` - -#### Response - -```json -{ - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "uri": "http://10.0.0.1/captiveportal.jst", + "success": true + } } ``` @@ -901,23 +824,22 @@ This method takes no parameters. Gets the internet/public IP Address of the device. -### Events - -No Events - ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | -| params | object | it allows empty parameter too | -| params.ipversion | string | either IPv4 or IPv6 | +| params | object | It allows empty parameter too | +| params?.interface | string | *(optional)* An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| params?.ipversion | string | *(optional)* Either IPv4 or IPv6 | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.publicIP | string | Returns an public ip of the device | +| result.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| result.ipversion | string | Either IPv4 or IPv6 | +| result.ipaddress | string | The IP address | | result.success | boolean | Whether the request succeeded | ### Example @@ -926,12 +848,13 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetPublicIP", - "params": { - "ipversion": "IPv4" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetPublicIP", + "params": { + "interface": "wlan0", + "ipversion": "IPv4" + } } ``` @@ -939,12 +862,14 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "publicIP": "69.136.49.95", - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "interface": "wlan0", + "ipversion": "IPv4", + "ipaddress": "192.168.1.101", + "success": true + } } ``` @@ -953,27 +878,23 @@ No Events Pings the specified endpoint with the specified number of packets. -### Events - -No Events - ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | | params.endpoint | string | The host name or IP address | -| params.ipversion | string | either IPv4 or IPv6 | -| params.noOfRequest | integer | The number of packets to send. Default is 15 | -| params.timeout | integer | Timeout | -| params.guid | string | The globally unique identifier | +| params.ipversion | string | Either IPv4 or IPv6 | +| params?.count | integer | *(optional)* The number of requests to send. Default is 1 | +| params?.timeout | integer | *(optional)* Timeout in seconds. Default is 1 | +| params?.guid | string | *(optional)* The globally unique identifier | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.target | string | The target IP address | +| result.endpoint | string | The host name or IP address | | result.packetsTransmitted | integer | The number of packets sent | | result.packetsReceived | integer | The number of packets received | | result.packetLoss | string | The number of packets lost | @@ -991,16 +912,16 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.Ping", - "params": { - "endpoint": "45.57.221.20", - "ipversion": "IPv4", - "noOfRequest": 10, - "timeout": 30, - "guid": "..." - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.Ping", + "params": { + "endpoint": "45.57.221.20", + "ipversion": "IPv4", + "count": 10, + "timeout": 5, + "guid": "..." + } } ``` @@ -1008,21 +929,21 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "target": "45.57.221.20", - "packetsTransmitted": 10, - "packetsReceived": 10, - "packetLoss": "0.0", - "tripMin": "61.264", - "tripAvg": "130.397", - "tripMax": "230.832", - "tripStdDev": "80.919", - "error": "...", - "guid": "...", - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "endpoint": "45.57.221.20", + "packetsTransmitted": 10, + "packetsReceived": 10, + "packetLoss": "0.0", + "tripMin": "61.264", + "tripAvg": "130.397", + "tripMax": "230.832", + "tripStdDev": "80.919", + "error": "...", + "guid": "...", + "success": true + } } ``` @@ -1031,27 +952,24 @@ No Events Traces the specified endpoint with the specified number of packets using `traceroute`. -### Events - -No Events - ### Parameters -| Name | Type | Description | -| :-------- | :-------- | :-------- | +| Name | Type | Description | +| :-------- | :-------- | :-------- | | params | object | | -| params.ipversion| string | *(optional)* The host name or IP address | -| params.endpoint | string | The host name or IP address | -| params.packets | integer | *(optional)* The number of packets to send. Default is 10 | -| params.guid | string | *(optional)* The globally unique identifier | +| params.endpoint | string | The host name or IP address | +| params.ipversion | string | Either IPv4 or IPv6 | +| params?.packets | integer | *(optional)* The number of packets to send. Default is 5 | +| params?.guid | string | *(optional)* The globally unique identifier | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.target | string | The target IP address | -| result.results | string | The results from `traceroute` | +| result.endpoint | string | The host name or IP address | +| result.results | string | The response of traceroute | +| result.guid | string | The globally unique identifier | | result.success | boolean | Whether the request succeeded | ### Example @@ -1060,13 +978,15 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.Trace", - "params": { - "endpoint": "45.57.221.20", - "packets": 10 - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.Trace", + "params": { + "endpoint": "45.57.221.20", + "ipversion": "IPv4", + "packets": 10, + "guid": "..." + } } ``` @@ -1074,32 +994,33 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "target": "45.57.221.20", - "success": true, - "results": "<<>>" - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "endpoint": "45.57.221.20", + "results": "...", + "guid": "...", + "success": true + } } ``` ## *StartWiFiScan [method](#head.Methods)* -Initiates WiFi scaning. This method supports scanning for specific range of frequency like 2.4GHz only or 5GHz only or 6GHz only or ALL. When no input passed about the frequency to be scanned, it scans for all. It publishes 'onAvailableSSIDs' event upon completion. +Initiates WiFi scanning. This method supports scanning specific frequency bands (2.4GHz, 5GHz, 6GHz). When no input is passed for frequency, it scans all supported frequencies. When list of SSIDs to be scanned specifically, it can be passed as input. It publishes 'onAvailableSSIDs' event upon completion. -### Events +Also see: [onAvailableSSIDs](#event.onAvailableSSIDs) -| Event | Description | -| :-------- | :-------- | -| [onAvailableSSIDs](#event.onAvailableSSIDs) | Triggered when list of SSIDs is available after the scan completes. | ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.frequency | string | The frequency to scan. An empty or `null` value scans all frequencies. If a frequency is specified (2.4 or 5.0), then the results are only returned for matching frequencies | +| params?.frequencies | array | *(optional)* Frequency bands to scan. Omit this field or pass "ALL" to scan all frequencies | +| params?.frequencies[#] | string | *(optional)* The frequency to scan | +| params?.ssids | array | *(optional)* The list of SSIDs to be scanned | +| params?.ssids[#] | string | *(optional)* The SSID to scan | ### Result @@ -1114,12 +1035,17 @@ Initiates WiFi scaning. This method supports scanning for specific range of freq ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StartWiFiScan", - "params": { - "frequency": "..." - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.StartWiFiScan", + "params": { + "frequencies": [ + "2.4" + ], + "ssids": [ + "Xfinity Mobile" + ] + } } ``` @@ -1127,11 +1053,11 @@ Initiates WiFi scaning. This method supports scanning for specific range of freq ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` @@ -1140,10 +1066,6 @@ Initiates WiFi scaning. This method supports scanning for specific range of freq Stops WiFi scanning. Any discovered SSIDs from the call to the `StartWiFiScan` method up to the point where this method is called are still returned as event. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -1161,9 +1083,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StopWiFiScan" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.StopWiFiScan" } ``` @@ -1171,11 +1093,11 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` @@ -1184,10 +1106,6 @@ This method takes no parameters. Gets list of saved SSIDs. This method returns all the SSIDs that are saved as array. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -1197,8 +1115,8 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.ssids | array | Known SSIDS | -| result.ssids[#] | string | | +| result.ssids | array | A list of known SSIDs | +| result.ssids[#] | string | The WiFi SSID Name | | result.success | boolean | Whether the request succeeded | ### Example @@ -1207,9 +1125,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetKnownSSIDs" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetKnownSSIDs" } ``` @@ -1217,14 +1135,14 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "ssids": [ - "Xfinity_Guest" - ], - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "ssids": [ + "Xfinity_Guest" + ], + "success": true + } } ``` @@ -1233,18 +1151,14 @@ This method takes no parameters. Saves the SSID, passphrase, and security mode for upcoming and future sessions. This method only adds to the persistent memory; does not disconnect from currently connected SSID. -### Events - -No Events - ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.ssid | string | The paired SSID | +| params.ssid | string | The WiFi SSID Name | | params.passphrase | string | The access point password | -| params.securityMode | integer | The security mode. See `getSupportedSecurityModes` | +| params.security | integer | The security mode. See `GetSupportedSecurityModes` | ### Result @@ -1259,14 +1173,14 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.AddToKnownSSIDs", - "params": { - "ssid": "123412341234", - "passphrase": "password", - "securityMode": 6 - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.AddToKnownSSIDs", + "params": { + "ssid": "myHomeSSID", + "passphrase": "password", + "security": 2 + } } ``` @@ -1274,32 +1188,75 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` ## *RemoveKnownSSID [method](#head.Methods)* -Remove given SSID from saved SSIDs. This method just removes from the list and of the list is having only one entry thats being removed, it will initiate a disconnect. +Remove given SSID from saved SSIDs. This method just removes an entry from the list and of the list is having only one entry thats being removed, it will initiate a disconnect. -### Events +Also see: [onWiFiStateChange](#event.onWiFiStateChange), [onIPAddressChange](#event.onIPAddressChange), [onInternetStatusChange](#event.onInternetStatusChange) + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.ssid | string | The WiFi SSID Name | + +### Result + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| result | object | | +| result.success | boolean | Whether the request succeeded | + +### Example + +#### Request + +```json +{ + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.RemoveKnownSSID", + "params": { + "ssid": "myHomeSSID" + } +} +``` + +#### Response + +```json +{ + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } +} +``` + + +## *ConnectToKnownSSID [method](#head.Methods)* + +Connects to a saved SSID. The `ssid` parameter is mandatory. Returns failure if `ssid` is not specified or not found in the saved SSIDs list. + +Also see: [onWiFiStateChange](#event.onWiFiStateChange), [onIPAddressChange](#event.onIPAddressChange), [onInternetStatusChange](#event.onInternetStatusChange) -| Event | Description | -| :-------- | :-------- | -| [onWiFiStateChange](#event.onWiFiStateChange) | Triggered when Wifi state changes to DISCONNECTED | -| [onAddressChange](#event.onAddressChange) | Triggered when an IP Address is assigned or lost | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when internet connection state changed | ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.ssid | string | The paired SSID | +| params.ssid | string | The WiFi SSID Name | ### Result @@ -1314,12 +1271,12 @@ Remove given SSID from saved SSIDs. This method just removes from the list and o ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.RemoveKnownSSID", - "params": { - "ssid": "123412341234" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.ConnectToKnownSSID", + "params": { + "ssid": "myHomeSSID" + } } ``` @@ -1327,33 +1284,41 @@ Remove given SSID from saved SSIDs. This method just removes from the list and o ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` ## *WiFiConnect [method](#head.Methods)* -Initiates request to connect to the specified SSID with the given passphrase. Passphrase can be `null` when the network security is `NONE`. When called with no arguments, this method attempts to connect to the saved SSID and password. See `AddToKnownSSIDs`. +Initiates request to connect to the specified SSID with the given passphrase. Passphrase can be `null` when the network security is `NONE`. The security mode is decided based on the highest security mode provided by the SSID. Also when called with no arguments, this method attempts to connect to the last connected SSID. See `AddToKnownSSIDs`. -### Events +Also see: [onWiFiStateChange](#event.onWiFiStateChange) -| Event | Description | -| :-------- | :-------- | -| [onWiFiStateChange](#event.onWiFiStateChange) | Triggered when Wifi state changes to CONNECTING, CONNECTED . | ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.ssid | string | The paired SSID | -| params.passphrase | string | The access point password | -| params.securityMode | integer | The security mode. See `getSupportedSecurityModes` | -| params.persistSSIDInfo | boolean | *(optional)* Option to connect to SSID without persisting AccessPoint details (default: *true*; always persist) | +| params?.ssid | string | *(optional)* The WiFi SSID Name | +| params?.passphrase | string | *(optional)* The access point password | +| params?.security | integer | *(optional)* Optional override to explicitly select the security mode; if omitted, the mode is decided based on the highest security mode provided by the SSID. See `GetSupportedSecurityModes` for valid values | +| params?.bssid | string | *(optional)* BSSID to connect to. If specified, restricts connection to this BSSID only. Defaults to the best available BSSID for the SSID | +| params?.frequency | integer | *(optional)* Frequency band: `1` - 2.4GHz, `2` - 5GHz. If specified, connects only to SSIDs on the given frequency band. Defaults to best available | +| params?.ca_cert | string | *(optional)* The ca_cert to be used for EAP | +| params?.client_cert | string | *(optional)* The client_cert to be used for EAP | +| params?.private_key | string | *(optional)* The private_key to be used for EAP | +| params?.private_key_passwd | string | *(optional)* The private_key_passwd to be used for EAP | +| params?.eap | string | *(optional)* The EAP type to be used | +| params?.eap_identity | string | *(optional)* The identity to be used for EAP | +| params?.eap_password | string | *(optional)* The eap_password to be used for EAP | +| params?.eap_phase1 | string | *(optional)* The eap_phase1 to be used for EAP | +| params?.eap_phase2 | string | *(optional)* The eap_phase2 to be used for EAP | +| params?.persist | boolean | *(optional)* To persist the SSID across reboots; similar to auto connect | ### Result @@ -1368,15 +1333,26 @@ Initiates request to connect to the specified SSID with the given passphrase. Pa ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.WiFiConnect", - "params": { - "ssid": "123412341234", - "passphrase": "password", - "securityMode": 6, - "persistSSIDInfo": true - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.WiFiConnect", + "params": { + "ssid": "myHomeSSID", + "passphrase": "password", + "security": 2, + "bssid": "00:11:22:33:44:55", + "frequency": 2, + "ca_cert": "...", + "client_cert": "...", + "private_key": "...", + "private_key_passwd": "...", + "eap": "TLS", + "eap_identity": "...", + "eap_password": "...", + "eap_phase1": "...", + "eap_phase2": "...", + "persist": true + } } ``` @@ -1384,11 +1360,11 @@ Initiates request to connect to the specified SSID with the given passphrase. Pa ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` @@ -1397,13 +1373,8 @@ Initiates request to connect to the specified SSID with the given passphrase. Pa Disconnects from the currently connected SSID. A event will be posted upon completion. -### Events +Also see: [onWiFiStateChange](#event.onWiFiStateChange), [onIPAddressChange](#event.onIPAddressChange), [onInternetStatusChange](#event.onInternetStatusChange) -| Event | Description | -| :-------- | :-------- | -| [onWIFIStateChange](#event.onWIFIStateChange) | Triggered when Wifi state changes to DISCONNECTED (only if currently connected). | -| [onAddressChange](#event.onAddressChange) | Triggered when an IP Address is assigned or lost | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when internet connection state changed | ### Parameters This method takes no parameters. @@ -1421,9 +1392,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.WiFiDisconnect" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.WiFiDisconnect" } ``` @@ -1431,11 +1402,11 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` @@ -1444,10 +1415,6 @@ This method takes no parameters. Returns the connected SSID information. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -1457,13 +1424,13 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.ssid | string | The paired SSID | -| result.bssid | string | The paired BSSID | -| result.securityMode | string | The security mode. See the `connect` method | -| result.signalStrength | string | The RSSI value in dBm | -| result.frequency | string | The supported frequency for this SSID in GHz | -| result.rate | string | The physical data rate in Mbps | -| result.noise | string | The average noise strength in dBm | +| result.ssid | string | The WiFi SSID Name | +| result.bssid | string | The BSSID of given SSID | +| result.security | string | The security mode. See the `connect` method | +| result.strength | integer | The WiFi Signal RSSI value in dBm | +| result.frequency | number | The supported frequency for this SSID in GHz | +| result.rate | integer | The physical data rate in Mbps | +| result.noise | integer | The WiFi Signal Noise detected in dBm | | result.success | boolean | Whether the request succeeded | ### Example @@ -1472,9 +1439,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetConnectedSSID" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetConnectedSSID" } ``` @@ -1482,18 +1449,18 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "ssid": "123412341234", - "bssid": "ff:ff:ff:ff:ff:ff", - "securityMode": "5", - "signalStrength": "-27.000000", - "frequency": "2.442000", - "rate": "144.000000", - "noise": "-121.000000", - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "ssid": "myHomeSSID", + "bssid": "AA:BB:CC:DD:EE:FF", + "security": "2", + "strength": -32, + "frequency": 2.442, + "rate": 144, + "noise": -96, + "success": true + } } ``` @@ -1504,20 +1471,15 @@ Initiates a connection using Wifi Protected Setup (WPS). An existing connection If the `method` parameter is set to `SERIALIZED_PIN`, then RDK retrieves the serialized pin using the Manufacturer (MFR) API. If the `method` parameter is set to `PIN`, then RDK use the pin supplied as part of the request. If the `method` parameter is set to `PBC`, then RDK uses Push Button Configuration (PBC) to obtain the pin. -### Events +Also see: [onWiFiStateChange](#event.onWiFiStateChange), [onIPAddressChange](#event.onIPAddressChange), [onInternetStatusChange](#event.onInternetStatusChange) -| Event | Description | -| :-------- | :-------- | -| [onWIFIStateChange](#event.onWIFIStateChange) | Triggered when Wifi state changes to DISCONNECTED (only if currently connected), CONNECTING, CONNECTED. | -| [onAddressChange](#event.onAddressChange) | Triggered when an IP Address is assigned or lost | -| [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when internet connection state changed | ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.method | integer | The method used to obtain the pin (must be one of the following: PBC=0, PIN=1, SERIALIZED_PIN=2) | -| params.wps_pin | string | A valid 8 digit WPS pin number. Use this parameter when the `method` parameter is set to `PIN` | +| params.method | string | The method used to obtain the pin (must be one of the following: PBC=0, PIN=1, SERIALIZED_PIN=2) | +| params?.pin | string | *(optional)* A valid 8 digit WPS pin number. Use this parameter when the `method` parameter is set to `PIN` | ### Result @@ -1533,13 +1495,13 @@ If the `method` parameter is set to `SERIALIZED_PIN`, then RDK retrieves the ser ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StartWPS", - "params": { - "method": 1, - "wps_pin": "88888888" - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.StartWPS", + "params": { + "method": "PIN", + "pin": "88888888" + } } ``` @@ -1547,12 +1509,12 @@ If the `method` parameter is set to `SERIALIZED_PIN`, then RDK retrieves the ser ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "pin": "88888888", - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "pin": "88888888", + "success": true + } } ``` @@ -1561,11 +1523,8 @@ If the `method` parameter is set to `SERIALIZED_PIN`, then RDK retrieves the ser Cancels the in-progress WPS pairing operation. The operation forcefully stops the in-progress pairing attempt and aborts the current scan. WPS pairing must be in-progress for the operation to succeed. -### Events +Also see: [onWiFiStateChange](#event.onWiFiStateChange) -| Event | Description | -| :-------- | :-------- | -| [onWIFIStateChange](#event.onWIFIStateChange) | Triggered when Wifi state changes to DISCONNECTED. | ### Parameters This method takes no parameters. @@ -1583,9 +1542,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.StopWPS" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.StopWPS" } ``` @@ -1593,24 +1552,27 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` - -## *GetWiFiSignalStrength [method](#head.Methods)* + +## *GetWiFiSignalQuality [method](#head.Methods)* -Get WiFiSignalStrength of connected SSID. +Get WiFi signal quality of currently connected SSID. The signal quality is identified based on the Signal to Noise ratio which is calculated as SNR = rssi - noise. The possible states are +* 'Excellent' : More than 40 dBm +* 'Good' : 40 dBm to 25 dBm +* 'Fair' : 25 dBm to 18 dBm +* 'Weak' : 18 dBm to 0 dBm +* 'Disconnected' : 0 dBm +. -### Events +Also see: [onWiFiSignalQualityChange](#event.onWiFiSignalQualityChange) -| Event | Description | -| :-------- | :-------- | -| [onWiFiSignalStrengthChange](#event.onWiFiSignalStrengthChange) | Triggered when Wifi signal strength switches between Excellent, Good, Fair, Weak. | ### Parameters This method takes no parameters. @@ -1620,9 +1582,11 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.ssid | string | The paired SSID | -| result.signalStrength | string | The RSSI value in dBm | -| result.quality | integer | Signal strength Quality | +| result.ssid | string | The WiFi SSID Name | +| result.quality | string | WiFi Quality based on Signal to Noise Ratio (SNR) | +| result.snr | integer | Signal to Noise Ratio(SNR) in dBm | +| result.strength | integer | The WiFi Signal RSSI value in dBm | +| result.noise | integer | The WiFi Signal Noise detected in dBm | | result.success | boolean | Whether the request succeeded | ### Example @@ -1631,9 +1595,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetWiFiSignalStrength" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetWiFiSignalQuality" } ``` @@ -1641,14 +1605,16 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "ssid": "123412341234", - "signalStrength": "-27.000000", - "quality": 123, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "ssid": "myHomeSSID", + "quality": "Excellent", + "snr": 74, + "strength": -32, + "noise": -96, + "success": true + } } ``` @@ -1657,10 +1623,6 @@ This method takes no parameters. Returns the Wifi security modes that the device supports. -### Events - -No Events - ### Parameters This method takes no parameters. @@ -1670,22 +1632,11 @@ This method takes no parameters. | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.security_modes | object | The supported security modes and its associated integer value | -| result.security_modes?.NET_WIFI_SECURITY_NONE | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WEP_64 | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WEP_128 | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_PSK_TKIP | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_PSK_AES | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA2_PSK_TKIP | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA2_PSK_AES | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_ENTERPRISE_AES | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_WPA2_PSK | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA3_PSK_AES | integer | *(optional)* | -| result.security_modes?.NET_WIFI_SECURITY_WPA3_SAE | integer | *(optional)* | +| result.security | object | The supported security modes and its associated integer value | +| result.security.NONE | integer | Security mode for open network | +| result.security.WPA_PSK | integer | Supports security mode WPA,WPA-PSK,WPA2-PSK, WPA3-Personal-Transition | +| result.security.SAE | integer | Supports security mode WPA3-Personal | +| result.security.EAP | integer | Supports security mode WPA enterprise | | result.success | boolean | Whether the request succeeded | ### Example @@ -1694,9 +1645,9 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetSupportedSecurityModes" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetSupportedSecurityModes" } ``` @@ -1704,59 +1655,51 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "security_modes": { - "NET_WIFI_SECURITY_NONE": 0, - "NET_WIFI_SECURITY_WEP_64": 1, - "NET_WIFI_SECURITY_WEP_128": 2, - "NET_WIFI_SECURITY_WPA_PSK_TKIP": 3, - "NET_WIFI_SECURITY_WPA_PSK_AES": 4, - "NET_WIFI_SECURITY_WPA2_PSK_TKIP": 5, - "NET_WIFI_SECURITY_WPA2_PSK_AES": 6, - "NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP": 7, - "NET_WIFI_SECURITY_WPA_ENTERPRISE_AES": 8, - "NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP": 9, - "NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES": 10, - "NET_WIFI_SECURITY_WPA_WPA2_PSK": 11, - "NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE": 12, - "NET_WIFI_SECURITY_WPA3_PSK_AES": 13, - "NET_WIFI_SECURITY_WPA3_SAE": 14 - }, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "security": { + "NONE": 0, + "WPA_PSK": 1, + "SAE": 2, + "EAP": 3 + }, + "success": true + } } ``` - -## *SetLogLevel [method](#head.Methods)* - -Set Log level for more information. The possible set log level are as follows. -* `0`: FATAL -* `1`: ERROR -* `2`: WARNING -* `3`: INFO -* `4`: VERBOSE -* `5`: TRACE -. - -### Events + +## *GetWifiState [method](#head.Methods)* -No Events +Returns the current Wifi State. The possible Wifi states are as follows. +**Wifi States** +* `0`: WIFI_STATE_UNINSTALLED - The device was in an installed state and was uninstalled; or, the device does not have a Wifi radio installed +* `1`: WIFI_STATE_DISABLED - The device is installed but not yet enabled +* `2`: WIFI_STATE_DISCONNECTED - The device is installed and enabled, but not yet connected to a network +* `3`: WIFI_STATE_PAIRING - The device is in the process of pairing, but not yet connected to a network +* `4`: WIFI_STATE_CONNECTING - The device is attempting to connect to a network +* `5`: WIFI_STATE_CONNECTED - The device is successfully connected to a network +* `6`: WIFI_STATE_SSID_NOT_FOUND - The requested SSID to connect is not found +* `7`: WIFI_STATE_SSID_CHANGED - The device connected SSID is changed +* `8`: WIFI_STATE_CONNECTION_LOST - The device network connection is lost +* `9`: WIFI_STATE_CONNECTION_FAILED - The device connection got failed +* `10`: WIFI_STATE_CONNECTION_INTERRUPTED - The device connection is interrupted +* `11`: WIFI_STATE_INVALID_CREDENTIALS - The credentials provided to connect is not valid +* `12`: WIFI_STATE_AUTHENTICATION_FAILED - Authentication process as a whole could not be successfully completed +* `13`: WIFI_STATE_ERROR - The device has encountered an unrecoverable error with the Wifi adapter. ### Parameters -| Name | Type | Description | -| :-------- | :-------- | :-------- | -| params | object | | -| params.logLevel | integer | Set Log level to get more information | +This method takes no parameters. ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | +| result.state | integer | The given State | +| result.status | string | WiFi status | | result.success | boolean | Whether the request succeeded | ### Example @@ -1765,12 +1708,9 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.SetLogLevel", - "params": { - "logLevel": 1 - } + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.GetWifiState" } ``` @@ -1778,47 +1718,35 @@ No Events ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "state": 4, + "status": "WIFI_STATE_CONNECTED", + "success": true + } } ``` - -## *GetWifiState [method](#head.Methods)* + +## *SetHostname [method](#head.Methods)* -Returns the current Wifi State. The possible Wifi states are as follows. -* `0`: UNINSTALLED - The device was in an installed state and was uninstalled; or, the device does not have a Wifi radio installed -* `1`: DISABLED - The device is installed but not yet enabled -* `2`: DISCONNECTED - The device is installed and enabled, but not yet connected to a network -* `3`: PAIRING - The device is in the process of pairing, but not yet connected to a network -* `4`: CONNECTING - The device is attempting to connect to a network -* `5`: CONNECTED - The device is successfully connected to a network -* `6`: SSID_NOT_FOUND - The requested SSID to connect is not found -* `7`: SSID_CHANGED - The device connected SSID is changed -* `8`: CONNECTION_LOST - The device network connection is lost -* `9`: CONNECTION_FAILED - The device network connection got failed -* `10`: CONNECTION_INTERRUPTED - The device connection is interrupted -* `11`: INVALID_CREDENTIALS - The credentials provided to connect is not valid -* `12`: AUTHENTICATION_FAILED - Authentication process as a whole could not be successfully completed -* `13`: ERROR - The device has encountered an unrecoverable error with the Wifi adapter - -### Events - -No Events +To configure a custom DHCP hostname instead of the default (which is typically the default hostname). + +Setting host name will take effect upon reconnect; like, device reboot, wake-up from deepsleep, while connecting to new Wi-Fi connection, WiFi On/Off, or renewal of the DHCP lease. ### Parameters -This method takes no parameters. +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.hostname | string | The hostname to be set for the device (maximum 32 characters) | ### Result | Name | Type | Description | | :-------- | :-------- | :-------- | | result | object | | -| result.state | integer | The Wifi operational state | | result.success | boolean | Whether the request succeeded | ### Example @@ -1827,9 +1755,12 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "method": "org.rdk.NetworkManager.GetWifiState" + "jsonrpc": "2.0", + "id": 42, + "method": "org.rdk.NetworkManager.1.SetHostname", + "params": { + "hostname": "RDK-Device" + } } ``` @@ -1837,34 +1768,33 @@ This method takes no parameters. ```json { - "jsonrpc": "2.0", - "id": 42, - "result": { - "state": 2, - "success": true - } + "jsonrpc": "2.0", + "id": 42, + "result": { + "success": true + } } ``` # Notifications -Notifications are autonomous events, triggered by the internals of the implementation, and broadcasted via JSON-RPC to all registered observers. Refer to [[Thunder](#ref.Thunder)] for information on how to register for a notification. +Notifications are autonomous events triggered by the internals of the implementation and broadcasted via JSON-RPC to all registered observers. Refer to [[Thunder](#ref.Thunder)] for information on how to register for a notification. -The following events are provided by the org.rdk.NetworkManager plugin: +The following events are provided by the org.rdk.NetworkManager interface: NetworkManager interface events: | Event | Description | | :-------- | :-------- | | [onInterfaceStateChange](#event.onInterfaceStateChange) | Triggered when an interface state is changed | -| [onAddressChange](#event.onAddressChange) | Triggered when an IP Address is assigned or lost | -| [onActiveInterfaceChange](#event.onActiveInterfaceChange) | Triggered when the primary/active interface changes, regardless if it's from a system operation or through the `SetPrimaryInterface` method | +| [onIPAddressChange](#event.onIPAddressChange) | Triggered when an IP Address is assigned or lost | +| [onRouteChange](#event.onRouteChange) | Triggered when the default route changes and a new gateway/DNS becomes available for an interface | +| [onActiveInterfaceChange](#event.onActiveInterfaceChange) | Triggered when the primary/active interface changes | | [onInternetStatusChange](#event.onInternetStatusChange) | Triggered when internet connection state changed | | [onAvailableSSIDs](#event.onAvailableSSIDs) | Triggered when scan completes or when scan cancelled | | [onWiFiStateChange](#event.onWiFiStateChange) | Triggered when WIFI connection state get changed | -| [onWiFiSignalStrengthChange](#event.onWiFiSignalStrengthChange) | Triggered when WIFI connection Signal Strength get changed | - +| [onWiFiSignalQualityChange](#event.onWiFiSignalQualityChange) | Triggered when WIFI Signal quality changed which is decided based on SNR value which is defined in `GetWiFiSignalQuality` | ## *onInterfaceStateChange [event](#head.Notifications)* @@ -1884,23 +1814,25 @@ Triggered when an interface state is changed. The possible states are | :-------- | :-------- | :-------- | | params | object | | | params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | -| params.state | string | Current state of the interface | +| params.state | integer | Current state of the interface | +| params.status | string | Current status of the interface | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onInterfaceStateChange", - "params": { - "interface": "wlan0", - "state": "INTERFACE_ADDED" - } + "jsonrpc": "2.0", + "method": "client.events.1.onInterfaceStateChange", + "params": { + "interface": "wlan0", + "state": 1, + "status": "INTERFACE_LINK_UP" + } } ``` - -## *onAddressChange [event](#head.Notifications)* + +## *onIPAddressChange [event](#head.Notifications)* Triggered when an IP Address is assigned or lost. @@ -1910,48 +1842,80 @@ Triggered when an IP Address is assigned or lost. | :-------- | :-------- | :-------- | | params | object | | | params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | -| params.isIPv6 | boolean | It will be true if the IP address is IPv6 | -| params.ipAddress | string | The IPv6 or IPv4 address for the interface | -| params.status | string | Whether IP address was acquired or lost (must be one of the following: 'ACQUIRED', 'LOST') (must be one of the following: *`ACQUIRED`*, *`LOST`*) | +| params.ipaddress | string | The IP address | +| params.ipversion | string | Either IPv4 or IPv6 | +| params.status | string | Whether IP address was acquired or lost (must be one of the following: 'ACQUIRED', 'LOST') | + +### Example + +```json +{ + "jsonrpc": "2.0", + "method": "client.events.1.onIPAddressChange", + "params": { + "interface": "wlan0", + "ipaddress": "192.168.1.101", + "ipversion": "IPv4", + "status": "ACQUIRED" + } +} +``` + + +## *onRouteChange [event](#head.Notifications)* + +Triggered when the default route changes and a new gateway/DNS becomes available for an interface. + +### Parameters + +| Name | Type | Description | +| :-------- | :-------- | :-------- | +| params | object | | +| params.interface | string | An interface, such as `eth0` or `wlan0`, depending upon availability of the given interface | +| params.ipversion | string | Either IPv4 or IPv6 | +| params.ipaddress | string | The IP address | +| params.gateway | string | The gateway address | +| params.primarydns | string | The primary DNS address | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onAddressChange", - "params": { - "interface": "wlan0", - "isIPv6": false, - "ipAddress": "192.168.1.100", - "status": "ACQUIRED" - } + "jsonrpc": "2.0", + "method": "client.events.1.onRouteChange", + "params": { + "interface": "wlan0", + "ipversion": "IPv4", + "ipaddress": "192.168.1.101", + "gateway": "192.168.1.1", + "primarydns": "192.168.1.1" + } } ``` ## *onActiveInterfaceChange [event](#head.Notifications)* -Triggered when the primary/active interface changes, regardless if it's from a system operation or through the `SetPrimaryInterface` method. +Triggered when the primary/active interface changes. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.oldInterfaceName | string | The previous interface that was changed | -| params.newInterfaceName | string | The current interface | +| params.prevActiveInterface | string | The previous interface that was changed | +| params.activeInterface | string | The current interface | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onActiveInterfaceChange", - "params": { - "oldInterfaceName": "wlan0", - "newInterfaceName": "eth0" - } + "jsonrpc": "2.0", + "method": "client.events.1.onActiveInterfaceChange", + "params": { + "prevActiveInterface": "wlan0", + "activeInterface": "eth0" + } } ``` @@ -1965,23 +1929,27 @@ Triggered when internet connection state changed.The possible internet connectio | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.PrevState | integer | The privious internet connection state | -| params.PrevStatus | string | The previous internet connection status | +| params.prevState | integer | The previous internet connection state | +| params.prevStatus | string | The previous internet connection status | | params.state | integer | The internet connection state | | params.status | string | The internet connection status | +| params.interface | string | The internet status change on default interface | +| params?.reason | string | *(optional)* The ConnectivityCheckMgr reason when status is NO_INTERNET | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onInternetStatusChange", - "params": { - "PrevState": 1, - "PrevStatus": "NO_INTERNET", - "state": 4, - "status": "FULLY_CONNECTED" - } + "jsonrpc": "2.0", + "method": "client.events.1.onInternetStatusChange", + "params": { + "prevState": 3, + "prevStatus": "FULLY_CONNECTED", + "state": 1, + "status": "NO_INTERNET", + "interface": "wlan0", + "reason": "PROBE_FAILED" + } } ``` @@ -1997,94 +1965,89 @@ Triggered when scan completes or when scan cancelled. | params | object | | | params.ssids | array | On Available SSID's | | params.ssids[#] | object | | -| params.ssids[#].ssid | string | ssid | -| params.ssids[#].security | integer | security | -| params.ssids[#].signalStrength | string | signalStrength | -| params.ssids[#].frequency | string | frequency | +| params.ssids[#].ssid | string | Discovered SSID | +| params.ssids[#].bssid | string | Discovered BSSID | +| params.ssids[#].security | integer | The security mode. See `GetSupportedSecurityModes` | +| params.ssids[#].strength | integer | The WiFi Signal RSSI value in dBm | +| params.ssids[#].frequency | number | The supported frequency for this SSID in GHz | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onAvailableSSIDs", - "params": { - "ssids": [ - { - "ssid": "myAP-2.4", - "security": 6, - "signalStrength": "-27.000000", - "frequency": "2.442000" - } - ] - } + "jsonrpc": "2.0", + "method": "client.events.1.onAvailableSSIDs", + "params": { + "ssids": [ + { + "ssid": "myAP-2.4", + "bssid": "00:11:22:33:44:55", + "security": 2, + "strength": -32, + "frequency": 2.442 + } + ] + } } ``` ## *onWiFiStateChange [event](#head.Notifications)* -Triggered when WIFI connection state get changed. The possible states are, - * '0' - 'WIFI_STATE_UNINSTALLED' - * '1' - 'WIFI_STATE_DISABLED' - * '2' - 'WIFI_STATE_DISCONNECTED' - * '3' - 'WIFI_STATE_PAIRING' - * '4' - 'WIFI_STATE_CONNECTING' - * '5' - 'WIFI_STATE_CONNECTED' - * '6' - 'WIFI_STATE_SSID_NOT_FOUND' - * '7' - 'WIFI_STATE_SSID_CHANGED' - * '8' - 'WIFI_STATE_CONNECTION_LOST' - * '9' - 'WIFI_STATE_CONNECTION_FAILED' - * '10'- 'WIFI_STATE_CONNECTION_INTERRUPTED' - * '11' - 'WIFI_STATE_INVALID_CREDENTIALS' - * '12' - 'WIFI_STATE_AUTHENTICATION_FAILED' - * '13' - 'WIFI_STATE_ERROR' -. +Triggered when WIFI connection state get changed. The possible states are defined in `GetWifiState()`. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.state | integer | onWiFiStateChange | +| params.state | integer | WiFi State | +| params.status | string | WiFi status | +| params.ssid | string | The SSID associated with the Wi-Fi profile causing the state transition. Disconnected state, contains the SSID associated with the connection that was disconnected. The SSID will be empty when WPS initiated and no AP found with WPS enabled | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onWiFiStateChange", - "params": { - "state": 5 - } + "jsonrpc": "2.0", + "method": "client.events.1.onWiFiStateChange", + "params": { + "state": 5, + "status": "WIFI_STATE_CONNECTED", + "ssid": "myHomeSSID" + } } ``` - -## *onWiFiSignalStrengthChange [event](#head.Notifications)* + +## *onWiFiSignalQualityChange [event](#head.Notifications)* -Triggered when WIFI connection Signal Strength get changed. +Triggered when WIFI Signal quality changed which is decided based on SNR value which is defined in `GetWiFiSignalQuality`. ### Parameters | Name | Type | Description | | :-------- | :-------- | :-------- | | params | object | | -| params.ssid | string | Signal Strength changed SSID | -| params.signalLevel | string | Signal Strength | -| params.signalQuality | string | Signal quality | +| params.ssid | string | The WiFi SSID Name | +| params.quality | string | WiFi Quality based on Signal to Noise Ratio (SNR) | +| params.snr | integer | Signal to Noise Ratio(SNR) in dBm | +| params.strength | integer | The WiFi Signal RSSI value in dBm | +| params.noise | integer | The WiFi Signal Noise detected in dBm | ### Example ```json { - "jsonrpc": "2.0", - "method": "client.events.onWiFiSignalStrengthChange", - "params": { - "ssid": "home-new_123", - "signalLevel": "-27.000000", - "signalQuality": "Excellent" - } + "jsonrpc": "2.0", + "method": "client.events.1.onWiFiSignalQualityChange", + "params": { + "ssid": "myHomeSSID", + "quality": "Excellent", + "snr": 74, + "strength": -32, + "noise": -96 + } } ``` diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt new file mode 100644 index 000000000..5a1c27d86 --- /dev/null +++ b/interface/CMakeLists.txt @@ -0,0 +1,59 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Generate ProxyStub for INetworkManager.h") + +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) +find_package(CompileSettingsDebug CONFIG REQUIRED) +find_package(ProxyStubGenerator REQUIRED) + +set(PROXYLIB ${NAMESPACE}${PROJECT_NAME}Proxy) +set(ProxyStubGenerator_DIR ${CMAKE_SYSROOT}${CMAKE_INSTALL_PREFIX}/tools/cmake ${ProxyStubGenerator_DIR}) + +if(NOT GENERATOR_SEARCH_PATH) + set(GENERATOR_SEARCH_PATH ${CMAKE_SYSROOT}${CMAKE_INSTALL_PREFIX}/include/${NAMESPACE}) +endif() + +file(GLOB INTERFACE_HEADER ${CMAKE_CURRENT_SOURCE_DIR}) + +ProxyStubGenerator(INPUT "${INTERFACE_HEADER}" OUTDIR "${CMAKE_CURRENT_BINARY_DIR}/generated" INCLUDE_PATH ${GENERATOR_SEARCH_PATH}) + +file(GLOB PROXY_STUB_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/generated/ProxyStubs*.cpp") + +add_library(${PROXYLIB} SHARED ${PROXY_STUB_SOURCES} + ../plugin/Module.cpp) + +target_include_directories(${PROXYLIB} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../plugin/) + +target_link_libraries(${PROXYLIB} PRIVATE + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}COM::${NAMESPACE}COM + CompileSettingsDebug::CompileSettingsDebug) + +set_target_properties(${PROXYLIB} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE) + +string(TOLOWER ${NAMESPACE} NAMESPACE_LIB) + +install(TARGETS ${PROXYLIB} EXPORT ${PROXYLIB}Targets + LIBRARY DESTINATION lib/${NAMESPACE_LIB}/proxystubs COMPONENT libs) + +install(FILES INetworkManager.h DESTINATION include/${NAMESPACE}/interfaces) diff --git a/interface/INetworkManager.h b/interface/INetworkManager.h new file mode 100644 index 000000000..d551f9dec --- /dev/null +++ b/interface/INetworkManager.h @@ -0,0 +1,306 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include "Module.h" + +// @stubgen:include + +namespace WPEFramework +{ + namespace Exchange + { + enum myIDs { + ID_NETWORKMANAGER = 0x800004E0, + ID_NETWORKMANAGER_NOTIFICATION = ID_NETWORKMANAGER + 1, + ID_NETWORKMANAGER_INTERFACE_DETAILS_ITERATOR = ID_NETWORKMANAGER + 2, + ID_NETWORKMANAGER_WIFI_SECURITY_MODE_ITERATOR = ID_NETWORKMANAGER + 3 + }; + + /* @json @text:keep */ + struct EXTERNAL INetworkManager: virtual public Core::IUnknown + { + // All interfaces require a unique ID, defined in Ids.h + enum { ID = ID_NETWORKMANAGER }; + + // Define the RPC methods + enum InterfaceType : uint8_t { + INTERFACE_TYPE_ETHERNET /* @text: ETHERNET */, + INTERFACE_TYPE_WIFI /* @text: WIFI */, + INTERFACE_TYPE_P2P /* @text: P2P */, + }; + + struct EXTERNAL InterfaceDetails { + InterfaceType type; + string name; + string mac; + bool enabled; + bool connected; + }; + + enum IPVersion : uint8_t + { + IP_ADDRESS_V4 /* @text: IPv4 */, + IP_ADDRESS_V6 /* @text: IPv6 */, + }; + + enum IPStatus : uint8_t + { + IP_LOST /* @text: LOST */, + IP_ACQUIRED /* @text: ACQUIRED */, + }; + + struct EXTERNAL IPAddress { + string ipversion /* @text: ipversion */; + bool autoconfig /* @text: autoconfig */; + string dhcpserver /* @text: dhcpserver */; + string ula /* @text: ula */; + string ipaddress /* @text: ipaddress */; + uint32_t prefix /* @text: prefix */; + string gateway /* @text: gateway */; + string primarydns /* @text: primarydns */; + string secondarydns /* @text: secondarydns */; + }; + + // Define the RPC methods + enum InternetStatus : uint8_t + { + INTERNET_NOT_AVAILABLE /* @text: NO_INTERNET */, + INTERNET_LIMITED /* @text: LIMITED_INTERNET */, + INTERNET_CAPTIVE_PORTAL /* @text: CAPTIVE_PORTAL */, + INTERNET_FULLY_CONNECTED /* @text: FULLY_CONNECTED */, + INTERNET_UNKNOWN /* @text: UNKNOWN */, + }; + + enum WiFiWPS : uint8_t + { + WIFI_WPS_PBC /* @text: PBC */, + WIFI_WPS_PIN /* @text: PIN */, + WIFI_WPS_SERIALIZED_PIN /* @text: SERIALIZED_PIN */, + }; + + enum WIFISecurityMode : uint8_t + { + WIFI_SECURITY_NONE /* @text: NONE */, + WIFI_SECURITY_WPA_PSK /* @text: WPA-PSK */, + WIFI_SECURITY_SAE /* @text: SAE */, + WIFI_SECURITY_EAP /* @text: EAP */, + }; + + enum WIFIFrequency : uint8_t + { + WIFI_FREQUENCY_ALL /* @text: ALL */, + WIFI_FREQUENCY_2_4_GHZ /* @text: 2.4GHz */, + WIFI_FREQUENCY_5_GHZ /* @text: 5GHz */, + WIFI_FREQUENCY_6_GHZ /* @text: 6GHz */, + }; + + struct EXTERNAL WiFiScanResults { + string ssid; + WIFISecurityMode security; + string strength; + string frequency; + }; + + struct EXTERNAL WiFiConnectTo { + string ssid; + string passphrase; + string bssid; + WIFIFrequency frequency; + WIFISecurityMode security; + string ca_cert; + string client_cert; + string private_key; + string private_key_passwd; + string eap; + string eap_identity; + string eap_password; + string eap_phase1; + string eap_phase2; + bool persist; + }; + + struct EXTERNAL WiFiSSIDInfo { + string ssid; + string bssid; + WIFISecurityMode security; + int32_t strength; + double frequency; + int32_t rate; + int32_t noise; + }; + + struct EXTERNAL WIFISecurityModeInfo { + WIFISecurityMode security; + string securityName; + }; + + enum WiFiSignalQuality : uint8_t + { + WIFI_SIGNAL_DISCONNECTED /* @text: Disconnected */, + WIFI_SIGNAL_WEAK /* @text: Weak */, + WIFI_SIGNAL_FAIR /* @text: Fair */, + WIFI_SIGNAL_GOOD /* @text: Good */, + WIFI_SIGNAL_EXCELLENT /* @text: Excellent */ + }; + + enum Logging : uint8_t + { + LOG_LEVEL_FATAL, + LOG_LEVEL_ERROR, + LOG_LEVEL_WARNING, + LOG_LEVEL_INFO, + LOG_LEVEL_DEBUG + }; + + // The state of the interface + enum InterfaceState : uint8_t + { + INTERFACE_ADDED, + INTERFACE_LINK_UP, + INTERFACE_LINK_DOWN, + INTERFACE_ACQUIRING_IP, + INTERFACE_REMOVED, + INTERFACE_DISABLED + }; + + enum WiFiState : uint8_t + { + WIFI_STATE_UNINSTALLED, + WIFI_STATE_DISABLED, + WIFI_STATE_DISCONNECTED, + WIFI_STATE_PAIRING, + WIFI_STATE_CONNECTING, + WIFI_STATE_CONNECTED, + WIFI_STATE_SSID_NOT_FOUND, + WIFI_STATE_SSID_CHANGED, + WIFI_STATE_CONNECTION_LOST, + WIFI_STATE_CONNECTION_FAILED, + WIFI_STATE_CONNECTION_INTERRUPTED, + WIFI_STATE_INVALID_CREDENTIALS, + WIFI_STATE_AUTHENTICATION_FAILED, + WIFI_STATE_ERROR, + WIFI_STATE_INVALID + }; + + using IInterfaceDetailsIterator = RPC::IIteratorType; + using ISecurityModeIterator = RPC::IIteratorType; + using IStringIterator = RPC::IIteratorType; + + /* @brief Get all the Available Interfaces */ + virtual uint32_t GetAvailableInterfaces (IInterfaceDetailsIterator*& interfaces/* @out */) = 0; + + /* @brief Get the Primary Interface used for external world communication */ + virtual uint32_t GetPrimaryInterface (string& interface /* @out */) = 0; + + /* @brief Enable/Disable the given interface */ + virtual uint32_t SetInterfaceState (const string& interface /* @in */, const bool enabled /* @in */) = 0; + /* @brief Get the state of given interface */ + virtual uint32_t GetInterfaceState (const string& interface /* @in */, bool& enabled /* @out */) = 0; + + /* @brief Get IP Address Of the Interface */ + virtual uint32_t GetIPSettings(string& interface /* @inout */, const string& ipversion /* @in */, IPAddress& address /* @out */) = 0; + /* @brief Set IP Address Of the Interface */ + virtual uint32_t SetIPSettings(const string& interface /* @in */, const IPAddress& address /* @in */) = 0; + + /* @brief Get STUN Endpoint to be used for identifying Public IP */ + virtual uint32_t GetStunEndpoint (string &endpoint /* @out */, uint32_t &port /* @out */, uint32_t &timeout /* @out */, uint32_t &cacheLifetime/* @out */) const = 0; + /* @brief Set STUN Endpoint to be used to identify Public IP */ + virtual uint32_t SetStunEndpoint (string const endpoint /* @in */, const uint32_t port /* @in */, const uint32_t timeout /* @in */, const uint32_t cacheLifetime /* @in */) = 0; + + /* @brief Get ConnectivityTest Endpoints */ + virtual uint32_t GetConnectivityTestEndpoints(IStringIterator*& endpoints/* @out */) const = 0; + /* @brief Set ConnectivityTest Endpoints */ + virtual uint32_t SetConnectivityTestEndpoints(IStringIterator* const endpoints /* @in */) = 0; + + /* @brief Get Internet Connectivity Status */ + virtual uint32_t IsConnectedToInternet(string &ipversion /* @inout */, string &interface /* @inout */, InternetStatus& status /* @out */, string& reason /* @out */ = EmptyReason()) = 0; + + /* @brief Get Authentication URL if the device is behind Captive Portal */ + virtual uint32_t GetCaptivePortalURI(string &uri/* @out */) const = 0; + + /* @brief Get the Public IP used for external world communication */ + virtual uint32_t GetPublicIP (string& interface /* @inout */, string &ipversion /* @inout */, string& ipaddress /* @out */) = 0; + + /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ + virtual uint32_t Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t count /* @in */, const uint16_t timeout /* @in */, const string guid /* @in */, string& response /* @out */) = 0; + + /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ + virtual uint32_t Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t nqueries /* @in */, const string guid /* @in */, string& response /* @out */) = 0; + + /* @brief Set the dhcp hostname */ + virtual uint32_t SetHostname(const string& hostname /* @in */) = 0; + + // WiFi Specific Methods + /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ + virtual uint32_t StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) = 0; + virtual uint32_t StopWiFiScan(void) = 0; + + virtual uint32_t GetKnownSSIDs(IStringIterator*& ssids /* @out */) = 0; + virtual uint32_t AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) = 0; + virtual uint32_t RemoveKnownSSID(const string& ssid /* @in */) = 0; + virtual uint32_t ConnectToKnownSSID(const string& ssid /* @in */) = 0; + + virtual uint32_t WiFiConnect(const WiFiConnectTo& ssid /* @in */) = 0; + virtual uint32_t WiFiDisconnect(void) = 0; + virtual uint32_t GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) = 0; + + virtual uint32_t StartWPS(const WiFiWPS& method /* @in */, const string& pin /* @in */) = 0; + virtual uint32_t StopWPS(void) = 0; + virtual uint32_t GetWifiState(WiFiState &state /* @out */) = 0; + virtual uint32_t GetWiFiSignalQuality(string& ssid /* @out */, int& strength /* @out */, int& noise /* @out */, int& snr /* @out */, WiFiSignalQuality& quality /* @out */) = 0; + virtual uint32_t GetSupportedSecurityModes(ISecurityModeIterator*& modes/* @out */) const = 0; + + /* @brief Set the network manager plugin log level */ + virtual uint32_t SetLogLevel(const Logging& level /* @in */) = 0; + virtual uint32_t GetLogLevel(Logging& level /* @out */) = 0; + + /* @brief Configure the Network Manager plugin */ + virtual uint32_t Configure(const string configLine/* @in */) = 0; + + /* @event */ + struct EXTERNAL INotification : virtual public Core::IUnknown + { + enum { ID = ID_NETWORKMANAGER_NOTIFICATION }; + + // Network Notifications that other processes can subscribe to + virtual void onInterfaceStateChange(const InterfaceState state /* @in */, const string interface /* @in */){}; + virtual void onActiveInterfaceChange(const string prevActiveInterface /* @in */, const string currentActiveInterface /* @in */){}; + virtual void onIPAddressChange(const string interface /* @in */, const string ipversion /* @in */, const string ipaddress /* @in */, const IPStatus status /* @in */){}; + virtual void onInternetStatusChange(const InternetStatus prevState /* @in */, const InternetStatus currState /* @in */, const string interface /* @in */, const string reason /* @in */){}; + + // WiFi Notifications that other processes can subscribe to + virtual void onAvailableSSIDs(const string jsonOfScanResults /* @in */){}; + virtual void onWiFiStateChange(const WiFiState state /* @in */, const string ssid /* @in */){}; + virtual void onWiFiSignalQualityChange(const string ssid /* @in */, const int strength /* @in */, const int noise /* @in */, const int snr /* @in */, const WiFiSignalQuality quality /* @in */){}; + virtual void onRouteChange(const string interface /* @in */, const string ipversion /* @in */, const string ipaddress /* @in */, const string gateway /* @in */, const string primarydns /* @in */){}; + }; + + // Allow other processes to register/unregister from our notifications + virtual uint32_t Register(INetworkManager::INotification* notification) = 0; + virtual uint32_t Unregister(INetworkManager::INotification* notification) = 0; + private: + static string& EmptyReason() + { + static thread_local string reason; + return reason; + } + }; + } +} diff --git a/legacy/CMakeLists.txt b/legacy/CMakeLists.txt new file mode 100644 index 000000000..79a45990f --- /dev/null +++ b/legacy/CMakeLists.txt @@ -0,0 +1,83 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Build Legacy Plugins") + +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) + +set(PLUGIN_LEGACY_NETWORK ${NAMESPACE}Network) +set(PLUGIN_LEGACY_WIFI ${NAMESPACE}WiFiManager) + +set(PLUGIN_LEGACY_NW_STARTUPORDER "35" CACHE STRING "To configure startup order of Legacy Network plugin") +set(PLUGIN_LEGACY_WIFI_STARTUPORDER "35" CACHE STRING "To configure startup order of Legacy WiFi plugin") + +set(PLUGIN_LEGACY_NW_AUTOSTART "false" CACHE STRING "The autostart of Legacy Network plugin") +set(PLUGIN_LEGACY_WIFI_AUTOSTART "false" CACHE STRING "The autostart of Legacy WiFi plugin") + +set(PLUGIN_BUILD_REFERENCE ${PROJECT_VERSION} CACHE STRING "To Set the Hash for the plugin") +add_definitions(-DPLUGIN_BUILD_REFERENCE=${PLUGIN_BUILD_REFERENCE}) + +if (USE_RDK_LOGGER) + find_package(rdklogger REQUIRED) + add_definitions(-DUSE_RDK_LOGGER) + + include_directories(${RDKLOGGER_INCLUDE_DIRS}) +endif (USE_RDK_LOGGER) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin) + +add_library(${PLUGIN_LEGACY_NETWORK} SHARED + LegacyNetworkAPIs.cpp + ../plugin/NetworkManagerLogger.cpp + ../plugin/Module.cpp +) + +target_link_libraries(${PLUGIN_LEGACY_NETWORK} PRIVATE ${NAMESPACE}Core::${NAMESPACE}Core ${NAMESPACE}Plugins::${NAMESPACE}Plugins) + +if (USE_RDK_LOGGER) + target_link_libraries(${PLUGIN_LEGACY_NETWORK} PRIVATE ${RDKLOGGER_LIBRARIES}) +endif (USE_RDK_LOGGER) + +set_target_properties(${PLUGIN_LEGACY_NETWORK} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES) + +install(TARGETS ${PLUGIN_LEGACY_NETWORK} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) + +add_library(${PLUGIN_LEGACY_WIFI} SHARED + LegacyWiFiManagerAPIs.cpp + ../plugin/NetworkManagerLogger.cpp + ../plugin/Module.cpp +) + +target_link_libraries(${PLUGIN_LEGACY_WIFI} PRIVATE ${NAMESPACE}Core::${NAMESPACE}Core ${NAMESPACE}Plugins::${NAMESPACE}Plugins) + +if (USE_RDK_LOGGER) + target_link_libraries(${PLUGIN_LEGACY_WIFI} PRIVATE ${RDKLOGGER_LIBRARIES}) +endif (USE_RDK_LOGGER) + +set_target_properties(${PLUGIN_LEGACY_WIFI} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES) + +install(TARGETS ${PLUGIN_LEGACY_WIFI} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) + +write_config(PLUGINS LegacyNetworkAPIs CLASSNAME Network LOCATOR lib${PLUGIN_LEGACY_NETWORK}.so) +write_config(PLUGINS LegacyWiFiManagerAPIs CLASSNAME WiFiManager LOCATOR lib${PLUGIN_LEGACY_WIFI}.so) diff --git a/legacy/LegacyNetworkAPIs.conf.in b/legacy/LegacyNetworkAPIs.conf.in new file mode 100644 index 000000000..70bef4cd9 --- /dev/null +++ b/legacy/LegacyNetworkAPIs.conf.in @@ -0,0 +1,4 @@ +precondition = ["Platform"] +callsign = "org.rdk.Network" +autostart = "@PLUGIN_LEGACY_NW_AUTOSTART@" +startuporder = "@PLUGIN_LEGACY_NW_STARTUPORDER@" diff --git a/legacy/LegacyNetworkAPIs.cpp b/legacy/LegacyNetworkAPIs.cpp new file mode 100644 index 000000000..172e43f3a --- /dev/null +++ b/legacy/LegacyNetworkAPIs.cpp @@ -0,0 +1,1077 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include "LegacyNetworkAPIs.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerJsonEnum.h" + +using namespace std; +using namespace WPEFramework::Plugin; +#define API_VERSION_NUMBER_MAJOR 2 +#define API_VERSION_NUMBER_MINOR 0 +#define API_VERSION_NUMBER_PATCH 0 +#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager" +#define SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS 500 +#define DEFAULT_PING_PACKETS 15 + +#define LOG_INPARAM() { string json; parameters.ToString(json); NMLOG_INFO("params=%s", json.c_str() ); } +#define LOG_OUTPARAM() { string json; response.ToString(json); NMLOG_INFO("response=%s", json.c_str() ); } +#define DEBUGLOG_INPARAM() { string json; parameters.ToString(json); NMLOG_DEBUG("params=%s", json.c_str() ); } +#define DEBUGLOG_OUTPARAM() { string json; response.ToString(json); NMLOG_DEBUG("response=%s", json.c_str() ); } + +#define returnJson(rc) \ + { \ + if (Core::ERROR_NONE == rc) \ + response["success"] = true; \ + else \ + response["success"] = false; \ + LOG_OUTPARAM(); \ + return Core::ERROR_NONE; \ + } + +namespace WPEFramework +{ + class Job : public Core::IDispatch { + public: + Job(function work) + : _work(work) + { + } + void Dispatch() override + { + _work(); + } + + private: + function _work; + }; + + static Plugin::Metadata metadata( + // Version (Major, Minor, Patch) + API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH, + // Preconditions + {}, + // Terminations + {}, + // Controls + {} + ); + + Network* _gNWInstance = nullptr; + namespace Plugin + { + SERVICE_REGISTRATION(Network, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); + Network::Network() + : PluginHost::JSONRPC() + , m_service(nullptr) + , m_subsIfaceStateChange(false) + , m_subsActIfaceChange(false) + , m_subsIPAddrChange(false) + , m_subsInternetChange(false) + { + _gNWInstance = this; + m_timer.connect(std::bind(&Network::subscribeToEvents, this)); + registerLegacyMethods(); + } + + Network::~Network() + { + _gNWInstance = nullptr; + } + + const string Network::Initialize(PluginHost::IShell* service ) + { + NetworkManagerLogger::Init(); + m_service = service; + m_service->AddRef(); + string message{}; + + string callsign(NETWORK_MANAGER_CALLSIGN); + + string token = ""; + + // TODO: use interfaces and remove token + auto security = m_service->QueryInterfaceByCallsign("SecurityAgent"); + if (security != nullptr) { + string payload = "http://localhost"; + if (security->CreateToken(static_cast(payload.length()), reinterpret_cast(payload.c_str()), token) == Core::ERROR_NONE) { + NMLOG_DEBUG("Network plugin got security token"); + } else { + NMLOG_WARNING("Network plugin failed to get security token"); + } + security->Release(); + } else { + NMLOG_DEBUG("Network plugin: No security agent"); + } + + string query = "token=" + token; + + auto interface = m_service->QueryInterfaceByCallsign(callsign); + if (interface != nullptr) + { + int retry = 0; + PluginHost::IShell::state state = PluginHost::IShell::state::UNAVAILABLE; + do{ + state = interface->State(); + if(PluginHost::IShell::state::ACTIVATED == state) + { + NMLOG_INFO("Dependency Plugin '%s' Ready", callsign.c_str()); + break; + } + usleep(500*1000); + } while(retry++ < 5); + if(PluginHost::IShell::state::ACTIVATED == state) + { + Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); + m_networkmanager = make_shared >(_T(NETWORK_MANAGER_CALLSIGN), _T("org.rdk.Network"), query); + + subscribeToEvents(); + } + else + message = _T("dependency Plugin 'NetworkManager' not Ready"); + interface->Release(); + } + else + message = _T("Failed to get IShell for 'NetworkManager'"); + return message; + } + + void Network::Deinitialize(PluginHost::IShell* /* service */) + { + m_timer.stop(); + unregisterLegacyMethods(); + + if (m_networkmanager) + m_networkmanager.reset(); + + m_networkmanager = NULL; + m_service->Release(); + m_service = nullptr; + _gNWInstance = nullptr; + } + + string Network::Information() const + { + return(string()); + } + + /** + * Hook up all our JSON RPC methods + * + * Each method definition comprises of: + * * Input parameters + * * Output parameters + * * Method name + * * Function that implements that method + */ + void Network::registerLegacyMethods(void) + { + CreateHandler({2}); + + Register("getStbIp", &Network::getStbIp, this); + Register("getSTBIPFamily", &Network::getSTBIPFamily, this); + Register("getInterfaces", &Network::getInterfaces, this); + Register("isInterfaceEnabled", &Network::isInterfaceEnabled, this); + Register("getPublicIP", &Network::getPublicIP, this); + Register("setInterfaceEnabled", &Network::setInterfaceEnabled, this); + Register("getDefaultInterface", &Network::getDefaultInterface, this); + Register("setDefaultInterface", &Network::setDefaultInterface, this); + Register("setIPSettings", &Network::setIPSettings, this); + Register("getIPSettings", &Network::getIPSettings, this); + Register("getInternetConnectionState", &Network::getInternetConnectionState, this); + Register("ping", &Network::doPing, this); + Register("isConnectedToInternet", &Network::isConnectedToInternet, this); + Register("setStunEndPoint", &Network::setStunEndpoint, this); + Register("trace", &Network::doTrace, this); + Register("setConnectivityTestEndpoints", &Network::setConnectivityTestEndpoints, this); + Register("startConnectivityMonitoring", &Network::startConnectivityMonitoring, this); + Register("getCaptivePortalURI", &Network::getCaptivePortalURI, this); + Register("stopConnectivityMonitoring", &Network::stopConnectivityMonitoring, this); + GetHandler(2)->Register("setIPSettings", &Network::setIPSettings, this); + GetHandler(2)->Register("getIPSettings", &Network::getIPSettings2, this); + } + + /** + * Unregister all our JSON-RPC methods + */ + void Network::unregisterLegacyMethods(void) + { + Unregister("getStbIp"); + Unregister("getSTBIPFamily"); + Unregister("getInterfaces"); + Unregister("isInterfaceEnabled"); + Unregister("getPublicIP"); + Unregister("setInterfaceEnabled"); + Unregister("getDefaultInterface"); + Unregister("setDefaultInterface"); + Unregister("setIPSettings"); + Unregister("getIPSettings"); + Unregister("getInternetConnectionState"); + Unregister("ping"); + Unregister("isConnectedToInternet"); + Unregister("setStunEndPoint"); + Unregister("trace"); + Unregister("setConnectivityTestEndpoints"); + Unregister("startConnectivityMonitoring"); + Unregister("getCaptivePortalURI"); + Unregister("stopConnectivityMonitoring"); + } + +#define CIDR_NETMASK_IP_LEN 32 +const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN+1] = { + "0.0.0.0", + "128.0.0.0", + "192.0.0.0", + "224.0.0.0", + "240.0.0.0", + "248.0.0.0", + "252.0.0.0", + "254.0.0.0", + "255.0.0.0", + "255.128.0.0", + "255.192.0.0", + "255.224.0.0", + "255.240.0.0", + "255.248.0.0", + "255.252.0.0", + "255.254.0.0", + "255.255.0.0", + "255.255.128.0", + "255.255.192.0", + "255.255.224.0", + "255.255.240.0", + "255.255.248.0", + "255.255.252.0", + "255.255.254.0", + "255.255.255.0", + "255.255.255.128", + "255.255.255.192", + "255.255.255.224", + "255.255.255.240", + "255.255.255.248", + "255.255.255.252", + "255.255.255.254", + "255.255.255.255", + }; + + uint32_t Network::getInterfaces (const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + Exchange::INetworkManager::IInterfaceDetailsIterator* _interfaces{}; + + rc = _nwmgr->GetAvailableInterfaces(_interfaces); + if (Core::ERROR_NONE == rc) + { + if (_interfaces != nullptr) + { + JsonArray array; + Exchange::INetworkManager::InterfaceDetails entry{}; + while (_interfaces->Next(entry) == true) + { + JsonObject each; + Core::JSON::EnumType type{entry.type}; + each["interface"] = type.Data(); + each["macAddress"] = entry.mac; + each["enabled"] = entry.enabled; + each["connected"] = entry.connected; + + array.Add(JsonValue(each)); + } + + _interfaces->Release(); + response["interfaces"] = array; + } + } + _nwmgr->Release(); + } + + returnJson(rc); + } + + uint32_t Network::setStunEndpoint(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string endpoint = parameters["server"].String(); + uint32_t port = parameters["port"].Number(); + uint32_t bindTimeout = parameters["timeout"].Number(); + uint32_t cacheTimeout = parameters["cache_timeout"].Number(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->SetStunEndpoint(endpoint, port, bindTimeout, cacheTimeout); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t Network::setInterfaceEnabled (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + if (parameters.HasLabel("interface") && parameters.HasLabel("enabled")) + { + const string givenInterface = parameters["interface"].String(); + const bool enabled = parameters["enabled"].Boolean(); + const string interface = getInterfaceTypeToName(givenInterface); + + if ("wlan0" != interface && "eth0" != interface) + rc = Core::ERROR_BAD_REQUEST; + else + { + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->SetInterfaceState(interface, enabled); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + } + } + else + rc = Core::ERROR_BAD_REQUEST; + + returnJson(rc); + } + + uint32_t Network::getDefaultInterface (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string interface; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetPrimaryInterface(interface); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + string mappedInterface = getInterfaceNameToType(interface); + response["interface"] = mappedInterface; + } + returnJson(rc); + } + + uint32_t Network::setDefaultInterface(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_NONE; + + returnJson(rc); + } + + uint32_t Network::setIPSettings(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IPAddress address{}; + + string givenInterface = ""; + string interface = ""; + string ipversion = ""; + + if (parameters.HasLabel("interface")) + { + givenInterface = parameters["interface"].String(); + interface = getInterfaceTypeToName(givenInterface); + + if ("wlan0" != interface && "eth0" != interface) + { + return Core::ERROR_BAD_REQUEST; + } + } + else + { + return Core::ERROR_BAD_REQUEST; + } + + if (parameters.HasLabel("ipversion")) + { + address.ipversion = parameters["ipversion"].String(); + if (("IPv4" != address.ipversion) && ("IPv6" != address.ipversion)) + { + return Core::ERROR_BAD_REQUEST; + } + } + else + return Core::ERROR_BAD_REQUEST; + + address.autoconfig = parameters["autoconfig"].Boolean(); + if (!address.autoconfig) + { + address.ipaddress = parameters["ipaddr"].String(); + address.gateway = parameters["gateway"].String(); + address.primarydns = parameters["primarydns"].String(); + address.secondarydns = parameters["secondarydns"].String(); + + auto it = find(begin(CIDR_PREFIXES), end(CIDR_PREFIXES), parameters["netmask"].String()); + if (it == end(CIDR_PREFIXES)) + return Core::ERROR_BAD_REQUEST; + else + address.prefix = distance(begin(CIDR_PREFIXES), it); + + } + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->SetIPSettings(interface, address); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t Network::internalGetIPSettings(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + string interface{}; + string givenInterface{}; + string ipversion{}; + + if (parameters.HasLabel("interface")) + { + givenInterface = parameters["interface"].String(); + interface = getInterfaceTypeToName(givenInterface); + + if (!interface.empty() && "wlan0" != interface && "eth0" != interface) + { + return Core::ERROR_BAD_REQUEST; + } + } + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + Exchange::INetworkManager::IPAddress address{}; + rc = _nwmgr->GetIPSettings(interface, ipversion, address); + _nwmgr->Release(); + + if (Core::ERROR_NONE == rc) + { + givenInterface = getInterfaceNameToType(interface); + + response["interface"] = givenInterface; + response["ipversion"] = address.ipversion; + response["autoconfig"] = address.autoconfig; + if (!address.ipaddress.empty()) + { + response["ipaddr"] = address.ipaddress; + if ("IPv4" == address.ipversion) + { + if(address.prefix > CIDR_NETMASK_IP_LEN) + { + rc = Core::ERROR_GENERAL; + return rc; + } + response["netmask"] = CIDR_PREFIXES[address.prefix]; + } + else + response["netmask"] = address.prefix; + + response["dhcpserver"] = address.dhcpserver; + response["gateway"] = address.gateway; + response["primarydns"] = address.primarydns; + response["secondarydns"] = address.secondarydns; + } + } + } + return rc; + } + + uint32_t Network::getIPSettings (const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + JsonObject tmpResponse; + + DEBUGLOG_INPARAM(); + rc = internalGetIPSettings(parameters, tmpResponse); + + if (tmpResponse.HasLabel("ipaddr") && (!tmpResponse["ipaddr"].String().empty())) + response = tmpResponse; + else + { + NMLOG_INFO("IP Address not assigned to the given interface yet"); + rc = Core::ERROR_GENERAL; + } + if (Core::ERROR_NONE == rc) + response["success"] = true; + else + response["success"] = false; + DEBUGLOG_OUTPARAM(); + return Core::ERROR_NONE; + } + + uint32_t Network::getIPSettings2 (const JsonObject& parameters, JsonObject& response) + { + DEBUGLOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + rc = internalGetIPSettings(parameters, response); + if (Core::ERROR_NONE == rc) + response["success"] = true; + else + response["success"] = false; + DEBUGLOG_OUTPARAM(); + return Core::ERROR_NONE; + } + + uint32_t Network::isConnectedToInternet(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + string ipversion{}; + string interface{}; + Exchange::INetworkManager::InternetStatus status{}; + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr != nullptr) + { + rc = _nwmgr->IsConnectedToInternet(ipversion, interface, status); + _nwmgr->Release(); + + if (Core::ERROR_NONE == rc) + { + response["ipversion"] = ipversion; + response["connectedToInternet"] = (Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED == status); + } + } + + returnJson(rc); + } + + uint32_t Network::getInternetConnectionState(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + string ipversion{}; + string interface{}; + Exchange::INetworkManager::InternetStatus status{}; + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr != nullptr) + { + rc = _nwmgr->IsConnectedToInternet(ipversion, interface, status); + + if (Core::ERROR_NONE == rc) + { + response["ipversion"] = ipversion; + response["state"] = JsonValue(status); + if (Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL == status) + { + string uri{}; + _nwmgr->GetCaptivePortalURI(uri); + response["URI"] = uri; + } + } + _nwmgr->Release(); + } + + returnJson(rc); + } + + uint32_t Network::doPing(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + string result{}; + string endpoint{}; + uint32_t rc = Core::ERROR_GENERAL; + if (parameters.HasLabel("endpoint")) + { + string guid{}; + string ipversion{"IPv4"}; + uint32_t noOfRequest = 1; + uint16_t timeOutInSeconds = 1; + + endpoint = parameters["endpoint"].String(); + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + if (parameters.HasLabel("packets")) + noOfRequest = parameters["packets"].Number(); + + if (parameters.HasLabel("guid")) + guid = parameters["guid"].String(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->Ping(ipversion, endpoint, noOfRequest, timeOutInSeconds, guid, result); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + } + + if (Core::ERROR_NONE == rc) + { + JsonObject reply; + reply.FromString(result); + response = reply; + response["target"] = endpoint; + } + LOG_OUTPARAM(); + return rc; + } + + uint32_t Network::doTrace(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + if (parameters.HasLabel("endpoint")) + { + string result{}; + string ipversion{"IPv4"}; + const string endpoint = parameters["endpoint"].String(); + const uint32_t noOfRequest = parameters["packets"].Number(); + const string guid = parameters["guid"].String(); + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->Trace(ipversion, endpoint, noOfRequest, guid, result); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + JsonObject reply; + reply.FromString(result); + response = reply; + response["target"] = endpoint; + } + } + LOG_OUTPARAM(); + return rc; + } + + uint32_t Network::getPublicIP(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string ipAddress{}; + string ipversion = "IPv4"; + if (parameters.HasLabel("ipv6") && parameters["ipv6"].Boolean()) + ipversion = "IPv6"; + + string interface = ""; + if (parameters.HasLabel("iface")) + { + string givenInterface = parameters["iface"].String(); + interface = getInterfaceTypeToName(givenInterface); + } + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetPublicIP(interface, ipversion, ipAddress); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["public_ip"] = ipAddress; + } + returnJson(rc); + } + + uint32_t Network::isInterfaceEnabled (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + if (parameters.HasLabel("interface")) + { + bool enabled; + const string givenInterface = parameters["interface"].String(); + const string interface = getInterfaceTypeToName(givenInterface); + + if ("wlan0" != interface && "eth0" != interface) + return Core::ERROR_BAD_REQUEST; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetInterfaceState(interface, enabled); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["enabled"] = enabled; + } + else + rc = Core::ERROR_BAD_REQUEST; + + returnJson(rc); + } + + uint32_t Network::setConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + ::WPEFramework::RPC::IIteratorType* endpointsIter{}; + JsonArray array = parameters["endpoints"].Array(); + + if (0 == array.Length() || 5 < array.Length()) + { + NMLOG_DEBUG("minimum of 1 to maximum of 5 Urls are allowed"); + returnJson(rc); + } + + std::vector endpoints; + JsonArray::Iterator index(array.Elements()); + while (index.Next() == true) + { + if (Core::JSON::Variant::type::STRING == index.Current().Content()) + { + endpoints.push_back(index.Current().String().c_str()); + } + else + { + NMLOG_DEBUG("Unexpected variant type"); + returnJson(rc); + } + } + endpointsIter = (Core::Service::Create(endpoints)); + if (endpointsIter == nullptr) { + returnJson(rc); + } + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + rc = _nwmgr->SetConnectivityTestEndpoints(endpointsIter); + else + rc = Core::ERROR_UNAVAILABLE; + + if(_nwmgr) + _nwmgr->Release(); + + if (endpointsIter) + endpointsIter->Release(); + + returnJson(rc); + } + + uint32_t Network::startConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_NONE; + returnJson(rc); + } + + uint32_t Network::getCaptivePortalURI(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string uri; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetCaptivePortalURI(uri); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["uri"] = uri; + + returnJson(rc); + } + + uint32_t Network::stopConnectivityMonitoring(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_NONE; + returnJson(rc); + } + + uint32_t Network::getStbIp(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + JsonObject tmpResponse; + + rc = internalGetIPSettings(parameters, tmpResponse); + + if ((Core::ERROR_NONE == rc) && tmpResponse.HasLabel("ipaddr")) + { + response["ip"] = tmpResponse["ipaddr"]; + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t Network::getSTBIPFamily(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + JsonObject tmpResponse; + JsonObject tmpParameters; + + tmpParameters["ipversion"] = parameters["family"]; + + rc = internalGetIPSettings(tmpParameters, tmpResponse); + + if ((Core::ERROR_NONE == rc) && tmpResponse.HasLabel("ipaddr")) + { + response["ip"] = tmpResponse["ipaddr"]; + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + /** Private */ + void Network::subscribeToEvents(void) + { + uint32_t errCode = Core::ERROR_GENERAL; + if (m_networkmanager) + { + if (!m_subsIfaceStateChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onInterfaceStateChange"), &Network::onInterfaceStateChange); + if (Core::ERROR_NONE == errCode) + m_subsIfaceStateChange = true; + else + NMLOG_ERROR ("Subscribe to onInterfaceStateChange failed, errCode: %u", errCode); + } + + if (!m_subsActIfaceChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onActiveInterfaceChange"), &Network::onActiveInterfaceChange); + if (Core::ERROR_NONE == errCode) + m_subsActIfaceChange = true; + else + { + NMLOG_ERROR("Subscribe to onActiveInterfaceChange failed, errCode: %u", errCode); + } + } + + if (!m_subsIPAddrChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onIPAddressChange"), &Network::onIPAddressChange); + if (Core::ERROR_NONE == errCode) + m_subsIPAddrChange = true; + else + { + NMLOG_ERROR("Subscribe to onIPAddressChange failed, errCode: %u", errCode); + } + } + + if (!m_subsInternetChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onInternetStatusChange"), &Network::onInternetStatusChange); + if (Core::ERROR_NONE == errCode) + m_subsInternetChange = true; + else + { + NMLOG_ERROR("Subscribe to onInternetStatusChange failed, errCode: %u", errCode); + } + } + } + else + NMLOG_ERROR("m_networkmanager is null"); + + if (m_subsIfaceStateChange && m_subsActIfaceChange && m_subsIPAddrChange && m_subsInternetChange) + { + m_timer.stop(); + NMLOG_INFO("All the required events are subscribed; Retry timer stoped"); + } + else + { + m_timer.start(SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS); + NMLOG_INFO("Few required events are yet to be subscribed; Retry timer started"); + } + } + + string Network::getInterfaceNameToType(const string & interface) + { + if(interface == "wlan0") + return string("WIFI"); + else if(interface == "eth0") + return string("ETHERNET"); + return string(""); + } + + string Network::getInterfaceTypeToName(const string & interface) + { + if(interface == "WIFI") + return string("wlan0"); + else if(interface == "ETHERNET") + return string("eth0"); + return string(""); + } + + /** Event Handling and Publishing */ + void Network::ReportonInterfaceStateChange(const JsonObject& parameters) + { + JsonObject legacyParams; + string json; + string state = parameters["status"].String(); + + legacyParams["interface"] = getInterfaceNameToType(parameters["interface"].String()); + + /* State check */ + if(state == "INTERFACE_ADDED") + legacyParams["enabled"] = true; + else if(state == "INTERFACE_REMOVED") + legacyParams["enabled"] = false; + else if(state == "INTERFACE_LINK_UP") + legacyParams["status"] = "CONNECTED"; + else if(state == "INTERFACE_LINK_DOWN") + legacyParams["status"] = "DISCONNECTED"; + + legacyParams.ToString(json); + if((state == "INTERFACE_ADDED") || (state == "INTERFACE_REMOVED")) + { + NMLOG_INFO("Posting onInterfaceStatusChanged as %s", json.c_str()); + Notify("onInterfaceStatusChanged", legacyParams); + } + else if((state == "INTERFACE_LINK_UP") || (state == "INTERFACE_LINK_DOWN")) + { + NMLOG_INFO("Posting onConnectionStatusChanged as %s", json.c_str()); + Notify("onConnectionStatusChanged", legacyParams); + } + + return; + } + + void Network::ReportonActiveInterfaceChange(const JsonObject& parameters) + { + JsonObject legacyParams; + + legacyParams["oldInterfaceName"] = getInterfaceNameToType(parameters["prevActiveInterface"].String()); + legacyParams["newInterfaceName"] = getInterfaceNameToType(parameters["currentActiveInterface"].String()); + + string json; + legacyParams.ToString(json); + + NMLOG_INFO("Posting onDefaultInterfaceChanged as %s", json.c_str()); + Notify("onDefaultInterfaceChanged", legacyParams); + return; + } + + void Network::ReportonIPAddressChange(const JsonObject& parameters) + { + JsonObject legacyParams; + legacyParams["interface"] = getInterfaceNameToType(parameters["interface"].String()); + + if (parameters["ipversion"].String() == "IPv6") + { + legacyParams["ip6Address"] = parameters["ipaddress"]; + } + else + { + legacyParams["ip4Address"] = parameters["ipaddress"]; + } + + legacyParams["status"] = parameters["status"]; + + string json; + legacyParams.ToString(json); + NMLOG_INFO("Posting onIPAddressStatusChanged as %s", json.c_str()); + + Notify("onIPAddressStatusChanged", legacyParams); + + return; + } + + void Network::ReportonInternetStatusChange(const JsonObject& parameters) + { + JsonObject legacyParams; + string json; + + legacyParams["state"] = parameters["state"]; + legacyParams["status"] = parameters["status"]; + legacyParams.ToString(json); + + NMLOG_INFO("Posting onInternetStatusChanged as, %s", json.c_str()); + Notify("onInternetStatusChange", legacyParams); + return; + } + + void Network::onInterfaceStateChange(const JsonObject& parameters) + { + if(_gNWInstance) + _gNWInstance->ReportonInterfaceStateChange(parameters); + + return; + } + + void Network::onActiveInterfaceChange(const JsonObject& parameters) + { + if(_gNWInstance) + _gNWInstance->ReportonActiveInterfaceChange(parameters); + return; + } + + void Network::onIPAddressChange(const JsonObject& parameters) + { + if(_gNWInstance) + _gNWInstance->ReportonIPAddressChange(parameters); + return; + } + + void Network::onInternetStatusChange(const JsonObject& parameters) + { + if(_gNWInstance) + _gNWInstance->ReportonInternetStatusChange(parameters); + } + } +} diff --git a/LegacyPlugin_NetworkAPIs.h b/legacy/LegacyNetworkAPIs.h similarity index 77% rename from LegacyPlugin_NetworkAPIs.h rename to legacy/LegacyNetworkAPIs.h index d662881da..e8b5764ed 100644 --- a/LegacyPlugin_NetworkAPIs.h +++ b/legacy/LegacyNetworkAPIs.h @@ -1,3 +1,21 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ #pragma once #include @@ -6,6 +24,7 @@ #include "Module.h" #include "core/Link.h" #include "NetworkManagerTimer.h" +#include "INetworkManager.h" namespace WPEFramework { namespace Plugin { @@ -28,11 +47,12 @@ namespace WPEFramework { Network(const Network&) = delete; Network& operator=(const Network&) = delete; - void RegisterLegacyMethods(); - void UnregisterLegacyMethods(); + void registerLegacyMethods(void); + void unregisterLegacyMethods(void); void subscribeToEvents(void); - static std::string getInterfaceMapping(const std::string &interface); - void activatePrimaryPlugin(); + uint32_t internalGetIPSettings(const JsonObject& parameters, JsonObject& response); + string getInterfaceNameToType(const string & interface); + string getInterfaceTypeToName(const string & interface); /* Methods */ uint32_t getInterfaces(const JsonObject& parameters, JsonObject& response); @@ -44,6 +64,7 @@ namespace WPEFramework { uint32_t doPing(const JsonObject& parameters, JsonObject& response); uint32_t setIPSettings(const JsonObject& parameters, JsonObject& response); uint32_t getIPSettings(const JsonObject& parameters, JsonObject& response); + uint32_t getIPSettings2(const JsonObject& parameters, JsonObject& response); uint32_t isConnectedToInternet(const JsonObject& parameters, JsonObject& response); uint32_t setConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response); uint32_t getInternetConnectionState(const JsonObject& parameters, JsonObject& response); @@ -51,7 +72,7 @@ namespace WPEFramework { uint32_t getCaptivePortalURI(const JsonObject& parameters, JsonObject& response); uint32_t stopConnectivityMonitoring(const JsonObject& parameters, JsonObject& response); uint32_t getPublicIP(const JsonObject& parameters, JsonObject& response); - uint32_t setStunEndPoint(const JsonObject& parameters, JsonObject& response); + uint32_t setStunEndpoint(const JsonObject& parameters, JsonObject& response); uint32_t getStbIp(const JsonObject& parameters, JsonObject& response); uint32_t getSTBIPFamily(const JsonObject& parameters, JsonObject& response); @@ -82,8 +103,8 @@ namespace WPEFramework { private: PluginHost::IShell* m_service; - std::shared_ptr> m_networkmanager; - string m_defaultInterface; + std::shared_ptr> m_networkmanager; + //WPEFramework::Exchange::INetworkManager* m_nwmgr; NetworkManagerTimer m_timer; bool m_subsIfaceStateChange; diff --git a/legacy/LegacyWiFiManagerAPIs.conf.in b/legacy/LegacyWiFiManagerAPIs.conf.in new file mode 100644 index 000000000..92beadbb8 --- /dev/null +++ b/legacy/LegacyWiFiManagerAPIs.conf.in @@ -0,0 +1,4 @@ +precondition = ["Platform"] +callsign = "org.rdk.Wifi" +autostart = "@PLUGIN_LEGACY_WIFI_AUTOSTART@" +startuporder = "@PLUGIN_LEGACY_WIFI_STARTUPORDER@" diff --git a/legacy/LegacyWiFiManagerAPIs.cpp b/legacy/LegacyWiFiManagerAPIs.cpp new file mode 100644 index 000000000..02c9da61b --- /dev/null +++ b/legacy/LegacyWiFiManagerAPIs.cpp @@ -0,0 +1,976 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include "LegacyWiFiManagerAPIs.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerJsonEnum.h" + +using namespace std; +using namespace WPEFramework::Plugin; +#define API_VERSION_NUMBER_MAJOR 2 +#define API_VERSION_NUMBER_MINOR 0 +#define API_VERSION_NUMBER_PATCH 0 +#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager" +#define SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS 500 +#define WPA_SUPPLICANT_CONF "/opt/secure/wifi/wpa_supplicant.conf" + +#define LOG_INPARAM() { string json; parameters.ToString(json); NMLOG_INFO("params=%s", json.c_str() ); } +#define LOG_OUTPARAM() { string json; response.ToString(json); NMLOG_INFO("response=%s", json.c_str() ); } + +#define returnJson(rc) \ + { \ + if (Core::ERROR_NONE == rc) \ + response["success"] = true; \ + else \ + response["success"] = false; \ + LOG_OUTPARAM(); \ + return rc; \ + } + +/*! Error code: A recoverable, unexpected error occurred, + * as defined by one of the following values */ +typedef enum _WiFiErrorCode_t { + WIFI_SSID_CHANGED, /**< The SSID of the network changed */ + WIFI_CONNECTION_LOST, /**< The connection to the network was lost */ + WIFI_CONNECTION_FAILED, /**< The connection failed for an unknown reason */ + WIFI_CONNECTION_INTERRUPTED, /**< The connection was interrupted */ + WIFI_INVALID_CREDENTIALS, /**< The connection failed due to invalid credentials */ + WIFI_NO_SSID, /**< The SSID does not exist */ + WIFI_UNKNOWN, /**< Any other error */ + WIFI_AUTH_FAILED /**< The connection failed due to auth failure */ +} WiFiErrorCode_t; + +typedef enum _SsidSecurity +{ + NET_WIFI_SECURITY_NONE = 0, + NET_WIFI_SECURITY_WEP_64, + NET_WIFI_SECURITY_WEP_128, + NET_WIFI_SECURITY_WPA_PSK_TKIP, + NET_WIFI_SECURITY_WPA_PSK_AES, + NET_WIFI_SECURITY_WPA2_PSK_TKIP, + NET_WIFI_SECURITY_WPA2_PSK_AES, + NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA_WPA2_PSK, + NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE, + NET_WIFI_SECURITY_WPA3_PSK_AES, + NET_WIFI_SECURITY_WPA3_SAE, + NET_WIFI_SECURITY_NOT_SUPPORTED = 99, +} SsidSecurity; + +namespace WPEFramework +{ + class Job : public Core::IDispatch { + public: + Job(function work) + : _work(work) + { + } + void Dispatch() override + { + _work(); + } + + private: + function _work; + }; + + static Plugin::Metadata metadata( + // Version (Major, Minor, Patch) + API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH, + // Preconditions + {}, + // Terminations + {}, + // Controls + {} + ); + + WiFiManager* _gWiFiInstance = nullptr; + namespace Plugin + { + SERVICE_REGISTRATION(WiFiManager, API_VERSION_NUMBER_MAJOR, API_VERSION_NUMBER_MINOR, API_VERSION_NUMBER_PATCH); + + WiFiManager::WiFiManager() + : PluginHost::JSONRPC() + , m_service(nullptr) + , m_subsWiFiStateChange(false) + , m_subsAvailableSSIDs(false) + , m_subsWiFiStrengthChange(false) + { + _gWiFiInstance = this; + m_timer.connect(std::bind(&WiFiManager::subscribeToEvents, this)); + registerLegacyMethods(); + } + + WiFiManager::~WiFiManager() + { + _gWiFiInstance = nullptr; + } + + const string WiFiManager::Initialize(PluginHost::IShell* service ) + { + NetworkManagerLogger::Init(); + m_service = service; + string message{}; + + m_service->AddRef(); + + string callsign(NETWORK_MANAGER_CALLSIGN); + string token = ""; + + // TODO: use interfaces and remove token + auto security = m_service->QueryInterfaceByCallsign("SecurityAgent"); + if (security != nullptr) { + string payload = "http://localhost"; + if (security->CreateToken( + static_cast(payload.length()), + reinterpret_cast(payload.c_str()), + token) + == Core::ERROR_NONE) { + NMLOG_DEBUG("WiFi manager plugin got security token"); + } else { + NMLOG_WARNING("WiFi manager plugin failed to get security token"); + } + security->Release(); + } else { + NMLOG_INFO("WiFi manager plugin: No security agent"); + } + + string query = "token=" + token; + auto interface = m_service->QueryInterfaceByCallsign(callsign); + if (interface != nullptr) + { + int retry = 0; + PluginHost::IShell::state state = PluginHost::IShell::state::UNAVAILABLE; + do{ + state = interface->State(); + if(PluginHost::IShell::state::ACTIVATED == state) + { + NMLOG_INFO("Dependency Plugin '%s' Ready", callsign.c_str()); + break; + } + usleep(500*1000); + } while(retry++ < 5); + if(PluginHost::IShell::state::ACTIVATED == state) + { + Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); + m_networkmanager = make_shared >(_T(NETWORK_MANAGER_CALLSIGN), _T("org.rdk.Wifi"), query); + subscribeToEvents(); + } + else + message = _T("dependency Plugin 'NetworkManager' not Ready"); + + interface->Release(); + } + else + message = _T("Failed to get IShell for 'NetworkManager'"); + + return message; + } + + void WiFiManager::Deinitialize(PluginHost::IShell* /* service */) + { + m_timer.stop(); + unregisterLegacyMethods(); + + if (m_networkmanager) + m_networkmanager.reset(); + + m_networkmanager = NULL; + m_service->Release(); + m_service = nullptr; + _gWiFiInstance = nullptr; + } + + string WiFiManager::Information() const + { + return(string()); + } + + /** + * Hook up all our JSON RPC methods + * + * Each method definition comprises of: + * * Input parameters + * * Output parameters + * * Method name + * * Function that implements that method + */ + void WiFiManager::registerLegacyMethods(void) + { + CreateHandler({2}); + Register("cancelWPSPairing", &WiFiManager::cancelWPSPairing, this); + Register("clearSSID", &WiFiManager::clearSSID, this); + Register("connect", &WiFiManager::connect, this); + Register("disconnect", &WiFiManager::disconnect, this); + Register("getConnectedSSID", &WiFiManager::getConnectedSSID, this); + Register("getCurrentState", &WiFiManager::getCurrentState, this); + Register("getPairedSSID", &WiFiManager::getPairedSSID, this); + Register("getPairedSSIDInfo", &WiFiManager::getPairedSSIDInfo, this); + Register("getSupportedSecurityModes", &WiFiManager::getSupportedSecurityModes, this); + Register("initiateWPSPairing", &WiFiManager::initiateWPSPairing, this); + GetHandler(2)->Register("initiateWPSPairing", &WiFiManager::initiateWPSPairing, this); + Register("isPaired", &WiFiManager::isPaired, this); + Register("saveSSID", &WiFiManager::saveSSID, this); + Register("startScan", &WiFiManager::startScan, this); + Register("stopScan", &WiFiManager::stopScan, this); + Register("retrieveSSID", &WiFiManager::retrieveSSID, this); + } + + /** + * Unregister all our JSON-RPC methods + */ + void WiFiManager::unregisterLegacyMethods(void) + { + Unregister("cancelWPSPairing"); + Unregister("clearSSID"); + Unregister("connect"); + Unregister("disconnect"); + Unregister("getConnectedSSID"); + Unregister("getCurrentState"); + Unregister("getPairedSSID"); + Unregister("getPairedSSIDInfo"); + Unregister("getSupportedSecurityModes"); + Unregister("initiateWPSPairing"); + Unregister("isPaired"); + Unregister("saveSSID"); + Unregister("startScan"); + Unregister("stopScan"); + Unregister("retrieveSSID"); + } + + static inline uint32_t mapToLegacySecurityMode(const uint32_t securityMode) + { + if (securityMode == 0) + return 0; /* NET_WIFI_SECURITY_NONE */ + else if (securityMode == 1) + return 6; /* NET_WIFI_SECURITY_WPA2_PSK_AES */ + else if (securityMode == 2) + return 14; /* NET_WIFI_SECURITY_WPA3_SAE */ + else if (securityMode == 3) + return 12; /* NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE */ + + return 0; /* NET_WIFI_SECURITY_NONE */ + } + + static inline uint32_t mapToNewSecurityMode(const uint32_t legacyMode) + { + if ((legacyMode == NET_WIFI_SECURITY_NONE) || + (legacyMode == NET_WIFI_SECURITY_WEP_64) || + (legacyMode == NET_WIFI_SECURITY_WEP_128)) + { + return 0; /* WIFI_SECURITY_NONE */ + } + else if ((legacyMode == NET_WIFI_SECURITY_WPA_PSK_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA_PSK_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA2_PSK_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA2_PSK_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA_WPA2_PSK) || + (legacyMode == NET_WIFI_SECURITY_WPA3_PSK_AES)) + { + return 1; /* WIFI_SECURITY_WPA_PSK */ + } + else if (legacyMode == NET_WIFI_SECURITY_WPA3_SAE) + { + return 2; /* WIFI_SECURITY_SAE */ + } + else if ((legacyMode == NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA_ENTERPRISE_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE)) + { + return 3; /* WIFI_SECURITY_EAP */ + } + + return 0; /* WIFI_SECURITY_NONE */ + } + + uint32_t WiFiManager::cancelWPSPairing (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->StopWPS(); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["result"] = string(); + + returnJson(rc); + } + + uint32_t WiFiManager::clearSSID (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string ssid{}; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->RemoveKnownSSID(ssid); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["result"] = 0; + + returnJson(rc); + } + + uint32_t WiFiManager::connect(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiConnectTo ssid{}; + JsonObject inParam = parameters; + + if (inParam.HasLabel("passphrase")) + inParam["passphrase"] = "*******"; + + string jsonStr; + inParam.ToString(jsonStr); + NMLOG_INFO("params=%s", jsonStr.c_str() ); + + if (parameters.HasLabel("ssid")) + ssid.ssid = parameters["ssid"].String(); + + if (parameters.HasLabel("passphrase")) + ssid.passphrase = parameters["passphrase"].String(); + + if (parameters.HasLabel("securityMode")) + ssid.security= static_cast (mapToNewSecurityMode(parameters["securityMode"].Number())); + + ssid.persist = true; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->WiFiConnect(ssid); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t WiFiManager::getConnectedSSID (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetConnectedSSID(ssidInfo); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["ssid"] = ssidInfo.ssid; + response["bssid"] = ssidInfo.bssid; + response["rate"] = to_string(ssidInfo.rate); + response["noise"] = to_string(ssidInfo.noise); + response["security"] = JsonValue(mapToLegacySecurityMode(ssidInfo.security)); + response["signalStrength"] = to_string(ssidInfo.strength); + std::string freqStr = to_string(ssidInfo.frequency); + response["frequency"] = freqStr.substr(0, 3); + } + returnJson(rc); + } + + uint32_t WiFiManager::getCurrentState(const JsonObject& parameters, JsonObject& response) + { + Exchange::INetworkManager::WiFiState state; + uint32_t rc = Core::ERROR_GENERAL; + + LOG_INPARAM(); + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetWifiState(state); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + /* Legacy Enums Mapping */ + if (state >= Exchange::INetworkManager::WiFiState::WIFI_STATE_ERROR) + response["state"] = 6; // 6: FAILED - The device has encountered an unrecoverable error with the Wifi adapter. + else if (state > Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED) + response["state"] = 2; // 2: DISCONNECTED - The device is installed and enabled, but not yet connected to a network + else + response["state"] = JsonValue(state); + } + + returnJson(rc); + } + + uint32_t WiFiManager::getPairedSSID(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + ::WPEFramework::RPC::IIteratorType* _ssids{}; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetKnownSSIDs(_ssids); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + if (_ssids != nullptr) + { + string _resultItem_{}; + while (_ssids->Next(_resultItem_) == true) { + response["ssid"] = _resultItem_; + /* Just take one Entry : 1st Entry */ + break; + } + _ssids->Release(); + } + } + returnJson(rc); + } + + uint32_t WiFiManager::getPairedSSIDInfo(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->GetConnectedSSID(ssidInfo); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["ssid"] = ssidInfo.ssid; + response["bssid"] = ssidInfo.bssid; + } + returnJson(rc); + } + + uint32_t WiFiManager::getSupportedSecurityModes(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_NONE; + JsonObject security_modes; + security_modes["NET_WIFI_SECURITY_NONE"] = (int)NET_WIFI_SECURITY_NONE; + security_modes["NET_WIFI_SECURITY_WEP_64"] = (int)NET_WIFI_SECURITY_WEP_64; + security_modes["NET_WIFI_SECURITY_WEP_128"] = (int)NET_WIFI_SECURITY_WEP_128; + security_modes["NET_WIFI_SECURITY_WPA_PSK_TKIP"] = (int)NET_WIFI_SECURITY_WPA_PSK_TKIP; + security_modes["NET_WIFI_SECURITY_WPA_PSK_AES"] = (int)NET_WIFI_SECURITY_WPA_PSK_AES; + security_modes["NET_WIFI_SECURITY_WPA2_PSK_TKIP"] = (int)NET_WIFI_SECURITY_WPA2_PSK_TKIP; + security_modes["NET_WIFI_SECURITY_WPA2_PSK_AES"] = (int)NET_WIFI_SECURITY_WPA2_PSK_AES; + security_modes["NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP"] = (int)NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP; + security_modes["NET_WIFI_SECURITY_WPA_ENTERPRISE_AES"] = (int)NET_WIFI_SECURITY_WPA_ENTERPRISE_AES; + security_modes["NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP"] = (int)NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP; + security_modes["NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES"] = (int)NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES; + security_modes["NET_WIFI_SECURITY_WPA_WPA2_PSK"] = (int)NET_WIFI_SECURITY_WPA_WPA2_PSK; + security_modes["NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE"] = (int)NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE; + security_modes["NET_WIFI_SECURITY_WPA3_PSK_AES"] = (int)NET_WIFI_SECURITY_WPA3_PSK_AES; + security_modes["NET_WIFI_SECURITY_WPA3_SAE"] = (int)NET_WIFI_SECURITY_WPA3_SAE; + security_modes["NET_WIFI_SECURITY_NOT_SUPPORTED"] = (int)NET_WIFI_SECURITY_NOT_SUPPORTED; + + response["security_modes"] = security_modes; + + returnJson(rc); + } + + uint32_t WiFiManager::isPaired (const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + LOG_INPARAM(); + JsonObject tmpResponse; + + rc = getPairedSSID(parameters, tmpResponse); + + if (Core::ERROR_NONE == rc) + { + if (tmpResponse.HasLabel("ssid")) + response["result"] = 1; + else + response["result"] = 0; + } + returnJson(rc); + } + + uint32_t WiFiManager::saveSSID (const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiConnectTo ssid{}; + JsonObject inParam = parameters; + + if (inParam.HasLabel("passphrase")) + inParam["passphrase"] = "*******"; + + string jsonStr; + inParam.ToString(jsonStr); + NMLOG_INFO("params=%s", jsonStr.c_str() ); + + if (parameters.HasLabel("ssid") && parameters.HasLabel("passphrase")) + { + ssid.ssid = parameters["ssid"].String(); + ssid.passphrase = parameters["passphrase"].String(); + ssid.security = static_cast (mapToNewSecurityMode(parameters["security"].Number())); + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->AddToKnownSSIDs(ssid); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + } + + if (Core::ERROR_NONE == rc) + response["result"] = 0; + + returnJson(rc); + } + + uint32_t WiFiManager::disconnect (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->WiFiDisconnect(); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["result"] = 0; + + returnJson(rc); + } + + uint32_t WiFiManager::initiateWPSPairing (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string wps_pin{}; + Core::JSON::EnumType method; + + if (parameters.HasLabel("method")) + { + if (parameters["method"].Content() == WPEFramework::Core::JSON::Variant::type::STRING) + method.FromString(parameters["method"].String()); + else if (parameters["method"].Content() == WPEFramework::Core::JSON::Variant::type::NUMBER) + method = static_cast (parameters["method"].Number()); + + if ((Exchange::INetworkManager::WIFI_WPS_PIN == method) && parameters.HasLabel("pin")) + { + wps_pin = parameters["pin"].String(); + } + } + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->StartWPS(method, wps_pin); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["result"] = 0; + else + response["result"] = 1; + returnJson(rc); + } + + uint32_t WiFiManager::startScan(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IStringIterator* frequencies = nullptr; + Exchange::INetworkManager::IStringIterator* ssids = NULL; + + if (parameters.HasLabel("frequency")) + { + std::vector frequencyList; + if (Core::JSON::Variant::type::STRING == parameters["frequency"].Content()) + { + frequencyList.push_back(parameters["frequency"].String()); + } + else + { + NMLOG_ERROR("Unexpected variant type in frequency parameter."); + returnJson(rc); + } + + frequencies = (Core::Service::Create(frequencyList)); + if (frequencies == nullptr) { + returnJson(rc); + } + } + + if (parameters.HasLabel("ssid")) + { + string inputSSID = parameters["ssid"].String(); + string ssid{}; + vector inputSSIDlist; + stringstream ssidStream(inputSSID); + while (getline(ssidStream, ssid, '|')) + { + inputSSIDlist.push_back(ssid); + } + + ssids = (Core::Service::Create(inputSSIDlist)); + if (ssids == nullptr) { + if (frequencies) + frequencies->Release(); + returnJson(rc); + } + } + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->StartWiFiScan(frequencies, ssids); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + if (frequencies) + frequencies->Release(); + + if (ssids) + ssids->Release(); + + returnJson(rc); + } + + uint32_t WiFiManager::stopScan(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + auto _nwmgr = m_service->QueryInterfaceByCallsign(NETWORK_MANAGER_CALLSIGN); + if (_nwmgr) + { + rc = _nwmgr->StopWiFiScan(); + _nwmgr->Release(); + } + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t WiFiManager::retrieveSSID (const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + std::string line; + std::string securityPattern = "key_mgmt="; + std::string ssidPattern = "ssid="; + std::string passphrasePattern = "psk="; + std::string security, ssid, passphrase; + + std::ifstream configFile(WPA_SUPPLICANT_CONF); + if (!configFile.is_open()) + { + NMLOG_ERROR("Not able to open the file %s", WPA_SUPPLICANT_CONF); + response["success"] = false; + rc = Core::ERROR_NOT_EXIST; + return rc; + } + + while (std::getline(configFile, line)) + { + NMLOG_DEBUG("Attempting to read the configuration to populate SSID specific information"); + size_t pos; + + // Fetch ssid value + if (ssid.empty()) { + pos = line.find(ssidPattern); + if (pos != std::string::npos) + { + pos += ssidPattern.length(); + size_t end = line.find('"', pos + 1); + if (end == std::string::npos) + { + end = line.length(); + } + ssid = line.substr(pos + 1, end - pos - 1); + NMLOG_DEBUG("SSID found"); + continue; + } + } + + if (!ssid.empty()) { + // Fetch security value + pos = line.find(securityPattern); + if (pos != std::string::npos) + { + pos += securityPattern.length(); + size_t end = line.find(' ', pos); + if (end == std::string::npos) + { + end = line.length(); + } + security = line.substr(pos, end - pos); + continue; + } + + // Fetch passphare value + pos = line.find(passphrasePattern); + if (pos != std::string::npos) + { + pos += passphrasePattern.length(); + size_t end = line.find('"', pos + 1); + if (end == std::string::npos) + { + end = line.length(); + } + passphrase = line.substr(pos + 1, end - pos - 1); + } + NMLOG_DEBUG("Fetched SSID = %s, security = %s", ssid.c_str(), security.c_str()); + } + } + configFile.close(); + if (!ssid.empty()) + { + response["ssid"] = ssid; + //As enterprise data is not persisted, WPA_EAP mode is not considered here + if(security == "NONE") + response["securityMode"] = JsonValue(NET_WIFI_SECURITY_NONE); + else if(security == "SAE") + response["securityMode"] = JsonValue(NET_WIFI_SECURITY_WPA3_SAE); + else + response["securityMode"] = JsonValue(NET_WIFI_SECURITY_WPA2_PSK_AES); + /* WPA3_PSK_AES has backward compatibility for PSK. So WPA-PSK is considered as default */ + response["passphrase"] = passphrase; + response["success"] = true; + rc = Core::ERROR_NONE; + } + else + response["success"] = false; + return rc; + } + + /** Private */ + void WiFiManager::subscribeToEvents(void) + { + uint32_t errCode = Core::ERROR_GENERAL; + if (m_networkmanager) + { + if (!m_subsWiFiStateChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onWiFiStateChange"), &WiFiManager::onWiFiStateChange); + if (Core::ERROR_NONE == errCode) + m_subsWiFiStateChange = true; + else + NMLOG_ERROR ("Subscribe to onWiFiStateChange failed, errCode: %u", errCode); + } + + if (!m_subsAvailableSSIDs) + { + errCode = m_networkmanager->Subscribe(5000, _T("onAvailableSSIDs"), &WiFiManager::onAvailableSSIDs); + if (Core::ERROR_NONE == errCode) + m_subsAvailableSSIDs = true; + else + NMLOG_ERROR("Subscribe to onAvailableSSIDs failed, errCode: %u", errCode); + } + + if (!m_subsWiFiStrengthChange) + { + errCode = m_networkmanager->Subscribe(5000, _T("onWiFiSignalQualityChange"), &WiFiManager::onWiFiSignalQualityChange); + if (Core::ERROR_NONE == errCode) + m_subsWiFiStrengthChange = true; + else + NMLOG_ERROR("Subscribe to onWiFiSignalQualityChange failed, errCode: %u", errCode); + } + } + else + NMLOG_ERROR("m_networkmanager is null"); + + if (m_subsWiFiStateChange && m_subsAvailableSSIDs && m_subsWiFiStrengthChange) + { + m_timer.stop(); + NMLOG_INFO("All the required events are subscribed; Retry timer stoped"); + } + else + { + m_timer.start(SUBSCRIPTION_TIMEOUT_IN_MILLISECONDS); + NMLOG_INFO("Few required events are yet to be subscribed; Retry timer started"); + } + } + + bool WiFiManager::ErrorCodeMapping(const uint32_t ipvalue, uint32_t &opvalue) + { + bool ret = true; + + switch (ipvalue) + { + case Exchange::INetworkManager::WIFI_STATE_SSID_CHANGED: + opvalue = WIFI_SSID_CHANGED; + break; + case Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST: + opvalue = WIFI_CONNECTION_LOST; + break; + case Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED: + opvalue = WIFI_CONNECTION_FAILED; + break; + case Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED: + opvalue = WIFI_CONNECTION_INTERRUPTED; + break; + case Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS: + opvalue = WIFI_INVALID_CREDENTIALS; + break; + case Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND: + opvalue = WIFI_NO_SSID; + break; + case Exchange::INetworkManager::WIFI_STATE_ERROR: + opvalue = WIFI_UNKNOWN; + break; + case Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED: + opvalue = WIFI_AUTH_FAILED; + break; + default: + ret = false; + break; + } + return ret; + } + + /** Event Handling and Publishing */ + void WiFiManager::onWiFiStateChange(const JsonObject& parameters) + { + JsonObject legacyResult; + JsonObject legacyErrorResult; + string json; + uint32_t errorCode; + uint32_t state = parameters["state"].Number(); + + legacyResult["state"] = parameters["state"]; + legacyResult["isLNF"] = false; + + if(_gWiFiInstance) + { + if(ErrorCodeMapping(state, errorCode)) + { + legacyErrorResult["code"] = errorCode; + NMLOG_INFO("onError with errorcode as, %u", errorCode); + + legacyErrorResult.ToString(json); + NMLOG_INFO("Posting onError as %s", json.c_str()); + + _gWiFiInstance->Notify("onError", legacyErrorResult); + } + else + { + NMLOG_INFO("onWiFiStateChange with state as: %u", state); + + legacyResult.ToString(json); + NMLOG_INFO("Posting onWIFIStateChanged as %s", json.c_str()); + _gWiFiInstance->Notify("onWIFIStateChanged", legacyResult); + } + } + else + NMLOG_WARNING("Ignoring %s", __FUNCTION__); + + return; + } + + void WiFiManager::onAvailableSSIDs(const JsonObject& parameters) + { + string json; + JsonArray ssidsUpdated; + JsonObject newParameters; + JsonArray ssids = parameters["ssids"].Array(); + for (int i = 0; i < ssids.Length(); i++) + { + JsonObject object = ssids[i].Object(); + uint32_t security = object["security"].Number(); + JsonObject newObject; + newObject["ssid"] = object["ssid"]; + newObject["security"] = mapToLegacySecurityMode(security); + newObject["signalStrength"] = object["strength"]; + newObject["frequency"] = object["frequency"]; + ssidsUpdated.Add(newObject); + } + newParameters["ssids"] = ssidsUpdated; + newParameters["moreData"] = false; + + newParameters.ToString(json); + NMLOG_INFO("Event with %d SSIDs as, %s", ssids.Length(), json.c_str()); + if(_gWiFiInstance) + _gWiFiInstance->Notify("onAvailableSSIDs", newParameters); + else + NMLOG_WARNING("Ignoring %s", __FUNCTION__); + + return; + } + + void WiFiManager::onWiFiSignalQualityChange(const JsonObject& parameters) + { + JsonObject legacyParams; + legacyParams["signalStrength"] = parameters["strength"]; + legacyParams["strength"] = parameters["quality"]; + + string json; + legacyParams.ToString(json); + NMLOG_INFO("Posting onWifiSignalThresholdChanged as %s", json.c_str()); + if (_gWiFiInstance) + _gWiFiInstance->Notify("onWifiSignalThresholdChanged", legacyParams); + else + NMLOG_WARNING("Ignoring %s", __FUNCTION__); + + return; + } + } +} diff --git a/LegacyPlugin_WiFiManagerAPIs.h b/legacy/LegacyWiFiManagerAPIs.h similarity index 77% rename from LegacyPlugin_WiFiManagerAPIs.h rename to legacy/LegacyWiFiManagerAPIs.h index 242355824..90d817ca0 100644 --- a/LegacyPlugin_WiFiManagerAPIs.h +++ b/legacy/LegacyWiFiManagerAPIs.h @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2020 RDK Management +* Copyright 2023 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,19 +22,6 @@ #include "Module.h" #include "NetworkManagerTimer.h" -/*! Error code: A recoverable, unexpected error occurred, - * as defined by one of the following values */ -typedef enum _WiFiErrorCode_t { - WIFI_SSID_CHANGED, /**< The SSID of the network changed */ - WIFI_CONNECTION_LOST, /**< The connection to the network was lost */ - WIFI_CONNECTION_FAILED, /**< The connection failed for an unknown reason */ - WIFI_CONNECTION_INTERRUPTED, /**< The connection was interrupted */ - WIFI_INVALID_CREDENTIALS, /**< The connection failed due to invalid credentials */ - WIFI_NO_SSID, /**< The SSID does not exist */ - WIFI_UNKNOWN, /**< Any other error */ - WIFI_AUTH_FAILED /**< The connection failed due to auth failure */ -} WiFiErrorCode_t; - namespace WPEFramework { namespace Plugin { @@ -72,15 +59,14 @@ namespace WPEFramework { uint32_t getPairedSSID(const JsonObject& parameters, JsonObject& response); uint32_t getPairedSSIDInfo(const JsonObject& parameters, JsonObject& response); uint32_t isPaired(const JsonObject& parameters, JsonObject& response); - uint32_t setSignalThresholdChangeEnabled(const JsonObject& parameters, JsonObject& response); - uint32_t isSignalThresholdChangeEnabled(const JsonObject& parameters, JsonObject& response); uint32_t getSupportedSecurityModes(const JsonObject& parameters, JsonObject& response); + uint32_t retrieveSSID(const JsonObject& parameters, JsonObject& response); //End methods //Begin events static void onWiFiStateChange(const JsonObject& parameters); static void onAvailableSSIDs(const JsonObject& parameters); - static void onWiFiSignalStrengthChange(const JsonObject& parameters); + static void onWiFiSignalQualityChange(const JsonObject& parameters); //End events @@ -96,16 +82,15 @@ namespace WPEFramework { virtual string Information() const override; private: - void RegisterLegacyMethods(); - void UnregisterLegacyMethods(); + void registerLegacyMethods(void); + void unregisterLegacyMethods(void); void subscribeToEvents(void); static std::string getInterfaceMapping(const std::string &interface); static bool ErrorCodeMapping(const uint32_t ipvalue , uint32_t &opvalue); - void activatePrimaryPlugin(); private: PluginHost::IShell* m_service; - std::shared_ptr> m_networkmanager; + std::shared_ptr> m_networkmanager; NetworkManagerTimer m_timer; bool m_subsWiFiStateChange; bool m_subsAvailableSSIDs; diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt new file mode 100644 index 000000000..6f19009db --- /dev/null +++ b/plugin/CMakeLists.txt @@ -0,0 +1,193 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Build ${PROJECT_NAME} v${PROJECT_VERSION}") + +set(PLUGIN_NAME NetworkManager) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) +set(MODULE_IMPL_NAME ${NAMESPACE}${PLUGIN_NAME}Impl) + +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) +find_package(CURL) + +if (USE_RDK_LOGGER) + find_package(rdklogger REQUIRED) + add_definitions(-DUSE_RDK_LOGGER) + + include_directories(${RDKLOGGER_INCLUDE_DIRS}) +endif (USE_RDK_LOGGER) + +if (USE_TELEMETRY) + include_directories(${T2_INCLUDE_DIRS}) +endif (USE_TELEMETRY) + +if(ENABLE_GNOME_NETWORKMANAGER) + pkg_check_modules(GLIB REQUIRED glib-2.0) + pkg_check_modules(LIBNM REQUIRED libnm) + pkg_check_modules(GLIB REQUIRED gio-2.0) + if(ENABLE_MIGRATION_MFRMGR_SUPPORT) + add_definitions(-DENABLE_MIGRATION_MFRMGR_SUPPORT) + find_package(IARMBus REQUIRED) + endif () +else() + find_package(IARMBus REQUIRED) +endif () + +set(PLUGIN_NETWORKMANAGER_LOGLEVEL "3" CACHE STRING "To configure default loglevel NetworkManager plugin") +set(PLUGIN_NETWORKMANAGER_STARTUPORDER "25" CACHE STRING "To configure startup order of Unified NetworkManager plugin") + +set(PLUGIN_NETWORKMANAGER_AUTOSTART "false" CACHE STRING "Set the default AutoStart of NetworkManager Plugin") +set(PLUGIN_BUILD_REFERENCE ${PROJECT_VERSION} CACHE STRING "To Set the Hash for the plugin") +add_definitions(-DPLUGIN_BUILD_REFERENCE=${PLUGIN_BUILD_REFERENCE}) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin) + +# Build the main plugin that runs inside the daemon +add_library(${MODULE_NAME} SHARED + NetworkManager.cpp + NetworkManagerJsonRpc.cpp + NetworkManagerLogger.cpp + Module.cpp) + +target_link_libraries(${MODULE_NAME} PRIVATE + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE) + +if(USE_CONNECTIVITYCHECKMGR) + target_link_libraries(${MODULE_NAME} PRIVATE ${RFCAPI_LIBRARY}) + if(RFCAPI_INCLUDE_DIR) + target_include_directories(${MODULE_NAME} PRIVATE ${RFCAPI_INCLUDE_DIR}) + endif() +endif() + +add_library(${MODULE_IMPL_NAME} SHARED + NetworkManagerImplementation.cpp + NetworkManagerStunClient.cpp + NetworkManagerLogger.cpp + NetworkManagerPowerClient.cpp + Module.cpp) + +# The built-in ConnectivityMonitor is always compiled. The ConnectivityCheckMgr +# delegation client (NetworkManagerConnectivityClient.cpp) is compiled only when +# USE_CONNECTIVITYCHECKMGR is enabled; runtime selection between the two backends +# is done in resolveConnectivityCheckMgrEnabled(). STUN is unaffected either way. +target_sources(${MODULE_IMPL_NAME} PRIVATE + NetworkManagerConnectivity.cpp) + +# Optional ConnectivityCheckMgr delegation. The USE_CONNECTIVITYCHECKMGR option, +# rfcapi library discovery, and the USE_CONNECTIVITYCHECKMGR compile definition are +# declared in the top-level CMakeLists.txt (mirroring USE_TELEMETRY). When enabled we +# compile the delegation client and link the rfcapi library used to read the TR-181 +# feature flag. +if(USE_CONNECTIVITYCHECKMGR) + target_sources(${MODULE_IMPL_NAME} PRIVATE NetworkManagerConnectivityClient.cpp) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${RFCAPI_LIBRARY}) + if(RFCAPI_INCLUDE_DIR) + target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${RFCAPI_INCLUDE_DIR}) + endif() +endif() + +if(ENABLE_GNOME_NETWORKMANAGER) + if(ENABLE_GNOME_GDBUS) + message("networkmanager building with gdbus") + target_sources(${MODULE_IMPL_NAME} PRIVATE + gnome/gdbus/NetworkManagerGdbusProxy.cpp + gnome/gdbus/NetworkManagerGdbusClient.cpp + gnome/gdbus/NetworkManagerGdbusEvent.cpp + gnome/gdbus/NetworkManagerGdbusMgr.cpp + gnome/gdbus/NetworkManagerGdbusUtils.cpp + gnome/NetworkManagerGnomeUtils.cpp + NetworkManagerSecretAgent.cpp) + target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ${LIBNM_INCLUDE_DIRS}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${GLIB_LIBRARIES} ${GIO_LIBRARIES} uuid) + else() + message("networkmanager building with libnm") + target_sources(${MODULE_IMPL_NAME} PRIVATE + gnome/NetworkManagerGnomeProxy.cpp + gnome/NetworkManagerGnomeWIFI.cpp + gnome/NetworkManagerGnomeEvents.cpp + gnome/NetworkManagerGnomeUtils.cpp + NetworkManagerSecretAgent.cpp ) + if(ENABLE_MIGRATION_MFRMGR_SUPPORT) + target_sources(${MODULE_IMPL_NAME} PRIVATE + gnome/NetworkManagerGnomeMfrMgr.cpp + gnome/gdbus/NetworkManagerGdbusMgr.cpp + gnome/gdbus/NetworkManagerGdbusUtils.cpp + ) + endif() + if(ENABLE_MIGRATION_MFRMGR_SUPPORT) + target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBNM_INCLUDE_DIRS} ${IARMBUS_INCLUDE_DIRS}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${LIBNM_LIBRARIES} ${IARMBUS_LIBRARIES}) + else() + target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${GLIB_INCLUDE_DIRS} ${LIBNM_INCLUDE_DIRS}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${LIBNM_LIBRARIES}) + endif() + endif() +else() + message("networkmanager building with RDK netsrvmgr") + target_sources(${MODULE_IMPL_NAME} PRIVATE rdk/NetworkManagerRDKProxy.cpp) + target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${IARMBUS_INCLUDE_DIRS}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${IARMBUS_LIBRARIES}) +endif() + +set_target_properties(${MODULE_IMPL_NAME} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE) + +target_link_libraries(${MODULE_IMPL_NAME} PRIVATE + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ) + + +target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${CURL_LIBRARIES}) +target_include_directories(${MODULE_IMPL_NAME} PRIVATE ${CURL_INCLUDE_DIRS}) + +if (USE_RDK_LOGGER) + target_link_libraries(${MODULE_NAME} PRIVATE ${RDKLOGGER_LIBRARIES}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${RDKLOGGER_LIBRARIES}) +endif (USE_RDK_LOGGER) + +if (USE_TELEMETRY) + target_link_libraries(${MODULE_NAME} PRIVATE ${T2_LIBRARIES}) + target_link_libraries(${MODULE_IMPL_NAME} PRIVATE ${T2_LIBRARIES}) +endif (USE_TELEMETRY) + +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) +install(TARGETS ${MODULE_IMPL_NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) + +if(ENABLE_GNOME_NETWORKMANAGER) + install(PROGRAMS gnome/scripts/nm_interface_handler.sh + DESTINATION ${CMAKE_INSTALL_PREFIX}/../lib/rdk) + install(FILES gnome/systemd/gnome-networkmanager-migration.conf + DESTINATION ${CMAKE_INSTALL_PREFIX}/../lib/systemd/system/wpeframework-networkmanager.service.d) + install(FILES gnome/systemd/nm-powermanager.conf + DESTINATION ${CMAKE_INSTALL_PREFIX}/../lib/systemd/system/wpeframework-networkmanager.service.d) +endif(ENABLE_GNOME_NETWORKMANAGER) + +#Generate Plugin configuration file +write_config() diff --git a/plugin/Module.cpp b/plugin/Module.cpp new file mode 100644 index 000000000..ebecea700 --- /dev/null +++ b/plugin/Module.cpp @@ -0,0 +1,21 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Module.h" +MODULE_NAME_DECLARATION(PLUGIN_BUILD_REFERENCE) diff --git a/Module.h b/plugin/Module.h similarity index 96% rename from Module.h rename to plugin/Module.h index b6552bb9d..6382d0d47 100644 --- a/Module.h +++ b/plugin/Module.h @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2022 RDK Management +* Copyright 2023 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/NetworkManager.conf.in b/plugin/NetworkManager.conf.in similarity index 85% rename from NetworkManager.conf.in rename to plugin/NetworkManager.conf.in index 22fca875b..ac3127cc8 100644 --- a/NetworkManager.conf.in +++ b/plugin/NetworkManager.conf.in @@ -1,8 +1,10 @@ -autostart = "true" callsign= "org.rdk.NetworkManager" +startuporder = "@PLUGIN_NETWORKMANAGER_STARTUPORDER@" +autostart = "@PLUGIN_NETWORKMANAGER_AUTOSTART@" process= JSON() process.add("outofprocess", "true") +process.add("locator", "lib@MODULE_IMPL_NAME@.so") connectivity = JSON() connectivity.add("endpoint_1", "@PLUGIN_NETWORKMANAGER_CONN_ENDPOINT_1@") diff --git a/NetworkManager.cpp b/plugin/NetworkManager.cpp similarity index 57% rename from NetworkManager.cpp rename to plugin/NetworkManager.cpp index 47a93053d..aaeb6a8e6 100644 --- a/NetworkManager.cpp +++ b/plugin/NetworkManager.cpp @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2022 RDK Management +* Copyright 2023 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,9 +18,13 @@ **/ #include "NetworkManager.h" - #include +#ifdef USE_CONNECTIVITYCHECKMGR +#include +#include "rfcapi.h" +#endif + namespace WPEFramework { namespace Plugin @@ -39,11 +43,11 @@ namespace WPEFramework NetworkManager::NetworkManager() : _connectionId(0), _service(nullptr), - _NetworkManager(nullptr), + _networkManagerImpl(nullptr), + _networkManager(nullptr), + m_useConnectivityCheckMgr(false), _notification(this) { - // Don't do any work in the constructor - all set up should be done in Initialize - m_defaultInterface = "wlan0"; } NetworkManager::~NetworkManager() @@ -61,13 +65,13 @@ namespace WPEFramework */ const string NetworkManager::Initialize(PluginHost::IShell *service) { + string message{}; // Make sure everything is null as we expect ASSERT(_service == nullptr); - ASSERT(_NetworkManager == nullptr); + ASSERT(_networkManager == nullptr); // Syslog Startup messages are always printed by default SYSLOG(Logging::Startup, (_T("Initializing NetworkManager"))); - SYSLOG(Logging::Startup, (_T("Initialize running in process %d"), Core::ProcessInfo().Id())); NetworkManagerLogger::Init(); // Register the Connection::Notification first. Do this before we start our actual plugin // in case something goes wrong or is disconnected - this way we know we're at least registered @@ -81,34 +85,63 @@ namespace WPEFramework // // Ideally for large, complex plugins we would actually split the plugin into two libraries - a thin library that just calls // _service->Root to launch WPEProcess, and a larger library that is only ever run inside WPEProcess only (we do this for Cobalt and WebKitBrowser) - _NetworkManager = service->Root(_connectionId, 25000, _T("NetworkManagerImplementation")); + _networkManager = service->Root(_connectionId, 25000, _T("NetworkManagerImplementation")); // Still running inside the main WPEFramework process - the child process will have now been spawned and registered if necessary - if (_NetworkManager != nullptr) + if (_networkManager != nullptr) { - // set the plugin configuration - Exchange::INetworkManager::NMLogging _loglevel; - _NetworkManager->Configure(_service->ConfigLine(), _loglevel); - // configure loglevel in libWPEFrameworkNetworkManager.so + // Register Notifications + SYSLOG(Logging::Startup, (_T("Registering Notification to NetworkManager"))); + _networkManager->Register(&_notification); + + SYSLOG(Logging::Startup, (_T("Configuring NetworkManager"))); + if (_networkManager->Configure(service->ConfigLine()) != Core::ERROR_NONE) + { + SYSLOG(Logging::Shutdown, (_T("NetworkManager failed to configure"))); + } + else + { +#ifdef USE_CONNECTIVITYCHECKMGR + RFC_ParamData_t rfcParam = {0}; + const WDMP_STATUS rfcRetCode = getRFCParameter( + const_cast("NetworkManager"), + const_cast("Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.ConnectivityCheckMgr.Enable"), + &rfcParam); + if (rfcRetCode == WDMP_SUCCESS || rfcRetCode == WDMP_ERR_DEFAULT_VALUE) { + m_useConnectivityCheckMgr = (strcasecmp(rfcParam.value, "true") == 0); + } +#endif + SYSLOG(Logging::Startup, (_T("Configuring successful"))); + } + + // Set the plugin log level + Exchange::INetworkManager::Logging _loglevel; + _networkManager->GetLogLevel(_loglevel); NetworkManagerLogger::SetLevel(static_cast (_loglevel)); - _NetworkManager->Register(&_notification); // Register all custom JSON-RPC methods + SYSLOG(Logging::Startup, (_T("Registering JSONRPC Methods"))); RegisterAllMethods(); + + + // Get IPlugin interface for this plugin + SYSLOG(Logging::Startup, (_T("Retrieve ComRPC Interface for NetworkManager"))); + _networkManagerImpl = _networkManager->QueryInterface(); } else { // Something went wrong, clean up + SYSLOG(Logging::Shutdown, (_T("NetworkManager out-of-process creation failed"))); TRACE(Trace::Error, (_T("Failed to initialize NetworkManager"))); _service->Unregister(&_notification); _service = nullptr; // Returning a string signals that we failed to initialize - WPEFramework will print this as an error message - return "Failed to initialize NetworkManager"; + message = _T("Failed to initialize NetworkManager"); } // Success - return ""; + return message; } /** @@ -119,26 +152,50 @@ namespace WPEFramework void NetworkManager::Deinitialize(PluginHost::IShell *service) { ASSERT(_service == service); - ASSERT(_NetworkManager != nullptr); + ASSERT(_networkManager != nullptr); - TRACE(Trace::Information, (_T("Deinitializing NetworkManager"))); - TRACE(Trace::Information, (_T("Deinitialize running in process %d"), Core::ProcessInfo().Id())); - - if (_NetworkManager != nullptr) + SYSLOG(Logging::Shutdown, (_T("Deinitializing NetworkManager"))); + if (_networkManager != nullptr) { - // TODO:: Work out exactly what triggers the shutdown of the out-of-process host + SYSLOG(Logging::Shutdown, (_T("Unregister Thunder Notifications for NetworkManager"))); _service->Unregister(&_notification); - _NetworkManager->Unregister(&_notification); + + SYSLOG(Logging::Shutdown, (_T("Unregister Event Notifications of NetworkManager"))); + _networkManager->Unregister(&_notification); // Unregister all our JSON-RPC methods + SYSLOG(Logging::Shutdown, (_T("Unregister JSON RPC Methods of NetworkManager"))); UnregisterAllMethods(); - _NetworkManager->Release(); + + // Release the IPlugin + if(_networkManagerImpl) + { + SYSLOG(Logging::Shutdown, (_T("Release of IPlugin of NetworkManager"))); + _networkManagerImpl->Release(); + _networkManagerImpl = nullptr; + } + + RPC::IRemoteConnection* connection = service->RemoteConnection(_connectionId); + + SYSLOG(Logging::Shutdown, (_T("Release of COMRPC Interface of NetworkManager"))); + _networkManager->Release(); + if (connection != nullptr) + { + SYSLOG(Logging::Shutdown, (_T("Release of Thunder COMRPC Connection of NetworkManager"))); + // Lets trigger the cleanup sequence for + // out-of-process code. Which will guard + // that unwilling processes, get shot if + // not stopped friendly :-) + connection->Terminate(); + connection->Release(); + } } // Set everything back to default _connectionId = 0; _service = nullptr; - _NetworkManager = nullptr; + _networkManagerImpl = nullptr; + _networkManager = nullptr; } string NetworkManager::Information() const diff --git a/NetworkManager.h b/plugin/NetworkManager.h similarity index 58% rename from NetworkManager.h rename to plugin/NetworkManager.h index a32ed8643..dea887e94 100644 --- a/NetworkManager.h +++ b/plugin/NetworkManager.h @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2022 RDK Management +* Copyright 2023 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,13 +21,12 @@ #include "Module.h" -// Include the interface we created -//#include #include "INetworkManager.h" #include "NetworkManagerLogger.h" #include #include +#include namespace WPEFramework { @@ -52,59 +51,6 @@ namespace WPEFramework Notification() = delete; Notification(const Notification &) = delete; Notification &operator=(const Notification &) = delete; - string InterfaceStateToString(Exchange::INetworkManager::InterfaceState event) - { - switch (event) - { - case Exchange::INetworkManager::INTERFACE_ADDED: - return "INTERFACE_ADDED"; - case Exchange::INetworkManager::INTERFACE_LINK_UP: - return "INTERFACE_LINK_UP"; - case Exchange::INetworkManager::INTERFACE_LINK_DOWN: - return "INTERFACE_LINK_DOWN"; - case Exchange::INetworkManager::INTERFACE_ACQUIRING_IP: - return "INTERFACE_ACQUIRING_IP"; - case Exchange::INetworkManager::INTERFACE_REMOVED: - return "INTERFACE_REMOVED"; - case Exchange::INetworkManager::INTERFACE_DISABLED: - return "INTERFACE_DISABLED"; - } - return ""; - } - - string WiFiSignalQualityToString(Exchange::INetworkManager::WiFiSignalQuality quality) - { - switch (quality) - { - case Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED: - return "Disconnected"; - case Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_WEAK: - return "Weak"; - case Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_FAIR: - return "Fair"; - case Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_GOOD: - return "Good"; - case Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT: - return "Excellent"; - } - return ""; - } - - string InternetStatusToString(const Exchange::INetworkManager::InternetStatus internetStatus) - { - switch (internetStatus) - { - case Exchange::INetworkManager::InternetStatus::INTERNET_LIMITED: - return "LIMITED_INTERNET"; - case Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL: - return "CAPTIVE_PORTAL"; - case Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED: - return "FULLY_CONNECTED"; - default: - return "NO_INTERNET"; - } - return ""; - } public: explicit Notification(NetworkManager *parent) @@ -117,83 +63,45 @@ namespace WPEFramework } public: - void onInterfaceStateChange(const Exchange::INetworkManager::InterfaceState event, const string interface) override - { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject params; - params["interface"] = interface; - params["state"] = InterfaceStateToString(event); - _parent.Notify("onInterfaceStateChange", params); - } - void onIPAddressChange(const string interface, const bool isAcquired, const bool isIPv6, const string ipAddress) override + void onInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject params; - params["status"] = string (isAcquired ? "ACQUIRED" : "LOST"); - params["interface"] = interface; - params["ipAddress"] = ipAddress; - params["isIPv6"] = isIPv6; - _parent.Notify("onIPAddressChange", params); + _parent.onInterfaceStateChange(state, interface); } void onActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject params; - params["oldInterfaceName"] = prevActiveInterface; - params["newInterfaceName"] = currentActiveinterface; - _parent.Notify("onActiveInterfaceChange", params); - + _parent.onActiveInterfaceChange(prevActiveInterface, currentActiveinterface); } - void onInternetStatusChange(const Exchange::INetworkManager::InternetStatus oldState, const Exchange::INetworkManager::InternetStatus newstate) override + void onIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject params; - - params["state"] = static_cast (newstate);; - params["status"] = InternetStatusToString(newstate); - params["prevState"] = static_cast (oldState); - params["prevStatus"] = InternetStatusToString(oldState); - - _parent.Notify("onInternetStatusChange", params); + _parent.onIPAddressChange(interface, ipversion, ipaddress, status); + } - if (Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED == newstate) - { - NMLOG_INFO("Notify Thunder ISubsystem internet"); - _parent.PublishToThunderAboutInternet(); - } + void onRouteChange(const string interface, const string ipversion, const string ipaddress, const string gateway, const string primarydns) override + { + _parent.onRouteChange(interface, ipversion, ipaddress, gateway, primarydns); } - // WiFi Notifications that other processes can subscribe to - void onAvailableSSIDs(const string jsonOfWiFiScanResults) override + void onInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string reason) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonArray scanResults; - JsonObject result; - scanResults.FromString(jsonOfWiFiScanResults); - result["ssids"] = scanResults; - _parent.Notify("onAvailableSSIDs", result); + _parent.onInternetStatusChange(prevState, currState, interface, reason); + } + void onAvailableSSIDs(const string jsonOfScanResults) override + { + _parent.onAvailableSSIDs(jsonOfScanResults); } - void onWiFiStateChange(const Exchange::INetworkManager::WiFiState state) override + void onWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject result; - result["state"] = static_cast (state); - _parent.Notify("onWiFiStateChange", result); + _parent.onWiFiStateChange(state, ssid); } - void onWiFiSignalStrengthChange(const string ssid, const string signalLevel, const Exchange::INetworkManager::WiFiSignalQuality signalQuality) override + void onWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality) override { - NMLOG_TRACE("%s", __FUNCTION__); - JsonObject result; - result["ssid"] = ssid; - result["signalQuality"] = WiFiSignalQualityToString(signalQuality); - result["signalLevel"] = signalLevel; - _parent.Notify("onWiFiSignalStrengthChange", result); + _parent.onWiFiSignalQualityChange(ssid, strength, noise, snr, quality); } // The activated/deactived methods are part of the RPC::IRemoteConnection::INotification @@ -247,7 +155,7 @@ namespace WPEFramework // find the real implementation // This allows other components to call QueryInterface() and // receive the actual implementation (which could be in-process or out-of-process) - INTERFACE_AGGREGATE(Exchange::INetworkManager, _NetworkManager) + INTERFACE_AGGREGATE(Exchange::INetworkManager, _networkManager) END_INTERFACE_MAP /* @@ -264,7 +172,53 @@ namespace WPEFramework return (m_publicIPAddress.empty() == true ? PluginHost::ISubSystem::IInternet::UNKNOWN : (m_publicIPAddressType == "IPV6" ? PluginHost::ISubSystem::IInternet::IPV6 : PluginHost::ISubSystem::IInternet::IPV4)); } void PublishToThunderAboutInternet(); + /* Class to store and manage cached data */ + template + class Cache { + public: + Cache() : is_set(false) {} + + Cache& operator=(const CacheValue& value) { + std::lock_guard lock(mutex); + this->value = value; + is_set.store(true); + return *this; + } + + Cache& operator=(CacheValue&& value) { + std::lock_guard lock(mutex); + this->value = std::move(value); + is_set.store(true); + return *this; + } + + bool isSet() const { + return is_set.load(); + } + + void reset() { + is_set.store(false); + } + const CacheValue& getValue() const { + std::lock_guard lock(mutex); + return value; + } + + CacheValue& getValue() { + std::lock_guard lock(mutex); + return value; + } + + private: + CacheValue value; + std::atomic is_set; + mutable std::mutex mutex; + }; + + // cached varibales + Cache m_ipv4AddressCache; + Cache m_ipv6AddressCache; private: // Notification/event handlers // Clean up when we're told to deactivate @@ -276,9 +230,9 @@ namespace WPEFramework // JSON-RPC methods (take JSON in, spit JSON back out) uint32_t SetLogLevel (const JsonObject& parameters, JsonObject& response); + uint32_t GetLogLevel (const JsonObject& parameters, JsonObject& response); uint32_t GetAvailableInterfaces (const JsonObject& parameters, JsonObject& response); uint32_t GetPrimaryInterface (const JsonObject& parameters, JsonObject& response); - uint32_t SetPrimaryInterface (const JsonObject& parameters, JsonObject& response); uint32_t GetInterfaceState(const JsonObject& parameters, JsonObject& response); uint32_t SetInterfaceState(const JsonObject& parameters, JsonObject& response); uint32_t GetIPSettings(const JsonObject& parameters, JsonObject& response); @@ -289,32 +243,42 @@ namespace WPEFramework uint32_t SetConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response); uint32_t IsConnectedToInternet(const JsonObject& parameters, JsonObject& response); uint32_t GetCaptivePortalURI(const JsonObject& parameters, JsonObject& response); - uint32_t StartConnectivityMonitoring(const JsonObject& parameters, JsonObject& response); - uint32_t StopConnectivityMonitoring(const JsonObject& parameters, JsonObject& response); uint32_t GetPublicIP(const JsonObject& parameters, JsonObject& response); uint32_t Ping(const JsonObject& parameters, JsonObject& response); uint32_t Trace(const JsonObject& parameters, JsonObject& response); + uint32_t SetHostname (const JsonObject& parameters, JsonObject& response); uint32_t StartWiFiScan(const JsonObject& parameters, JsonObject& response); uint32_t StopWiFiScan(const JsonObject& parameters, JsonObject& response); uint32_t GetKnownSSIDs(const JsonObject& parameters, JsonObject& response); uint32_t AddToKnownSSIDs(const JsonObject& parameters, JsonObject& response); uint32_t RemoveKnownSSID(const JsonObject& parameters, JsonObject& response); + uint32_t ConnectToKnownSSID(const JsonObject& parameters, JsonObject& response); uint32_t WiFiConnect(const JsonObject& parameters, JsonObject& response); uint32_t WiFiDisconnect(const JsonObject& parameters, JsonObject& response); uint32_t GetConnectedSSID(const JsonObject& parameters, JsonObject& response); uint32_t StartWPS(const JsonObject& parameters, JsonObject& response); uint32_t StopWPS(const JsonObject& parameters, JsonObject& response); uint32_t GetWifiState(const JsonObject& parameters, JsonObject& response); - uint32_t GetWiFiSignalStrength(const JsonObject& parameters, JsonObject& response); + uint32_t GetWiFiSignalQuality(const JsonObject& parameters, JsonObject& response); uint32_t GetSupportedSecurityModes(const JsonObject& parameters, JsonObject& response); + void onInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface); + void onActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface); + void onIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status); + void onRouteChange(const string interface, const string ipversion, const string ipaddress, const string gateway, const string primarydns); + void onInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string reason); + void onAvailableSSIDs(const string jsonOfScanResults); + void onWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid); + void onWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality); + private: uint32_t _connectionId; PluginHost::IShell *_service; - Exchange::INetworkManager *_NetworkManager; + PluginHost::IPlugin* _networkManagerImpl; + Exchange::INetworkManager *_networkManager; + bool m_useConnectivityCheckMgr; Core::Sink _notification; string m_publicIPAddress; - string m_defaultInterface; string m_publicIPAddressType; }; } diff --git a/plugin/NetworkManagerConnectivity.cpp b/plugin/NetworkManagerConnectivity.cpp new file mode 100644 index 000000000..57243aaef --- /dev/null +++ b/plugin/NetworkManagerConnectivity.cpp @@ -0,0 +1,786 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include + +#include "NetworkManagerImplementation.h" +#include "NetworkManagerConnectivity.h" +#include "NetworkManagerLogger.h" +#include "INetworkManager.h" + +namespace WPEFramework +{ + namespace Plugin + { + + extern NetworkManagerImplementation* _instance; + + constexpr auto INTERNET_NOT_AVAILABLE = Exchange::INetworkManager::InternetStatus::INTERNET_NOT_AVAILABLE; + constexpr auto INTERNET_LIMITED = Exchange::INetworkManager::InternetStatus::INTERNET_LIMITED; + constexpr auto INTERNET_CAPTIVE_PORTAL = Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL; + constexpr auto INTERNET_FULLY_CONNECTED = Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED; + constexpr auto INTERNET_UNKNOWN = Exchange::INetworkManager::InternetStatus::INTERNET_UNKNOWN; + + constexpr auto IP_ADDRESS_V4 = Exchange::INetworkManager::IPVersion::IP_ADDRESS_V4; + constexpr auto IP_ADDRESS_V6 = Exchange::INetworkManager::IPVersion::IP_ADDRESS_V6; + + static const char* getInternetStateString(Exchange::INetworkManager::InternetStatus state) + { + switch(state) + { + case INTERNET_NOT_AVAILABLE: return "NO_INTERNET"; + case INTERNET_LIMITED: return "LIMITED_INTERNET"; + case INTERNET_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL"; + case INTERNET_FULLY_CONNECTED: return "FULLY_CONNECTED"; + default: return "UNKNOWN"; + } + } + + void EndpointManager::writeEndpointsToFile(const std::vector& endpoints) + { + std::ofstream outputFile(m_CachefilePath); + if (outputFile.is_open()) + { + for (const std::string& str : endpoints) + { + outputFile << str << '\n'; + } + outputFile.close(); + } + else + { + NMLOG_ERROR("Connectivity endpoints file write error"); + } + } + + std::vector EndpointManager::readEndpointsFromFile() + { + std::vector readStrings; + std::ifstream inputFile(m_CachefilePath); + if (inputFile.is_open()) + { + std::string line; + while (std::getline(inputFile, line)) + { + readStrings.push_back(line); + } + inputFile.close(); + } + else + { + NMLOG_ERROR("Failed to open connectivity endpoint cache file"); + } + return readStrings; + } + + void EndpointManager::setConnectivityMonitorEndpoints(const std::vector& endpoints) + { + const std::lock_guard lock(m_endpointMutex); + if(endpoints.empty()) + { + NMLOG_ERROR("Empty endpoints"); + return; + } + + m_Endpoints.clear(); + for (const auto &endpoint : endpoints) { + if(!endpoint.empty() && endpoint.size() > 3) + m_Endpoints.push_back(endpoint.c_str()); + else + NMLOG_ERROR("endpoint not vallied = %s", endpoint.c_str()); + } + + // write the endpoints to a file + writeEndpointsToFile(m_Endpoints); + + std::string endpointsStr; + for (const auto& endpoint : m_Endpoints) + endpointsStr.append(endpoint).append(" "); + NMLOG_INFO("Connectivity monitor endpoints updated -: %d :- %s", static_cast(m_Endpoints.size()), endpointsStr.c_str()); + } + + EndpointManager::EndpointManager() + { + m_CachefilePath = NMCONNECTIVITY_MONITOR_CACHE_FILE; + m_Endpoints.clear(); // default value will be loaded from NetworkManagerImplementation configuration + + std::ifstream inputFile(m_CachefilePath); + if (inputFile.is_open()) + { + std::string line; + std::vector endpoints{}; + while (std::getline(inputFile, line)) + { + if(!line.empty() && line.size() > 3) + endpoints.push_back(line); + } + NMLOG_WARNING("cached connectivity endpoints loaded .."); + setConnectivityMonitorEndpoints(endpoints); + inputFile.close(); + } + else + { + NMLOG_ERROR("no endpoint cache file found"); + } + } + + std::vector EndpointManager::getConnectivityMonitorEndpoints() + { + const std::lock_guard lock(m_endpointMutex); + return m_Endpoints; + } +#if 0 + bool DnsResolver::resolveIP(std::string& uri, Exchange::INetworkManager::IPVersion& ipversion) + { + struct addrinfo sockAddrProps, *resultAddr= NULL; + char ipStr[INET6_ADDRSTRLEN] = {0}; + + if(ipversion == IP_ADDRESS_V4) + sockAddrProps.ai_family = AF_INET; + else if(ipversion == IP_ADDRESS_V6) + sockAddrProps.ai_family = AF_INET6; + else + sockAddrProps.ai_family = AF_UNSPEC; + + sockAddrProps.ai_socktype = SOCK_STREAM; + sockAddrProps.ai_flags = 0; + sockAddrProps.ai_protocol = 0; + + int ret = getaddrinfo(uri.c_str(), NULL, &sockAddrProps, &resultAddr); + if (ret != 0) { + NMLOG_WARNING("Resolved IP Failed getaddrinfo: %s", gai_strerror(ret)); + return false; + } + + NMLOG_DEBUG("Resolved IP addresses for %s", uri.c_str()); + + for (struct addrinfo * resutIP = resultAddr; resutIP != NULL; resutIP = resutIP->ai_next) + { + void *addr= NULL; + if (resutIP->ai_family == AF_INET) { + struct sockaddr_in *ipv4 = (struct sockaddr_in *)resutIP->ai_addr; + addr = &(ipv4->sin_addr); + ipv4Resolved = true; + } else if (resutIP->ai_family == AF_INET6) { + struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)resutIP->ai_addr; + addr = &(ipv6->sin6_addr); + ipv6Resolved = true; + } else { + continue; + } + + inet_ntop(resutIP->ai_family, addr, ipStr, sizeof(ipStr)); + NMLOG_DEBUG("Resolved IP --> %s", ipStr); + } + + freeaddrinfo(resultAddr); + return true; + } + + std::string DnsResolver::convertUrIToDomainName(std::string& url) + { + size_t domainStart = 0; + size_t protocolEnd = url.find("://"); + if (protocolEnd != std::string::npos) { + domainStart = protocolEnd + 3; + } + + size_t domainEnd = url.find('/', domainStart); + if (domainEnd != std::string::npos) + return url.substr(domainStart, domainEnd - domainStart); + else + return url.substr(domainStart); + } + + DnsResolver::DnsResolver(std::string url, Exchange::INetworkManager::IPVersion ipversion, int curlErrorCode) + { + NMLOG_DEBUG("url %s - ipversion %d - curl error %d ", url.c_str(), ipversion, curlErrorCode); + if(url.empty()) { + NMLOG_ERROR("URI/hostname missing"); + return; + } + switch (curlErrorCode) + { + case CURLE_COULDNT_RESOLVE_HOST: // 6 - Could not resolve host + case CURLE_OPERATION_TIMEDOUT: // 28 - Operation timeout. time-out period + case CURLE_RECV_ERROR: // 56 - Failure with receiving network data + { + m_domain = convertUrIToDomainName(url); + resolveIP(m_domain, ipversion); + break; + } + default: + ipv4Resolved = false; + ipv6Resolved = false; + break; + } + } +#endif + static void getDeviceModel(std::string& modelNum, std::string& buildVersion) + { + const std::string filePath = "/etc/device.properties"; + std::ifstream file(filePath); + if (!file.is_open()) { + NMLOG_ERROR("Error opening file: %s", filePath.c_str()); + modelNum = "RDKDevice"; + buildVersion = "1.0"; + return; + } + + std::string line; + const std::string modelKey = "MODEL_NUM="; + const std::string buildKey = "BUILD_VERSION="; + + while (std::getline(file, line)) { + if (line.find(modelKey) == 0) { // line starts with "MODEL_NUM=" + modelNum = line.substr(modelKey.length()); // return value after '=' + } else if (line.find(buildKey) == 0) { // line starts with "BUILD_VERSION=" + buildVersion = line.substr(buildKey.length()); + } + if (!modelNum.empty() && !buildVersion.empty()) { + break; + } + } + if(modelNum.empty()) + modelNum = "RDKDevice"; + if(buildVersion.empty()) + buildVersion = "1.0"; + } + + TestConnectivity::TestConnectivity(const std::vector& endpoints, + long timeout_ms, bool headReq, uint8_t ipversion, std::string interface) + { + getDeviceModel(m_deviceModel, m_buildVersion); + internetSate = INTERNET_UNKNOWN; + if(endpoints.size() < 1) { + NMLOG_ERROR("Endpoints size error ! curl check not possible"); + return; + } + + internetSate = checkCurlResponse(endpoints, timeout_ms, headReq, ipversion, interface); + } + + static bool curlVerboseEnabled() { + std::ifstream fileStream("/tmp/nm.plugin.debug"); + return fileStream.is_open(); + } + + static long current_time () + { + struct timespec ts; + clock_gettime (CLOCK_MONOTONIC, &ts); + return (ts.tv_sec * 1000) + (ts.tv_nsec / 1000000); + } + static size_t writeFunction(void* ptr, size_t size, size_t nmemb, std::string* data) { + #ifdef DBG_CURL_GET_RESPONSE + LOG_DBG("%s",(char*)ptr); + #endif + return size * nmemb; + } + + /* + * It is calculated as the current time plus the specified timeout duration. + * This ensures that the entire operation does not exceed the given timeout, providing a hard limit + * for the network connectivity check. + */ + Exchange::INetworkManager::InternetStatus TestConnectivity::checkCurlResponse(const std::vector& endpoints, + long timeout_ms, bool headReq, uint8_t ipversion, std::string interface) + { + long deadline = 0, startTime = current_time(), time_now = 0, time_earlier = 0; + std::string logmsg =""; + + CURLM *curl_multi_handle = curl_multi_init(); + if (!curl_multi_handle) + { + NMLOG_ERROR("curl_multi_init returned NULL"); + return INTERNET_NOT_AVAILABLE; + } + + CURLMcode mc; + std::vector curl_easy_handles; + std::vector http_responses; + struct curl_slist *chunk = NULL; + std::string userAgent = "RDKCaptiveCheck/1.1"; + userAgent += " " + m_deviceModel + "/" + m_buildVersion; + + chunk = curl_slist_append(chunk, "Cache-Control: no-cache, no-store"); + chunk = curl_slist_append(chunk, "Connection: close"); + for (const auto& endpoint : endpoints) + { + CURL *curl_easy_handle = curl_easy_init(); + if (!curl_easy_handle) + { + NMLOG_ERROR("endpoint = <%s> curl_easy_init returned NULL", endpoint.c_str()); + continue; + } + curlSetOpt(curl_easy_handle, CURLOPT_URL, endpoint.c_str()); + logmsg += endpoint; + /* set our custom set of headers */ + curlSetOpt(curl_easy_handle, CURLOPT_HTTPHEADER, chunk); + NMLOG_INFO ("INTERNET_CONNECTIVITY_MONITORING_USERAGENT : %s", userAgent.c_str()); + curlSetOpt(curl_easy_handle, CURLOPT_USERAGENT, userAgent.c_str()); + if(!headReq) + { + /* HTTPGET request added insted of HTTPHEAD request fix for DELIA-61526 */ + curlSetOpt(curl_easy_handle, CURLOPT_HTTPGET, 1L); + logmsg += ", Get"; + } + else + logmsg += ", Head"; + curlSetOpt(curl_easy_handle, CURLOPT_WRITEFUNCTION, writeFunction); + curlSetOpt(curl_easy_handle, CURLOPT_TIMEOUT_MS, timeout_ms); + if (IP_ADDRESS_V4 == ipversion) { + curlSetOpt(curl_easy_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + logmsg +=", IPv4"; + } + else if (IP_ADDRESS_V6 == ipversion) { + curlSetOpt(curl_easy_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + logmsg +=", IPv6"; + } + else + logmsg +=", IPv4/IPv6"; + + if(interface == "wlan0") + { + curlSetOpt(curl_easy_handle, CURLOPT_INTERFACE, "wlan0"); + logmsg +=", wlan0"; + } + else if(interface == "eth0") + { + curlSetOpt(curl_easy_handle, CURLOPT_INTERFACE, "eth0"); + logmsg +=", eth0"; + } + + curlSetOpt(curl_easy_handle, CURLOPT_PRIVATE, logmsg.c_str()); + if(curlVerboseEnabled()) + { + curlSetOpt(curl_easy_handle, CURLOPT_VERBOSE, 1L); + } + if (CURLM_OK != (mc = curl_multi_add_handle(curl_multi_handle, curl_easy_handle))) + { + NMLOG_ERROR("endpoint = <%s> curl_multi_add_handle returned %d (%s)", endpoint.c_str(), mc, curl_multi_strerror(mc)); + curl_easy_cleanup(curl_easy_handle); + continue; + } + curl_easy_handles.push_back(curl_easy_handle); + } + + int handles, msgs_left; + char *url = nullptr; + char *endpntConf = nullptr; + if((current_time() - startTime) > 1000) // 1 sec + { + NMLOG_WARNING("curl init taken more than 1000 ms; ie: %d ms", (int)(current_time() - startTime)); + } + + /* The deadline variable represents the absolute time by which the curl_multi_perform + operation must complete.providing a hard limit for the network connectivity check */ + deadline = current_time() + timeout_ms; + + while (1) + { + if (CURLM_OK != (mc = curl_multi_perform(curl_multi_handle, &handles))) + { + NMLOG_ERROR("curl_multi_perform returned %d (%s)", mc, curl_multi_strerror(mc)); + break; + } + for (CURLMsg *msg; NULL != (msg = curl_multi_info_read(curl_multi_handle, &msgs_left)); ) + { + long response_code = -1; + if (msg->msg != CURLMSG_DONE) + continue; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &endpntConf); + if (CURLE_OK == msg->data.result) { + if (curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response_code) == CURLE_OK) + { + if (HttpStatus_302_Found == response_code) { + if ( (curl_easy_getinfo(msg->easy_handle, CURLINFO_REDIRECT_URL, &url) == CURLE_OK) && url != nullptr) { + NMLOG_INFO("captive portal found !!!"); + captivePortalURI = url; + } + } + + // if ipversion not specified, determine it based on the resolved IP address + if(ipversion != IP_ADDRESS_V4 && ipversion != IP_ADDRESS_V6) + { + char *ip = NULL; + curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIMARY_IP, &ip); + if(ip != NULL) { + if(strchr(ip, ':')) + ipversion = IP_ADDRESS_V6; + else + ipversion = IP_ADDRESS_V4; //Fix Me: Error case handling if ip is NULL + NMLOG_DEBUG("ipversion not specified, determined ipversion = %s", (ipversion == IP_ADDRESS_V6) ? "IPv6" : "IPv4"); + } + else + { + NMLOG_WARNING("could not determine it as curl primary ip is NULL"); + } + } + } + } + else + { + NMLOG_ERROR("INTERNET_CONNECTIVITY_MONITORING_CURL_ERROR : For endpoint = <%s>, interface = <%s> got curl error = %d (%s)", + endpntConf, + interface.empty() ? "any" : interface.c_str(), + msg->data.result, + curl_easy_strerror(msg->data.result)); + curlErrorCode = static_cast(msg->data.result); + } + http_responses.push_back(response_code); + } + time_earlier = time_now; + time_now = current_time(); + if (handles == 0 || time_now >= deadline) + break; + if (CURLM_OK != (mc = curl_multi_poll(curl_multi_handle, NULL, 0, deadline - time_now, NULL))) + { + NMLOG_ERROR("curl_multi_poll returned %d (%s)", mc, curl_multi_strerror(mc)); + break; + } + } + + if(curlVerboseEnabled()) { + NMLOG_DEBUG("endpoints count = %d response count %d, handles = %d, deadline = %ld, time_now = %ld, time_earlier = %ld", + static_cast(endpoints.size()), static_cast(http_responses.size()), handles, deadline, time_now, time_earlier); + } + + for (const auto& curl_easy_handle : curl_easy_handles) + { + curl_easy_getinfo(curl_easy_handle, CURLINFO_PRIVATE, &endpntConf); + //LOG_DBG("endpoint = <%s> terminating attempt", endpoint); + curl_multi_remove_handle(curl_multi_handle, curl_easy_handle); + curl_easy_cleanup(curl_easy_handle); + } + curl_multi_cleanup(curl_multi_handle); + /* free the custom headers */ + curl_slist_free_all(chunk); + return checkInternetStateFromResponseCode(http_responses); + } + + /* + * verifying Most occurred response code is 50 % or more + * Example 1 : + * if we have 5 endpoints so response also 5 ( 204 302 204 204 200 ) . Here count is 204 :- 3, 302 :- 1, 200 :- 1 + * Return Internet State: FULLY_CONNECTED - 60 % + * Example 2 : + * if we have 4 endpoints so response also 4 ( 204 204 200 200 ) . Here count is 204 :- 2, 200 :- 2 + * Return Internet State: FULLY_CONNECTED - 50 % + */ + + Exchange::INetworkManager::InternetStatus TestConnectivity::checkInternetStateFromResponseCode(const std::vector& responses) + { + Exchange::INetworkManager::InternetStatus InternetConnectionState = INTERNET_NOT_AVAILABLE; + nsm_connectivity_httpcode http_response_code = HttpStatus_response_error; + + int max_count = 0; + for (int element : responses) + { + int element_count = count(responses.begin(), responses.end(), element); + if (element_count > max_count) + { + http_response_code = static_cast(element); + max_count = element_count; + } + } + + /* Calculate the percentage of the most frequent code occurrences */ + float percentage = (static_cast(max_count) / responses.size()); + + /* 50 % connectivity check */ + if (percentage >= 0.5) + { + switch (http_response_code) + { + case HttpStatus_204_No_Content: + InternetConnectionState = INTERNET_FULLY_CONNECTED; + NMLOG_INFO("Internet State: FULLY_CONNECTED - %.1f%%", (percentage*100)); + break; + case HttpStatus_200_OK: + InternetConnectionState = INTERNET_LIMITED; + NMLOG_INFO("Internet State: LIMITED_INTERNET - %.1f%%", (percentage*100)); + break; + case HttpStatus_511_Authentication_Required: + case HttpStatus_302_Found: + InternetConnectionState = INTERNET_CAPTIVE_PORTAL; + NMLOG_INFO("Internet State: CAPTIVE_PORTAL - %.1f%%", (percentage*100)); + break; + default: + InternetConnectionState = INTERNET_NOT_AVAILABLE; + if(http_response_code == -1) + NMLOG_ERROR("Internet State: NO_INTERNET (curl error)"); + else + NMLOG_WARNING("Internet State: NO_INTERNET (http code: %d - %.1f%%)", static_cast(http_response_code), percentage * 100); + break; + } + } + return InternetConnectionState; + } + + ConnectivityMonitor::ConnectivityMonitor() + { + NMLOG_WARNING("ConnectivityMonitor"); + m_cmRunning = false; + m_notify = true; + m_InternetState = INTERNET_UNKNOWN; + m_switchToInitial = true; + m_wakeupMonitoring = false; + startConnectivityMonitor(); + } + + ConnectivityMonitor::~ConnectivityMonitor() + { + NMLOG_WARNING("~ConnectivityMonitor"); + stopConnectivityMonitor(); + } + + std::vector ConnectivityMonitor::getConnectivityMonitorEndpoints() + { + return m_endpoint.getConnectivityMonitorEndpoints(); + } + + void ConnectivityMonitor::setConnectivityMonitorEndpoints(const std::vector &endpoints) + { + m_endpoint.setConnectivityMonitorEndpoints(endpoints); + } + + Exchange::INetworkManager::InternetStatus ConnectivityMonitor::getInternetState(std::string& interface, Exchange::INetworkManager::IPVersion& ipversion, bool ipVersionNotSpecified) + { + uint8_t ipversionLocal = static_cast(ipVersionNotSpecified ? 2 : ipversion); // 2 = both, 0 = IPv4, 1 = IPv6 + + // if ipversion not specified, check both IP versions and determine it based on the resolved IP address + TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, + NMCONNECTIVITY_CURL_HEAD_REQUEST, ipversionLocal, interface); + + if (interface.empty()) + interface = _instance->getDefaultInterface(); + + return testInternet.getInternetState(); + } + + std::string ConnectivityMonitor::getCaptivePortalURI() + { + if(m_InternetState == INTERNET_CAPTIVE_PORTAL) + { + NMLOG_INFO("captive portal URI = %s", m_captiveURI.c_str()); + return m_captiveURI; + } + + NMLOG_WARNING("No captive portal found !"); + return std::string(""); + } + + bool ConnectivityMonitor::startConnectivityMonitor() + { + if (m_cmRunning) + { + m_wakeupMonitoring = true; + m_cmCv.notify_one(); + NMLOG_DEBUG("connectivity monitor is already running"); + return true; + } + + m_cmRunning = true; + m_cmThrdID = std::thread(&ConnectivityMonitor::connectivityMonitorFunction, this); + + if(_instance != nullptr) { + NMLOG_INFO("connectivity monitor started - eth %s - wlan %s", + _instance->m_ethConnected.load()? "up":"down", _instance->m_wlanConnected.load()? "up":"down"); + } + + NMLOG_INFO("connectivity monitor is started"); + return true; + } + + bool ConnectivityMonitor::stopConnectivityMonitor() + { + m_cmRunning = false; + m_wakeupMonitoring = true; + m_cmCv.notify_one(); + if(m_cmThrdID.joinable()) + m_cmThrdID.join(); + m_InternetState = INTERNET_UNKNOWN; + NMLOG_INFO("connectivity monitor stoped !!!"); + return true; + } + + bool ConnectivityMonitor::switchToInitialCheck() + { + + if(_instance == nullptr) { + NMLOG_WARNING("networkmanagerimplementation instance is null"); + return false; + } + + m_notify = true; + m_switchToInitial = true; + m_wakeupMonitoring = true; + m_cmCv.notify_one(); + + string defaultIface = _instance->getDefaultInterface(); + NMLOG_INFO("switching to initial check - eth %s - wlan %s - default interface %s", + _instance->m_ethConnected.load()? "up":"down", _instance->m_wlanConnected.load()? "up":"down", + defaultIface.empty() ? "none" : defaultIface.c_str()); + + return true; + } + + void ConnectivityMonitor::notifyInternetStatusChangedEvent(Exchange::INetworkManager::InternetStatus newInternetState) + { + static Exchange::INetworkManager::InternetStatus oldState = INTERNET_UNKNOWN; + if(_instance != nullptr) + { + NMLOG_INFO("notifying internet state %s", getInternetStateString(newInternetState)); + Exchange::INetworkManager::InternetStatus newState = newInternetState; + string defaultIface = _instance->getDefaultInterface(); + _instance->ReportInternetStatusChange(oldState, newState, defaultIface); + m_InternetState = newInternetState; + oldState = newState; // 'm_InternetState' not exactly previous state, it may change to unknow when interface changed + } + else + NMLOG_FATAL("NetworkManagerImplementation Instance NULL notifyInternetStatusChange failed."); + } + + void ConnectivityMonitor::connectivityMonitorFunction() + { + int timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; + Exchange::INetworkManager::InternetStatus currentInternetState = INTERNET_NOT_AVAILABLE; + int InitialRetryCount = 0; + m_switchToInitial = true; + m_InternetState = INTERNET_UNKNOWN; + m_notify = true; + + while (m_cmRunning) { + if (nullptr == _instance) + { + NMLOG_DEBUG("Must be right from the constructor; because the _instance is NULL"); + timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; + m_InternetState = INTERNET_NOT_AVAILABLE; + currentInternetState = INTERNET_NOT_AVAILABLE; + InitialRetryCount = 0; + } + // Check if no interfaces are connected + else if (_instance != nullptr && !_instance->m_ethConnected.load() && !_instance->m_wlanConnected.load()) { + NMLOG_DEBUG("no interface connected, no ccm check"); + timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; + m_InternetState = INTERNET_NOT_AVAILABLE; + currentInternetState = INTERNET_NOT_AVAILABLE; + if (InitialRetryCount == 0) + m_notify = true; + InitialRetryCount = 1; + } + else + { + string defaultIface = _instance->getDefaultInterface(); + + if(defaultIface.empty()) + { + NMLOG_WARNING("default interface not set"); + currentInternetState = INTERNET_NOT_AVAILABLE; + if (InitialRetryCount == 0) + m_notify = true; + InitialRetryCount = 1; + } + else if (m_switchToInitial) + { + if (InitialRetryCount == 0) + m_notify = true; + NMLOG_INFO("Initial connectivity check - index:%d, current state:%s, interface:%s", InitialRetryCount, getInternetStateString(currentInternetState), defaultIface.c_str()); + timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; + TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, + NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, defaultIface); + currentInternetState = testInternet.getInternetState(); + + if (currentInternetState == INTERNET_NOT_AVAILABLE) { + NMLOG_DEBUG("interface connected but no internet"); + InitialRetryCount = 1; // continue same check for 5 sec + } + else { + if(currentInternetState == INTERNET_CAPTIVE_PORTAL) + m_captiveURI = testInternet.getCaptivePortal(); + + if (currentInternetState != m_InternetState) { + NMLOG_DEBUG("initial connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState)); + m_InternetState = currentInternetState; + InitialRetryCount = 1; // reset retry count to get continuous 3 same state + m_notify = true; + } + InitialRetryCount++; + } + + if (InitialRetryCount > NM_CONNECTIVITY_MONITOR_RETRY_COUNT) { + m_switchToInitial = false; + m_notify = true; + NMLOG_INFO("switching to ideal ccm check interface: %s", defaultIface.c_str()); + } + } + else + { + // ideal case check every 30 sec happenses when captive portal or limited internet + timeoutInSec = NMCONNECTIVITY_MONITOR_RETRY_INTERVAL; + InitialRetryCount = 0; + + if(m_InternetState != INTERNET_FULLY_CONNECTED) + { + TestConnectivity testInternet(m_endpoint(), NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS, + NMCONNECTIVITY_CURL_HEAD_REQUEST, 2, defaultIface); // check both IP versions + currentInternetState = testInternet.getInternetState(); + + if (currentInternetState == INTERNET_CAPTIVE_PORTAL) // if captive portal found copy the URL + m_captiveURI = testInternet.getCaptivePortal(); + + if (currentInternetState != m_InternetState) + { + NMLOG_INFO("ideal connectivity state change from %s to %s", getInternetStateString(m_InternetState), getInternetStateString(currentInternetState)); + m_switchToInitial = true; + m_notify = true; + InitialRetryCount = 1; + timeoutInSec = NMCONNECTIVITY_MONITOR_MIN_INTERVAL; // retry in 5 sec + } + } + } + } + + if (m_notify) { + m_InternetState = currentInternetState; + notifyInternetStatusChangedEvent(m_InternetState); + m_notify = false; + } + + if (!m_cmRunning) + break; + + // Wait for next interval + std::unique_lock lock(m_cmMutex); + if (m_cmCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [this] { return m_wakeupMonitoring.load(); })) + { + NMLOG_INFO("connectivity monitor received signal. skipping %d sec interval", timeoutInSec); + m_wakeupMonitoring = false; + } + } + } + + } // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/NetworkManagerConnectivity.h b/plugin/NetworkManagerConnectivity.h new file mode 100644 index 000000000..87ca75586 --- /dev/null +++ b/plugin/NetworkManagerConnectivity.h @@ -0,0 +1,159 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "INetworkManager.h" + +enum nsm_connectivity_httpcode { + HttpStatus_response_error = 99, + HttpStatus_200_OK = 200, + HttpStatus_204_No_Content = 204, + HttpStatus_301_Moved_Permanentl = 301, + HttpStatus_302_Found = 302, // captive portal + HttpStatus_307_Temporary_Redirect = 307, + HttpStatus_308_Permanent_Redirect = 308, + HttpStatus_403_Forbidden = 403, + HttpStatus_404_Not_Found = 404, + HttpStatus_511_Authentication_Required = 511 // captive portal RFC 6585 +}; + +#define NMCONNECTIVITY_CURL_HEAD_REQUEST true +#define NMCONNECTIVITY_CURL_GET_REQUEST false +#define NMCONNECTIVITY_MONITOR_CACHE_FILE "/tmp/nm.plugin.endpoints" +#define NMCONNECTIVITY_MONITOR_MIN_INTERVAL 5 // sec +#define NMCONNECTIVITY_MONITOR_RETRY_INTERVAL 30 // sec +#define NMCONNECTIVITY_CURL_REQUEST_TIMEOUT_MS 5000 // ms +#define NM_CONNECTIVITY_MONITOR_RETRY_COUNT 3 // 3 retry + +namespace WPEFramework +{ + namespace Plugin + { +#if 0 + class DnsResolver + { + public: + DnsResolver(std::string url, Exchange::INetworkManager::IPVersion ipversion, int curlErrorCode); + ~DnsResolver(){}; + bool operator()() { return (ipv6Resolved || ipv4Resolved);} + + private: + std::string m_domain{}; + bool ipv4Resolved = false; + bool ipv6Resolved = false; + std::string convertUrIToDomainName(std::string& url); + bool resolveIP(std::string& uri, Exchange::INetworkManager::IPVersion& ipversion); + }; +#endif + /* + * Save user specific endpoint into a cache file and load from the file + * if endpoints are empty in case plugin is restarted. + */ + class EndpointManager { + public: + EndpointManager(); + ~EndpointManager() {} + void writeEndpointsToFile(const std::vector& endpoints); + std::vector readEndpointsFromFile(); + void setConnectivityMonitorEndpoints(const std::vector& endpoints); + std::vector getConnectivityMonitorEndpoints(); + std::vector operator()() { return getConnectivityMonitorEndpoints(); } + + private: + std::string m_CachefilePath; + std::vector m_Endpoints; + std::mutex m_endpointMutex; + }; + + class TestConnectivity + { + TestConnectivity(const TestConnectivity&) = delete; + const TestConnectivity& operator=(const TestConnectivity&) = delete; + + public: + TestConnectivity(const std::vector& endpoints, long timeout_ms, bool headReq, + uint8_t ipversion, std::string interface = ""); + ~TestConnectivity(){} + std::string getCaptivePortal() {return captivePortalURI;} + Exchange::INetworkManager::InternetStatus getInternetState(){return internetSate;} + int getCurlError(){return curlErrorCode;} + private: + Exchange::INetworkManager::InternetStatus checkCurlResponse(const std::vector& endpoints, + long timeout_ms, bool headReq, uint8_t ipversion, std::string interface); + Exchange::INetworkManager::InternetStatus checkInternetStateFromResponseCode(const std::vector& responses); + std::string captivePortalURI; + std::string m_deviceModel{}; + std::string m_buildVersion{}; + Exchange::INetworkManager::InternetStatus internetSate; + int curlErrorCode = 0; + template + void curlSetOpt(CURL *curl, CURLoption option, curlValue value) + { + CURLcode response = curl_easy_setopt(curl, option, value); + if (response != CURLE_OK) { + NMLOG_ERROR("Error setting option %d with error: %s", option, curl_easy_strerror(response)); + } + return; + } + }; + + class ConnectivityMonitor + { + public: + ConnectivityMonitor(); + ~ConnectivityMonitor(); + bool stopConnectivityMonitor(); + bool startConnectivityMonitor(); + bool switchToInitialCheck(); + void setConnectivityMonitorEndpoints(const std::vector &endpoints); + std::vector getConnectivityMonitorEndpoints(); + Exchange::INetworkManager::InternetStatus getInternetState(std::string& interface, Exchange::INetworkManager::IPVersion& ipversion, bool ipVersionNotSpecified = false); + std::string getCaptivePortalURI(); + + private: + ConnectivityMonitor(const ConnectivityMonitor&) = delete; + ConnectivityMonitor& operator=(const ConnectivityMonitor&) = delete; + void connectivityMonitorFunction(); + void notifyInternetStatusChangedEvent(Exchange::INetworkManager::InternetStatus newState); + /* connectivity monitor */ + std::thread m_cmThrdID; + std::atomic m_cmRunning; + std::condition_variable m_cmCv; + std::mutex m_cmMutex; + std::atomic m_notify; + std::atomic m_switchToInitial; + std::atomic m_wakeupMonitoring; + std::string m_captiveURI; + std::atomic m_InternetState; + /* manages endpoints */ + EndpointManager m_endpoint; + }; + } // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/NetworkManagerConnectivityClient.cpp b/plugin/NetworkManagerConnectivityClient.cpp new file mode 100644 index 000000000..db8eeaaa8 --- /dev/null +++ b/plugin/NetworkManagerConnectivityClient.cpp @@ -0,0 +1,342 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManagerConnectivityClient.h" +#include "NetworkManagerLogger.h" +#include +#include + +using namespace WPEFramework; +using namespace WPEFramework::Exchange; +using namespace WPEFramework::Plugin; + +// --------------------------------------------------------------------------- +// NetworkManagerConnectivityClient +// --------------------------------------------------------------------------- + +NetworkManagerConnectivityClient::NetworkManagerConnectivityClient() + : mNotification(*this) +{ + NMLOG_INFO("ConnectivityCheckMgr client created; opening link asynchronously"); + mOpenThread = std::thread(&NetworkManagerConnectivityClient::openThreadLoop, this); +} + +NetworkManagerConnectivityClient::~NetworkManagerConnectivityClient() +{ + NMLOG_INFO("shutting down"); + { + std::lock_guard lock(mOpenMutex); + mStopOpenThread = true; + } + mOpenCv.notify_one(); + if (mOpenThread.joinable()) { + mOpenThread.join(); + } + + // Unregister without holding mLock to avoid deadlock when a notification + // arrives concurrently and tries to acquire mLock inside notifyInternetStatusChanged. + unregisterEvents(); + { + std::lock_guard lock(mLock); + if (mConnectivity != nullptr) { + mConnectivity->Release(); + mConnectivity = nullptr; + } + } + Close(Core::infinite); +} + +void NetworkManagerConnectivityClient::openThreadLoop() +{ + constexpr auto retryInterval = std::chrono::seconds(5); + + while (!mStopOpenThread.load()) { + NMLOG_INFO("connecting to ConnectivityCheckMgr"); + const uint32_t r = Open(RPC::CommunicationTimeOut, Connector(), "org.rdk.ConnectivityCheckMgr"); + if (r == Core::ERROR_NONE) { + // Connected; Operational() is invoked by the framework when the proxy is ready. + NMLOG_INFO("link to ConnectivityCheckMgr opened"); + return; + } + + NMLOG_WARNING("failed to open link to ConnectivityCheckMgr (error %u); retrying", r); + + std::unique_lock lock(mOpenMutex); + mOpenCv.wait_for(lock, retryInterval, [this] { return mStopOpenThread.load(); }); + } +} + +bool NetworkManagerConnectivityClient::IsValid() const +{ + LOG_ENTRY_FUNCTION(); + std::lock_guard lock(mLock); + return mConnectivity != nullptr; +} + +void NetworkManagerConnectivityClient::Operational(bool upAndRunning) +{ + NMLOG_DEBUG("Operational(%s)", upAndRunning ? "true" : "false"); + if (upAndRunning) { + { + std::lock_guard lock(mLock); + if (mConnectivity != nullptr) { + return; // already connected + } + mConnectivity = Interface(); + mHasCachedStatus = false; + mCachedStatus = Exchange::INetworkManager::INTERNET_UNKNOWN; + mCachedReason.clear(); + } + // Register without holding mLock: Register() can dispatch a synchronous + // notification on some Thunder builds, which would deadlock if mLock is held. + registerEvents(); + } else { + // Unregister without holding mLock for the same reason. + unregisterEvents(); + std::lock_guard lock(mLock); + if (mConnectivity != nullptr) { + mConnectivity->Release(); + mConnectivity = nullptr; + } + mHasCachedStatus = false; + mCachedStatus = Exchange::INetworkManager::INTERNET_UNKNOWN; + mCachedReason.clear(); + } +} + +Exchange::IConnectivityCheck* NetworkManagerConnectivityClient::acquireInterface() const +{ + std::lock_guard lock(mLock); + if (mConnectivity != nullptr) { + mConnectivity->AddRef(); + } + return mConnectivity; +} + +void NetworkManagerConnectivityClient::SetInternetStatusChangeHandler(InternetStatusChangeHandler handler) +{ + std::unique_lock lock(mLock); + mHandlerDrainCv.wait(lock, [this] { return !mHandlerClearInProgress; }); + + if (handler) { + mInternetStatusChangeHandler = std::move(handler); + return; + } + + // Clearing is a drain barrier. It must not be called by the active handler. + mHandlerClearInProgress = true; + mInternetStatusChangeHandler = nullptr; + mHandlerDrainCv.wait(lock, [this] { return mHandlersInFlight == 0; }); + mHandlerClearInProgress = false; + lock.unlock(); + mHandlerDrainCv.notify_all(); +} + +void NetworkManagerConnectivityClient::registerEvents() +{ + Exchange::IConnectivityCheck* connectivity = nullptr; + { + std::lock_guard lock(mLock); + if (mConnectivity == nullptr || mNotificationRegistered) { + return; + } + // Claim the flag before dropping mLock so a concurrent caller cannot register twice. + mNotificationRegistered = true; + connectivity = mConnectivity; + connectivity->AddRef(); + } + + const uint32_t r = connectivity->Register(&mNotification); + connectivity->Release(); + + if (r != Core::ERROR_NONE) { + NMLOG_ERROR("ConnectivityCheckMgr register(notification) failed (%u)", r); + std::lock_guard lock(mLock); + mNotificationRegistered = false; + return; + } + + NMLOG_INFO("registered for ConnectivityCheckMgr internet-status notifications"); +} + +void NetworkManagerConnectivityClient::unregisterEvents() +{ + Exchange::IConnectivityCheck* connectivity = nullptr; + { + std::lock_guard lock(mLock); + if (!mNotificationRegistered) { + return; + } + mNotificationRegistered = false; + if (mConnectivity == nullptr) { + return; + } + connectivity = mConnectivity; + connectivity->AddRef(); + } + + if (const uint32_t r = connectivity->Unregister(&mNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("ConnectivityCheckMgr unregister(notification) failed (%u)", r); + } + connectivity->Release(); +} + +void NetworkManagerConnectivityClient::notifyInternetStatusChanged(Exchange::IConnectivityCheck::InternetStatus status, + const std::string& reason) +{ + const NmInternetStatus mapped = mapStatus(status); + // Only NO_INTERNET carries a meaningful reason; keep the handler consistent with the cache. + const std::string filteredReason = (status == Exchange::IConnectivityCheck::NO_INTERNET) ? reason : std::string(); + + InternetStatusChangeHandler handler; + { + std::lock_guard lock(mLock); + mCachedStatus = mapped; + mCachedReason = filteredReason; + mHasCachedStatus = true; + if (mHandlerClearInProgress) { + return; + } + handler = mInternetStatusChangeHandler; + if (handler) { + ++mHandlersInFlight; + } + } + + if (!handler) { + return; + } + + struct HandlerCompletion { + explicit HandlerCompletion(NetworkManagerConnectivityClient& client) + : client(client) {} + ~HandlerCompletion() + { + client.completeInternetStatusChangeHandler(); + } + + NetworkManagerConnectivityClient& client; + } completion(*this); + + handler(mapped, filteredReason); +} + +void NetworkManagerConnectivityClient::completeInternetStatusChangeHandler() +{ + std::lock_guard lock(mLock); + --mHandlersInFlight; + if (mHandlersInFlight == 0) { + mHandlerDrainCv.notify_all(); + } +} + +void NetworkManagerConnectivityClient::Notification::OnInternetStatusChange( + const Exchange::IConnectivityCheck::InternetStatus status, + const string& reason) +{ + mParent.notifyInternetStatusChanged(status, reason); +} + +NetworkManagerConnectivityClient::NmInternetStatus +NetworkManagerConnectivityClient::mapStatus(Exchange::IConnectivityCheck::InternetStatus status) +{ + switch (status) { + case Exchange::IConnectivityCheck::NO_INTERNET: return Exchange::INetworkManager::INTERNET_NOT_AVAILABLE; + case Exchange::IConnectivityCheck::LIMITED_INTERNET: return Exchange::INetworkManager::INTERNET_LIMITED; + case Exchange::IConnectivityCheck::CAPTIVE_PORTAL: return Exchange::INetworkManager::INTERNET_CAPTIVE_PORTAL; + case Exchange::IConnectivityCheck::FULLY_CONNECTED: return Exchange::INetworkManager::INTERNET_FULLY_CONNECTED; + case Exchange::IConnectivityCheck::UNKNOWN: + default: return Exchange::INetworkManager::INTERNET_UNKNOWN; + } +} + +NetworkManagerConnectivityClient::NmInternetStatus +NetworkManagerConnectivityClient::getInternetState() +{ + std::string reason; + return getInternetState(reason); +} + +NetworkManagerConnectivityClient::NmInternetStatus +NetworkManagerConnectivityClient::getInternetState(std::string& reason) +{ + LOG_ENTRY_FUNCTION(); + reason.clear(); + + { + std::lock_guard lock(mLock); + if (mHasCachedStatus) { + reason = mCachedReason; + return mCachedStatus; + } + } + + // Cold path only: no notification received yet, so seed the cache once. + Exchange::IConnectivityCheck* connectivity = acquireInterface(); + if (connectivity == nullptr) { + NMLOG_WARNING("ConnectivityCheckMgr not available; returning INTERNET_UNKNOWN"); + return Exchange::INetworkManager::INTERNET_UNKNOWN; + } + + Exchange::IConnectivityCheck::StatusInfo info{}; + const uint32_t r = connectivity->GetInternetStatus(info); + connectivity->Release(); + + if (r != Core::ERROR_NONE) { + NMLOG_ERROR("ConnectivityCheckMgr GetInternetStatus failed (%u)", r); + return Exchange::INetworkManager::INTERNET_UNKNOWN; + } + + const NmInternetStatus mapped = mapStatus(info.status); + if (info.status == Exchange::IConnectivityCheck::NO_INTERNET) { + reason = info.reason; + } + + { + std::lock_guard lock(mLock); + // A notification may have landed while the RPC was in flight; it is newer. + if (!mHasCachedStatus) { + mCachedStatus = mapped; + mCachedReason = reason; + mHasCachedStatus = true; + } + } + return mapped; +} + +std::string NetworkManagerConnectivityClient::getCaptivePortalURI() +{ + LOG_ENTRY_FUNCTION(); + std::string uri; + + Exchange::IConnectivityCheck* connectivity = acquireInterface(); + if (connectivity == nullptr) { + NMLOG_WARNING("ConnectivityCheckMgr not available; returning empty captive-portal URI"); + return uri; + } + + const uint32_t r = connectivity->GetCaptivePortalURI(uri); + connectivity->Release(); + + if (r != Core::ERROR_NONE) { + NMLOG_ERROR("ConnectivityCheckMgr GetCaptivePortalURI failed (%u)", r); + uri.clear(); + } + return uri; +} diff --git a/plugin/NetworkManagerConnectivityClient.h b/plugin/NetworkManagerConnectivityClient.h new file mode 100644 index 000000000..09b9c5d59 --- /dev/null +++ b/plugin/NetworkManagerConnectivityClient.h @@ -0,0 +1,145 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include "Module.h" +#include "INetworkManager.h" +// IConnectivityCheck is generated/installed by entservices-cpc-apis under interfaces_cpc, +// not the standard Thunder interfaces/ path. +#include +#include +#include +#include +#include +#include +#include + +namespace WPEFramework { +namespace Plugin { + +/** + * COM-RPC client that delegates internet-connectivity queries to the + * ConnectivityCheckMgr plugin (org.rdk.ConnectivityCheckMgr). + * + * Because the delegation lives in the out-of-process implementation, both the + * JSON-RPC surface (shell -> implementation) and direct COM-RPC callers of + * INetworkManager::IsConnectedToInternet / GetCaptivePortalURI are served + * consistently. + * + * Mirrors NetworkManagerPowerClient: + * - Inherits SmartInterfaceType for automatic + * connect / reconnect and Operational() lifecycle callbacks. + * + * Lifecycle: + * Construction -> Open() connects to ConnectivityCheckMgr (async). + * Operational(true) -> acquires the proxy; IsValid() returns true. + * Operational(false) -> releases the proxy. + * Destruction -> Close(). + * + * All queries fall back to INTERNET_UNKNOWN / empty when the plugin is not + * available, so callers never crash on a boot-order race or NM/CCM restart. + */ +class NetworkManagerConnectivityClient : protected RPC::SmartInterfaceType { +public: + using NmInternetStatus = Exchange::INetworkManager::InternetStatus; + using InternetStatusChangeHandler = std::function; + + NetworkManagerConnectivityClient(); + ~NetworkManagerConnectivityClient() override; + + NetworkManagerConnectivityClient(const NetworkManagerConnectivityClient&) = delete; + NetworkManagerConnectivityClient& operator=(const NetworkManagerConnectivityClient&) = delete; + + /** Returns true when the ConnectivityCheckMgr COMRPC proxy is available. */ + bool IsValid() const; + + /** Delegated internet status, mapped to NetworkManager's InternetStatus. */ + NmInternetStatus getInternetState(); + + /** Delegated internet status and its current NO_INTERNET reason. */ + NmInternetStatus getInternetState(std::string& reason); + + /** Delegated captive-portal URI (empty when unavailable / not captive). */ + std::string getCaptivePortalURI(); + + /** Sets/clears callback invoked when ConnectivityCheckMgr publishes internet-status changes. */ + void SetInternetStatusChangeHandler(InternetStatusChangeHandler handler); + +private: + class Notification : public Exchange::IConnectivityCheck::INotification { + public: + explicit Notification(NetworkManagerConnectivityClient& parent) + : mParent(parent) {} + + void OnInternetStatusChange(const Exchange::IConnectivityCheck::InternetStatus status, + const string& reason) override; + + BEGIN_INTERFACE_MAP(Notification) + INTERFACE_ENTRY(Exchange::IConnectivityCheck::INotification) + END_INTERFACE_MAP + + private: + NetworkManagerConnectivityClient& mParent; + }; + + // SmartInterfaceType lifecycle callback. + void Operational(bool upAndRunning) override; + + void registerEvents(); + void unregisterEvents(); + void notifyInternetStatusChanged(Exchange::IConnectivityCheck::InternetStatus status, + const std::string& reason); + void completeInternetStatusChangeHandler(); + void openThreadLoop(); + + /* Returns an AddRef'd proxy (nullptr when unavailable) so the caller can invoke + * ConnectivityCheckMgr without holding mLock across the blocking COM-RPC call. */ + Exchange::IConnectivityCheck* acquireInterface() const; + + // 1:1 mapping ConnectivityCheckMgr InternetStatus -> NetworkManager InternetStatus. + static NmInternetStatus mapStatus(Exchange::IConnectivityCheck::InternetStatus status); + + mutable std::mutex mLock; + Exchange::IConnectivityCheck* mConnectivity{nullptr}; + Core::Sink mNotification; + InternetStatusChangeHandler mInternetStatusChangeHandler; + std::condition_variable mHandlerDrainCv; + uint32_t mHandlersInFlight{0}; + bool mHandlerClearInProgress{false}; + bool mNotificationRegistered{false}; // guarded by mLock + + // Status cache kept current by OnInternetStatusChange, so the hot + // isConnectedToInternet path needs no cross-process round trip. + NmInternetStatus mCachedStatus{Exchange::INetworkManager::INTERNET_UNKNOWN}; + std::string mCachedReason; + bool mHasCachedStatus{false}; + + // Open() runs on its own thread so plugin Configure() never blocks on, or + // re-enters, a ConnectivityCheckMgr that is not activated yet. + std::thread mOpenThread; + std::mutex mOpenMutex; + std::condition_variable mOpenCv; + std::atomic mStopOpenThread{false}; + + friend class NetworkManagerConnectivityClientTestAccess; +}; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/NetworkManagerImplementation.cpp b/plugin/NetworkManagerImplementation.cpp new file mode 100644 index 000000000..d91127295 --- /dev/null +++ b/plugin/NetworkManagerImplementation.cpp @@ -0,0 +1,1863 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include "NetworkManagerImplementation.h" + +#ifdef USE_CONNECTIVITYCHECKMGR +#include +#include "rfcapi.h" +#endif + +#if USE_TELEMETRY +#include "NetworkManagerJsonEnum.h" +#include +#endif + +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using namespace NetworkManagerLogger; + +#define CIDR_NETMASK_IP_LEN 32 +#define SSID_COMMAND "wpa_cli status" +#define SIGNAL_POLL_COMMAND "wpa_cli signal_poll" + +namespace WPEFramework +{ + namespace Plugin + { + extern NetworkManagerImplementation* _instance; + SERVICE_REGISTRATION(NetworkManagerImplementation, NETWORKMANAGER_MAJOR_VERSION, NETWORKMANAGER_MINOR_VERSION, NETWORKMANAGER_PATCH_VERSION); + + NetworkManagerImplementation::NetworkManagerImplementation() + { + /* Initialize STUN Endpoints */ + m_stunEndpoint = "stun.l.google.com"; + m_stunPort = 19302; + m_stunBindTimeout = 30; + m_stunCacheTimeout = 0; + m_defaultInterface = ""; + m_publicIP = ""; + m_ethConnected.store(false); + m_wlanConnected.store(false); + m_ethEnabled.store(false); + m_wlanEnabled.store(false); + m_ethDisconnectedForSleep.store(false); + m_wlanDisconnectedForSleep.store(false); + + /* Set NetworkManager Out-Process name to be NWMgrPlugin */ + Core::ProcessInfo().Name("NWMgrPlugin"); + + /* Initialize Network Manager */ + NetworkManagerLogger::Init(); + SYSLOG(::WPEFramework::Logging::Startup, (_T("NWMgrPlugin Out-Of-Process Instantiation; SHA: ") _T(EXPAND_AND_QUOTE(PLUGIN_BUILD_REFERENCE)))); + m_processMonThread = std::thread(&NetworkManagerImplementation::processMonitor, this, NM_PROCESS_MONITOR_INTERVAL_SEC); + + /* Start dedicated event dispatch thread */ + m_eventThreadStop.store(false); + m_eventThread = std::thread(&NetworkManagerImplementation::eventThreadFunction, this); + NMLOG_INFO("Event dispatch thread started"); + + #if USE_TELEMETRY + // Initialize Telemetry T2 for NwMgrPlugin + t2_init("NwMgrPlugin"); + #endif + } + + NetworkManagerImplementation::~NetworkManagerImplementation() + { + NMLOG_INFO("NetworkManager Out-Of-Process Shutdown/Cleanup"); + m_powerClient.reset(); +#ifdef USE_CONNECTIVITYCHECKMGR + if (connectivityClient) { + // Clear the handler first so no in-flight callback can reach this + // (partially-destroyed) object, then tear down the COM-RPC client. + connectivityClient->SetInternetStatusChangeHandler(nullptr); + connectivityClient.reset(); + } +#endif + if(!m_useConnectivityCheckMgr) + { + connectivityMonitor.stopConnectivityMonitor(); + } + _instance = nullptr; + platform_deinit(); + if(m_registrationThread.joinable()) + { + m_registrationThread.join(); + } + /* Stop WiFi Signal Monitoring */ + stopWiFiSignalQualityMonitor(); + + /* Stop event dispatch thread */ + { + std::unique_lock lock(m_eventMutex); + m_eventThreadStop.store(true); + m_eventCondVar.notify_one(); + } + if (m_eventThread.joinable()) { + m_eventThread.join(); + NMLOG_INFO("Event dispatch thread stopped"); + } + + { + std::unique_lock lock(m_processMonMutex); + m_processMonThreadStop.store(true); + m_processMonCondVar.notify_one(); + } + + if (m_processMonThread.joinable()) { + m_processMonThread.join(); + } + } + /** + * Register a notification callback + */ + uint32_t NetworkManagerImplementation::Register(INetworkManager::INotification *notification) + { + LOG_ENTRY_FUNCTION(); + _notificationLock.Lock(); + + // Make sure we can't register the same notification callback multiple times + if (std::find(_notificationCallbacks.begin(), _notificationCallbacks.end(), notification) == _notificationCallbacks.end()) { + _notificationCallbacks.push_back(notification); + notification->AddRef(); + } + + _notificationLock.Unlock(); + + return Core::ERROR_NONE; + } + + /** + * Unregister a notification callback + */ + uint32_t NetworkManagerImplementation::Unregister(INetworkManager::INotification *notification) + { + LOG_ENTRY_FUNCTION(); + _notificationLock.Lock(); + + // Make sure we can't register the same notification callback multiple times + auto itr = std::find(_notificationCallbacks.begin(), _notificationCallbacks.end(), notification); + if (itr != _notificationCallbacks.end()) { + (*itr)->Release(); + _notificationCallbacks.erase(itr); + } + + _notificationLock.Unlock(); + + return Core::ERROR_NONE; + } + + uint32_t NetworkManagerImplementation::Configure(const string configLine) + { + LOG_ENTRY_FUNCTION(); + Configuration config; + if(configLine.empty()) + { + SYSLOG(::WPEFramework::Logging::Shutdown, (_T("config line is empty"))); + return Core::ERROR_GENERAL; + } + else + { + SYSLOG(::WPEFramework::Logging::Startup, (_T("Loading incoming configuration"))); + config.FromString(configLine); + } + + NetworkManagerLogger::SetLevel(static_cast (config.loglevel.Value())); + NMLOG_DEBUG("loglevel %d", config.loglevel.Value()); + + /* Resolve the connectivity backend at runtime (replaces the old + * USE_CONNECTIVITY_CHECK_MGR compile-time macro). */ + m_useConnectivityCheckMgr = resolveConnectivityCheckMgrEnabled(config); +#ifdef USE_CONNECTIVITYCHECKMGR + if(m_useConnectivityCheckMgr) + { + /* Stop the built-in monitor (started by its constructor) so it does + * not run alongside the delegation client. */ + connectivityMonitor.stopConnectivityMonitor(); + if(!connectivityClient) + connectivityClient.reset(new NetworkManagerConnectivityClient()); + connectivityClient->SetInternetStatusChangeHandler( + [this](const Exchange::INetworkManager::InternetStatus status, const std::string& reason) { + OnDelegatedInternetStatusChange(status, reason); + }); + { + std::lock_guard lock(m_bridgedStatusMutex); + m_hasBridgedInternetStatus = false; + m_lastBridgedInternetStatus = Exchange::INetworkManager::INTERNET_UNKNOWN; + } + NMLOG_INFO("Connectivity delegated to ConnectivityCheckMgr (runtime selection)"); + } +#endif + + /* STUN configuration copy */ + m_stunEndpoint = config.stun.stunEndpoint.Value(); + m_stunPort = config.stun.port.Value(); + m_stunBindTimeout = config.stun.interval.Value(); + + NMLOG_INFO("stun endpoint %s", m_stunEndpoint.c_str()); + NMLOG_DEBUG("stun port %d", m_stunPort); + NMLOG_DEBUG("stun interval %d", m_stunBindTimeout); + + + /* Connectivity monitor endpoints configuration */ + std::vector connectEndpts; + if(!config.connectivityConf.endpoint_1.Value().empty()) { + NMLOG_INFO("connectivity endpoint 1 %s", config.connectivityConf.endpoint_1.Value().c_str()); + connectEndpts.push_back(config.connectivityConf.endpoint_1.Value().c_str()); + } + if(!config.connectivityConf.endpoint_2.Value().empty()) { + NMLOG_DEBUG("connectivity endpoint 2 %s", config.connectivityConf.endpoint_2.Value().c_str()); + connectEndpts.push_back(config.connectivityConf.endpoint_2.Value().c_str()); + } + if(!config.connectivityConf.endpoint_3.Value().empty()) { + NMLOG_DEBUG("connectivity endpoint 3 %s", config.connectivityConf.endpoint_3.Value().c_str()); + connectEndpts.push_back(config.connectivityConf.endpoint_3.Value().c_str()); + } + if(!config.connectivityConf.endpoint_4.Value().empty()) { + NMLOG_DEBUG("connectivity endpoint 4 %s", config.connectivityConf.endpoint_4.Value().c_str()); + connectEndpts.push_back(config.connectivityConf.endpoint_4.Value().c_str()); + } + if(!config.connectivityConf.endpoint_5.Value().empty()) { + NMLOG_DEBUG("connectivity endpoint 5 %s", config.connectivityConf.endpoint_5.Value().c_str()); + connectEndpts.push_back(config.connectivityConf.endpoint_5.Value().c_str()); + } + + /* Only seed endpoints when none are active; endpoints restored from the + * EndpointManager cache on restart must not be overwritten. */ + if (!m_useConnectivityCheckMgr && connectivityMonitor.getConnectivityMonitorEndpoints().size() < 1) + { + if (connectEndpts.size() < 1) + { + std::vector backup; + NMLOG_INFO("Connectivity endpoints are empty in config; use the default"); + backup.push_back("http://clients3.google.com/generate_204"); + connectivityMonitor.setConnectivityMonitorEndpoints(backup); + } + else + { + NMLOG_INFO("Use the connectivity endpoint from config"); + connectivityMonitor.setConnectivityMonitorEndpoints(connectEndpts); + } + } + + /* As all the configuration is set, lets instantiate platform */ + NetworkManagerImplementation::platform_init(); + /* change gnome networkmanager or netsrvmgr logg level */ + NetworkManagerImplementation::platform_logging(static_cast (config.loglevel.Value())); + m_powerClient.reset(new NetworkManagerPowerClient(*this)); + return(Core::ERROR_NONE); + } + + /* @brief Resolve whether internet-connectivity queries are delegated to the + * ConnectivityCheckMgr plugin. Precedence: RFC feature flag (when the + * RFC API is compiled in) -> config-line fallback -> default false. */ + bool NetworkManagerImplementation::resolveConnectivityCheckMgrEnabled(const Configuration& config) const + { + LOG_ENTRY_FUNCTION(); +#ifdef USE_CONNECTIVITYCHECKMGR + RFC_ParamData_t rfcParam = {0}; + WDMP_STATUS wdmpStatus = getRFCParameter(const_cast("NetworkManager"), + "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.ConnectivityCheckMgr.Enable", + &rfcParam); + if (wdmpStatus == WDMP_SUCCESS || wdmpStatus == WDMP_ERR_DEFAULT_VALUE) + { + bool enabled = (0 == strcasecmp(rfcParam.value, "true")); + NMLOG_INFO("RFC ConnectivityCheckMgr.Enable = '%s' -> %s", rfcParam.value, + enabled ? "delegate" : "internal monitor"); + return enabled; + } +#else + (void)config; + NMLOG_INFO("ConnectivityCheckMgr delegation not compiled in; using built-in monitor"); +#endif + return false; + } + + /* @brief Get STUN Endpoint to be used for identifying Public IP */ + uint32_t NetworkManagerImplementation::GetStunEndpoint (string &endpoint /* @out */, uint32_t& port /* @out */, uint32_t& bindTimeout /* @out */, uint32_t& cacheTimeout /* @out */) const + { + LOG_ENTRY_FUNCTION(); + endpoint = m_stunEndpoint; + port = m_stunPort; + bindTimeout = m_stunBindTimeout; + cacheTimeout = m_stunCacheTimeout; + return Core::ERROR_NONE; + } + + /* @brief Set STUN Endpoint to be used to identify Public IP */ + uint32_t NetworkManagerImplementation::SetStunEndpoint (string const endpoint /* @in */, const uint32_t port /* @in */, const uint32_t bindTimeout /* @in */, const uint32_t cacheTimeout /* @in */) + { + LOG_ENTRY_FUNCTION(); + + // If no parameters are provided, return error + if (endpoint.empty() && port == 0 && bindTimeout == 0 && cacheTimeout == 0) { + NMLOG_WARNING("No parameters provided to update STUN Endpoint"); + return Core::ERROR_BAD_REQUEST; + } + + // If port is provided but is 0, reject as invalid + if (port == 0 && !endpoint.empty()) { + NMLOG_WARNING("Invalid STUN port: 0"); + return Core::ERROR_BAD_REQUEST; + } + + if (!endpoint.empty()) + m_stunEndpoint = endpoint; + + if (0 != port) + m_stunPort = port; + + if (0 != bindTimeout) + m_stunBindTimeout = bindTimeout; + + if (0 != cacheTimeout) + m_stunCacheTimeout = cacheTimeout; + + return Core::ERROR_NONE; + } + + /* @brief Get ConnectivityTest Endpoints */ + uint32_t NetworkManagerImplementation::GetConnectivityTestEndpoints(IStringIterator*& endpoints/* @out */) const + { + LOG_ENTRY_FUNCTION(); + /* Endpoints are owned by ConnectivityCheckMgr when delegation is active. */ + if(m_useConnectivityCheckMgr) + { + NMLOG_WARNING("GetConnectivityTestEndpoints is not supported while connectivity is delegated to ConnectivityCheckMgr"); + return Core::ERROR_NOT_SUPPORTED; + } + + std::vector tmpEndpoints; + tmpEndpoints = connectivityMonitor.getConnectivityMonitorEndpoints(); + endpoints = (Core::Service::Create(tmpEndpoints)); + if(endpoints == nullptr) { + return Core::ERROR_GENERAL; + } + + return Core::ERROR_NONE; + } + + /* @brief Set ConnectivityTest Endpoints */ + uint32_t NetworkManagerImplementation::SetConnectivityTestEndpoints(IStringIterator* const endpoints /* @in */) + { + LOG_ENTRY_FUNCTION(); + /* Endpoints are owned by ConnectivityCheckMgr when delegation is active. */ + if(m_useConnectivityCheckMgr) + { + NMLOG_WARNING("SetConnectivityTestEndpoints is not supported while connectivity is delegated to ConnectivityCheckMgr"); + return Core::ERROR_NOT_SUPPORTED; + } + + std::vector tmpEndpoints; + + if(endpoints && (endpoints->Count() >= 1)) + { + string endpoint{}; + while(endpoints->Next(endpoint)) + { + /* The url must be atleast 7 letters to be a valid `http://` url */ + if(!endpoint.empty() && endpoint.size() > 7) + { + tmpEndpoints.push_back(endpoint); + } + } + connectivityMonitor.setConnectivityMonitorEndpoints(tmpEndpoints); + } + return Core::ERROR_NONE; + } + + /* @brief Get Internet Connectivty Status */ + uint32_t NetworkManagerImplementation::IsConnectedToInternet(string &ipversion /* @inout */, string &interface /* @inout */, InternetStatus &result /* @out */, string& reason /* @out */) + { + LOG_ENTRY_FUNCTION(); + reason.clear(); + Exchange::INetworkManager::IPVersion curlIPversion = Exchange::INetworkManager::IP_ADDRESS_V4; + bool ipVersionNotSpecified = false; + + if(!ipversion.empty() && ipversion != "IPv4" && ipversion != "IPv6") + { + NMLOG_WARNING("Invalid Ipversion argument %s", ipversion.c_str()); + return Core::ERROR_BAD_REQUEST; + } + else + { + if(ipversion == "IPv4") + curlIPversion = Exchange::INetworkManager::IP_ADDRESS_V4; + else if(ipversion == "IPv6") + curlIPversion = Exchange::INetworkManager::IP_ADDRESS_V6; + else + ipVersionNotSpecified = true; + } + + if(!interface.empty()&& interface != "wlan0" && interface != "eth0") + { + NMLOG_WARNING("Invalid Interface argument %s", interface.c_str()); + return Core::ERROR_BAD_REQUEST; + } + +#ifdef USE_CONNECTIVITYCHECKMGR + if(m_useConnectivityCheckMgr) + { + (void)ipVersionNotSpecified; + result = connectivityClient ? connectivityClient->getInternetState(reason) + : Exchange::INetworkManager::INTERNET_UNKNOWN; + } + else +#endif + { + result = connectivityMonitor.getInternetState(interface, curlIPversion, ipVersionNotSpecified); + } + if (Exchange::INetworkManager::IP_ADDRESS_V6 == curlIPversion) + ipversion = "IPv6"; + else + ipversion = "IPv4"; + + if(interface.empty()) + interface = getDefaultInterface(); + + return Core::ERROR_NONE; + } + + /* @brief Get Authentication URL if the device is behind Captive Portal */ + uint32_t NetworkManagerImplementation::GetCaptivePortalURI(string &uri /* @out */) const + { + LOG_ENTRY_FUNCTION(); +#ifdef USE_CONNECTIVITYCHECKMGR + if(m_useConnectivityCheckMgr) + uri = connectivityClient ? connectivityClient->getCaptivePortalURI() : std::string(); + else +#endif + uri = connectivityMonitor.getCaptivePortalURI(); + return Core::ERROR_NONE; + } + + /* @brief Get the active Interface used for external world communication */ + uint32_t NetworkManagerImplementation::GetPrimaryInterface (string& interface /* @out */) + { + LOG_ENTRY_FUNCTION(); + if(m_ethEnabled.load() && m_ethConnected.load()) + interface = "eth0"; + else if(m_wlanEnabled.load() && m_wlanConnected.load()) + interface = "wlan0"; + else + interface = ""; // no active interface + + NMLOG_DEBUG("Primary interface: %s, eth0: [enabled=%d, connected=%d], wlan0: [enabled=%d, connected=%d]", + interface.c_str(), m_ethEnabled.load(), m_ethConnected.load(), m_wlanEnabled.load(), m_wlanConnected.load()); + + setDefaultInterface(interface); + return Core::ERROR_NONE; + } + + /* @brief Get the Public IP used for external world communication */ + uint32_t NetworkManagerImplementation::GetPublicIP (string& interface /* @inout */, string &ipversion /* @inout */, string& ipaddress /* @out */) + { + LOG_ENTRY_FUNCTION(); + stun::bind_result result; + bool isIPv6 = (ipversion == "IPv6"); + + // Either Interface must be connected to get the public IP + if (!(m_ethConnected.load() || m_wlanConnected.load())) + { + NMLOG_WARNING("No interface Connected"); + return Core::ERROR_GENERAL; + } + + stun::protocol proto (isIPv6 ? stun::protocol::af_inet6 : stun::protocol::af_inet); + if(stunClient.bind(m_stunEndpoint, m_stunPort, interface, proto, m_stunBindTimeout, m_stunCacheTimeout, result)) + { + if (isIPv6) + ipversion = "IPv6"; + else + ipversion = "IPv4"; + + if (interface.empty()) + interface = getDefaultInterface(); + + ipaddress = result.public_ip; +#if USE_TELEMETRY + if(ipversion == "IPv4") + { + NMLOG_INFO("NM_PUBLIC_IPV4 = %s", ipaddress.c_str()); + logTelemetry("NM_PUBLIC_IPV4", ipaddress); + } + else + { + NMLOG_INFO("NM_PUBLIC_IPV6 = %s", ipaddress.c_str()); + logTelemetry("NM_PUBLIC_IPV6", ipaddress); + } +#endif + return Core::ERROR_NONE; + } + else + { + NMLOG_ERROR("stun bind failed for endpoint %s:%d", m_stunEndpoint.c_str(), m_stunPort); + return Core::ERROR_GENERAL; + } + } + + /* @brief Set the network manager plugin log level */ + uint32_t NetworkManagerImplementation::SetLogLevel(const Logging& level /* @in */) + { + LOG_ENTRY_FUNCTION(); + NetworkManagerLogger::SetLevel(static_cast(level)); + platform_logging(static_cast(level)); + NMLOG_DEBUG("loglevel %d", level); + return Core::ERROR_NONE; + } + + /* @brief Get the network manager plugin log level */ + uint32_t NetworkManagerImplementation::GetLogLevel(Logging& level /* @out */) + { + LOG_ENTRY_FUNCTION(); + LogLevel inLevel; + NetworkManagerLogger::GetLevel(inLevel); + + level = static_cast(inLevel); + return Core::ERROR_NONE; + } + + /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ + uint32_t NetworkManagerImplementation::Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const uint16_t timeOutInSeconds /* @in */, const string guid /* @in */, string& response /* @out */) + { + LOG_ENTRY_FUNCTION(); + char cmd[100] = ""; + string tempResult = ""; + if (endpoint.empty() || (ipversion != "IPv4" && ipversion != "IPv6")) + { + NMLOG_WARNING("Invalid arguments: endpoint=%s, ipversion=%s", endpoint.c_str(), ipversion.c_str()); + return Core::ERROR_BAD_REQUEST; + } + if(0 == strcasecmp("IPv6", ipversion.c_str())) + { + snprintf(cmd, sizeof(cmd), "ping6 -c %d -W %d -i 0.002 '%s' 2>&1", noOfRequest, timeOutInSeconds, endpoint.c_str()); + } + else + { + snprintf(cmd, sizeof(cmd), "ping -c %d -W %d -i 0.002 '%s' 2>&1", noOfRequest, timeOutInSeconds, endpoint.c_str()); + } + + NMLOG_DEBUG ("The Command is %s", cmd); + string commandToExecute(cmd); + executeExternally(NETMGR_PING, commandToExecute, tempResult); + + JsonObject temp; + temp.FromString(tempResult); + temp["endpoint"] = endpoint; + temp.ToString(response); + + return Core::ERROR_NONE; + } + + /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ + uint32_t NetworkManagerImplementation::Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const string guid /* @in */, string& response /* @out */) + { + LOG_ENTRY_FUNCTION(); + char cmd[256] = ""; + string tempResult = ""; + if (endpoint.empty() || (ipversion != "IPv4" && ipversion != "IPv6")) + { + NMLOG_WARNING("Invalid arguments: endpoint=%s, ipversion=%s", endpoint.c_str(), ipversion.c_str()); + return Core::ERROR_BAD_REQUEST; + } + if(0 == strcasecmp("IPv6", ipversion.c_str())) + { + snprintf(cmd, 256, "traceroute6 -w 3 -m 6 -q %d %s 64 2>&1", noOfRequest, endpoint.c_str()); + } + else + { + snprintf(cmd, 256, "traceroute -w 3 -m 6 -q %d %s 52 2>&1", noOfRequest, endpoint.c_str()); + } + + NMLOG_DEBUG ("The Command is %s", cmd); + string commandToExecute(cmd); + executeExternally(NETMGR_TRACE, commandToExecute, tempResult); + + JsonObject temp; + temp["endpoint"] = endpoint; + temp["results"] = tempResult; + temp.ToString(response); + + return Core::ERROR_NONE; + } + + void NetworkManagerImplementation::executeExternally(NetworkEvents event, const string commandToExecute, string& response) + { + LOG_ENTRY_FUNCTION(); + FILE *pipe = NULL; + string output{}; + char buffer[1024]; + JsonObject pingResult; + int exitStatus; + + pipe = popen(commandToExecute.c_str(), "r"); + if (pipe == NULL) + { + NMLOG_INFO ("%s: failed to open file '%s' for read mode with result: %s", __FUNCTION__, commandToExecute.c_str(), strerror(errno)); + return; + } + + if (NETMGR_PING == event) + { + while (!feof(pipe) && fgets(buffer, 1024, pipe) != NULL) + { + // remove newline from buffer + buffer[strcspn(buffer, "\n")] = '\0'; + string line(buffer); + + if( line.find( "packet" ) != string::npos ) + { + //Example: 10 packets transmitted, 10 packets received, 0% packet loss + stringstream ss( line ); + int transCount; + ss >> transCount; + pingResult["packetsTransmitted"] = transCount; + + string token; + getline( ss, token, ',' ); + getline( ss, token, ',' ); + stringstream ss2( token ); + int rxCount; + ss2 >> rxCount; + pingResult["packetsReceived"] = rxCount; + + getline( ss, token, ',' ); + string prefix = token.substr(0, token.find("%")); + pingResult["packetLoss"] = prefix.c_str(); + + } + else if( line.find( "min/avg/max" ) != string::npos ) + { + //Example: round-trip min/avg/max = 17.038/18.310/20.197 ms + stringstream ss( line ); + string fullpath; + getline( ss, fullpath, '=' ); + getline( ss, fullpath, '=' ); + + string prefix; + int index = fullpath.find("/"); + if (index >= 0) + { + prefix = fullpath.substr(0, fullpath.find("/")); + pingResult["tripMin"] = prefix.c_str(); + } + + index = fullpath.find("/"); + if (index >= 0) + { + fullpath = fullpath.substr(index + 1, fullpath.length()); + prefix = fullpath.substr(0, fullpath.find("/")); + pingResult["tripAvg"] = prefix.c_str(); + } + + index = fullpath.find("/"); + if (index >= 0) + { + fullpath = fullpath.substr(index + 1, fullpath.length()); + prefix = fullpath.substr(0, fullpath.find("/")); + pingResult["tripMax"] = prefix.c_str(); + } + + index = fullpath.find("/"); + if (index >= 0) + { + fullpath = fullpath.substr(index + 1, fullpath.length()); + pingResult["tripStdDev"] = fullpath.c_str(); + } + } + else if( line.find( "bad" ) != string::npos ) + { + pingResult["success"] = false; + pingResult["error"] = "Bad Address"; + } + } + exitStatus = pclose(pipe); + // Check the exit status to determine if the command was successful + if (WIFEXITED(exitStatus) && WEXITSTATUS(exitStatus) == 0) { + pingResult["success"] = true; + } else { + pingResult["success"] = false; + pingResult["error"] = "Could not ping endpoint"; + } + + pingResult.ToString(response); + NMLOG_DEBUG("Response is, %s", response.c_str()); + } + else if (NETMGR_TRACE == event) + { + + // We return the entire output of the trace command but since this contains newlines it is not valid as + // a json value so we will parse the output into an array of strings, one element for each line. + JsonArray list; + while (!feof(pipe) && fgets(buffer, 1024, pipe) != NULL) + { + // remove newline from buffer + buffer[strcspn(buffer, "\n")] = ' '; + string line(buffer); + list.Add(line); + } + + pclose(pipe); + list.ToString(response); + NMLOG_DEBUG("Response is, %s", response.c_str()); + } + return; + } + + void NetworkManagerImplementation::filterScanResults(JsonArray &ssids, + const std::vector& filterSsidslist, + const std::vector& filterFrequencies) + { + LOG_ENTRY_FUNCTION(); + JsonArray result; + double filterFreq = 0.0; + std::unordered_set scanForSsidsSet(filterSsidslist.begin(), filterSsidslist.end()); + + // If neither SSID list nor frequency is provided, exit + if (filterSsidslist.empty() && filterFrequencies.empty()) + { + NMLOG_DEBUG("Neither SSID nor Frequency is provided. Exiting function."); + return; + } + + for (int i = 0; i < ssids.Length(); i++) + { + JsonObject object = ssids[i].Object(); + string ssid = object["ssid"].String(); + string frequency = object["frequency"].String(); + + double frequencyValue = std::stod(frequency); + bool ssidMatches = scanForSsidsSet.empty() || scanForSsidsSet.find(ssid) != scanForSsidsSet.end(); + bool freqMatches = filterFrequencies.empty(); + if (!freqMatches) + { + for (const auto& selectedFrequency : filterFrequencies) + { + if (selectedFrequency == "ALL") + { + freqMatches = true; + break; + } + else + { + filterFreq = std::stod(selectedFrequency); + if (filterFreq == frequencyValue) + { + freqMatches = true; + break; + } + } + } + } + + if (ssidMatches && freqMatches) + result.Add(object); + } + ssids = result; + NMLOG_DEBUG("After filtering, found %d SSIDs.", ssids.Length()); + } + + // WiFi Specific Methods + /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ + uint32_t NetworkManagerImplementation::GetSupportedSecurityModes(ISecurityModeIterator*& security /* @out */) const + { + LOG_ENTRY_FUNCTION(); + std::vector modeInfo { + {WIFI_SECURITY_NONE, "NONE"}, + {WIFI_SECURITY_WPA_PSK, "WPA_PSK"}, + {WIFI_SECURITY_SAE, "SAE"}, + {WIFI_SECURITY_EAP, "EAP"}, + }; + + using Implementation = RPC::IteratorType; + security = Core::Service::Create(modeInfo); + if (security == nullptr) { + return Core::ERROR_GENERAL; + } + return Core::ERROR_NONE; + } + + + void NetworkManagerImplementation::enqueueEvent(NMPublishEvents event, EventDataVariant&& data) + { + LOG_ENTRY_FUNCTION(); + if (m_eventThreadStop.load(std::memory_order_relaxed)) + { + NMLOG_WARNING("Dropping event %d because event thread is stopping", event); + return; + } + { + std::lock_guard lock(m_eventMutex); + m_eventQueue.push({event, std::move(data)}); + NMLOG_DEBUG("Event %d queued, queue size: %zu", event, m_eventQueue.size()); + } + m_eventCondVar.notify_one(); + } + + void NetworkManagerImplementation::eventThreadFunction() + { + NMLOG_INFO("Event thread started"); + + while (!m_eventThreadStop.load()) { + std::unique_lock lock(m_eventMutex); + + // Wait for events or stop signal + m_eventCondVar.wait(lock, [this] { + return !m_eventQueue.empty() || m_eventThreadStop.load(); + }); + + // Process all queued events + while (!m_eventQueue.empty() && !m_eventThreadStop.load()) { + EventData eventData = std::move(m_eventQueue.front()); + m_eventQueue.pop(); + + // Unlock while processing to avoid blocking new events + lock.unlock(); + + NMLOG_DEBUG("Processing event %d from queue", eventData.event); + dispatchEvent(eventData.event, eventData.data); + + lock.lock(); + } + } + + NMLOG_INFO("Event thread exiting"); + } + + void NetworkManagerImplementation::dispatchEvent(NMPublishEvents event, const EventDataVariant& data) + { + LOG_ENTRY_FUNCTION(); + std::list callbacks; + /* + * Avoid holding _notificationLock while notifying subscribers: callbacks can be slow and may + * call back into NetworkManager, risking deadlock. Take a snapshot of callbacks with an + * extra reference, then release each reference after the invocation. + */ + _notificationLock.Lock(); + for (auto* tmpCB : _notificationCallbacks) { + tmpCB->AddRef(); + callbacks.push_back(tmpCB); + } + _notificationLock.Unlock(); + + switch(event) + { + case NM_ON_INTERFACESTATE_CHANGE: + { + NMLOG_INFO("Publishing onInterfaceStateChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onInterfaceStateChange(eventData.state, eventData.interface); + callback->Release(); + } + } + break; + case NM_ON_ACTIVEINTERFACE_CHANGE: + { + NMLOG_INFO("Publishing onActiveInterfaceChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onActiveInterfaceChange(eventData.prevActiveInterface, eventData.currentActiveInterface); + callback->Release(); + } + } + break; + case NM_ON_IPADDRESS_CHANGE: + { + NMLOG_INFO("Publishing onIPAddressChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onIPAddressChange(eventData.interface, eventData.ipversion, eventData.ipaddress, eventData.status); + callback->Release(); + } + } + break; + case NM_ON_INTERNETSTATUS_CHANGE: + { + NMLOG_INFO("Publishing onInternetStatusChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onInternetStatusChange(eventData.prevState, eventData.currState, eventData.interface, eventData.reason); + callback->Release(); + } + } + break; + case NM_ON_AVAILABLESSIDS: + { + NMLOG_INFO("Publishing onAvailableSSIDs Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onAvailableSSIDs(eventData.jsonResult); + callback->Release(); + } + } + break; + case NM_ON_WIFISTATE_CHANGE: + { + NMLOG_INFO("Publishing onWiFiStateChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onWiFiStateChange(eventData.state, eventData.ssid); + callback->Release(); + } + } + break; + case NM_ON_WIFISIGNALQUALITY_CHANGE: + { + NMLOG_INFO("Publishing onWiFiSignalQualityChange Event"); + const auto& eventData = std::get(data); + for (const auto callback : callbacks) { + callback->onWiFiSignalQualityChange(eventData.ssid, eventData.strength, eventData.noise, eventData.snr, eventData.quality); + callback->Release(); + } + } + break; + default: + { + for (const auto callback : callbacks) { + callback->Release(); + } + } + break; + } + } + + void NetworkManagerImplementation::ReportInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface) + { + LOG_ENTRY_FUNCTION(); + if(Exchange::INetworkManager::INTERFACE_LINK_DOWN == state || Exchange::INetworkManager::INTERFACE_REMOVED == state) + { + if(interface == "eth0") + { +#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK) + m_ethIPv4Address = {}; + m_ethIPv6Address = {}; +#endif + m_ethConnected.store(false); + setDefaultInterface("wlan0"); // If WiFi is connected, make it the default interface + // As default interface is changed to wlan0, switch connectivity monitor to initial check + if(!m_useConnectivityCheckMgr) + connectivityMonitor.switchToInitialCheck(); + } + else if(interface == "wlan0") + { +#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK) + m_wlanIPv4Address = {}; + m_wlanIPv6Address = {}; +#endif + m_wlanConnected.store(false); + bool triggerConnectivityCheck; + if(m_ethConnected.load()) + setDefaultInterface("eth0"); // If Ethernet is connected, make it the default interface + triggerConnectivityCheck = (getDefaultInterface() == interface); + if(triggerConnectivityCheck) + { + // When WiFi is disconnected while Ethernet is connected, we don't need to trigger connectivity monitor. + // For WiFi-only state and WiFi disconnected, we should trigger connectivity monitor. + if(!m_useConnectivityCheckMgr) + connectivityMonitor.switchToInitialCheck(); + } + } + } + + /* Only the Ethernet connection status is changing here. The WiFi status is updated in the WiFi state callback. */ + if(Exchange::INetworkManager::INTERFACE_LINK_UP == state) + { + if(interface == "eth0") + { + m_ethConnected.store(true); + m_ethEnabled.store(true); + } + else if(interface == "wlan0") + { + m_wlanConnected.store(true); + m_wlanEnabled.store(true); + } + // connectivityMonitor.switchToInitialCheck(); + // FIXME : Availability of interface does not mean that it has internet connection, so not triggering connectivity monitor check here. + } + + if(Exchange::INetworkManager::INTERFACE_ADDED == state) + { + if(interface == "eth0") + m_ethEnabled.store(true); + else if(interface == "wlan0") + m_wlanEnabled.store(true); + } + + { + InterfaceStateChangeData eventData{state, interface}; + NMLOG_INFO("Posting onInterfaceChange %s - %u", interface.c_str(), (unsigned)state); + enqueueEvent(NM_ON_INTERFACESTATE_CHANGE, std::move(eventData)); + } + return; + } + + void NetworkManagerImplementation::ReportActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface) + { + LOG_ENTRY_FUNCTION(); + + if(currentActiveinterface == "eth0") + { + m_ethConnected.store(true); + m_ethEnabled.store(true); + } + else if (currentActiveinterface == "wlan0") + { + m_wlanConnected.store(true); + m_wlanEnabled.store(true); + } + + { + ActiveInterfaceChangeData eventData{prevActiveInterface, currentActiveinterface}; + NMLOG_INFO("Posting onActiveInterfaceChange %s", currentActiveinterface.c_str()); + enqueueEvent(NM_ON_ACTIVEINTERFACE_CHANGE, std::move(eventData)); + } +#if USE_TELEMETRY + NMLOG_INFO("NM_INTERFACE_STATUS = Interface changed to %s", currentActiveinterface.c_str()); + logTelemetry("NM_INTERFACE_STATUS", "Interface changed to " + currentActiveinterface); +#endif + } + + void NetworkManagerImplementation::ReportIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status) + { + LOG_ENTRY_FUNCTION(); + if (Exchange::INetworkManager::IP_ACQUIRED == status) { + // Switch the connectivity monitor to initial check + // if ipaddress is aquired means there should be interface connected + if(interface == "eth0") + { + m_ethConnected.store(true); + m_ethEnabled.store(true); + } + else if(interface == "wlan0") + { + m_wlanConnected.store(true); + m_wlanEnabled.store(true); + } + + // FIXME : Availability of ip address for a given interface does not mean that its the default interface. This hardcoding will work for RDKProxy but not for Gnome. + bool isDefaultIface; + if (m_ethConnected.load() && m_wlanConnected.load()) + setDefaultInterface("eth0"); + else + setDefaultInterface(interface); + isDefaultIface = (getDefaultInterface() == interface); + + if(isDefaultIface) { + // As default interface is connected, switch connectivity monitor to initial check any way + if(!m_useConnectivityCheckMgr) + connectivityMonitor.switchToInitialCheck(); + } + else + NMLOG_DEBUG("No need to trigger connectivity monitor interface is %s", interface.c_str()); + } + + { + IPAddressChangeData eventData{interface, ipversion, ipaddress, status}; + NMLOG_INFO("Posting onIPAddressChange %s: %s %s %s", (Exchange::INetworkManager::IP_ACQUIRED == status) ? "IP acquired" : "IP lost", + interface.c_str(), ipversion.c_str(), ipaddress.c_str()); + enqueueEvent(NM_ON_IPADDRESS_CHANGE, std::move(eventData)); + } + } + + void NetworkManagerImplementation::ReportRouteChange(const string& interface, const string& ipversion) + { + string iface = interface; + Exchange::INetworkManager::IPAddress settings{}; + if (GetIPSettings(iface, ipversion, settings) != Core::ERROR_NONE) { + return; + } + ReportRouteChange(interface, ipversion, settings); + } + + void NetworkManagerImplementation::ReportRouteChange(const string& interface, const string& ipversion, const Exchange::INetworkManager::IPAddress& settings) + { + if (settings.ipaddress.empty() || settings.gateway.empty() || settings.primarydns.empty()) { + return; + } + + /* Snapshot the callbacks with an extra reference and invoke them outside + * _notificationLock; see dispatchEvent for the rationale. */ + std::list callbacks; + _notificationLock.Lock(); + for (auto* tmpCB : _notificationCallbacks) { + tmpCB->AddRef(); + callbacks.push_back(tmpCB); + } + _notificationLock.Unlock(); + + NMLOG_INFO("Posting onRouteChange %s %s ip=%s gw=%s dns=%s", + interface.c_str(), ipversion.c_str(), settings.ipaddress.c_str(), + settings.gateway.c_str(), settings.primarydns.c_str()); + for (const auto callback : callbacks) { + callback->onRouteChange(interface, ipversion, settings.ipaddress, + settings.gateway, settings.primarydns); + callback->Release(); + } + } + + void NetworkManagerImplementation::ReportInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string& reason) + { + LOG_ENTRY_FUNCTION(); +#if USE_TELEMETRY + // Log error only when ethernet is up and there's no internet + if(currState == Exchange::INetworkManager::INTERNET_NOT_AVAILABLE && + m_ethConnected.load() && + interface == "eth0" && + prevState != Exchange::INetworkManager::INTERNET_NOT_AVAILABLE) + { + NMLOG_INFO("NM_ETHERNET_CONNECTIVITY = Ethernet connectivity failed"); + logTelemetry("NM_ETHERNET_CONNECTIVITY", "Ethernet connectivity failed"); + } +#endif + { + const string noInternetReason = (currState == Exchange::INetworkManager::INTERNET_NOT_AVAILABLE) ? reason : string(); + InternetStatusChangeData eventData{prevState, currState, interface, noInternetReason}; + NMLOG_INFO("Posting onInternetStatusChange with current state as %u", (unsigned)currState); + enqueueEvent(NM_ON_INTERNETSTATUS_CHANGE, std::move(eventData)); + } + +#if USE_TELEMETRY + string stateStr = Core::EnumerateType(currState).Data(); + NMLOG_INFO("NM_INTERNET_STATUS = %s", stateStr.c_str()); + logTelemetry("NM_INTERNET_STATUS", stateStr); +#endif + } + + void NetworkManagerImplementation::OnDelegatedInternetStatusChange(const Exchange::INetworkManager::InternetStatus currState, const string& reason) + { + LOG_ENTRY_FUNCTION(); + +#ifdef USE_CONNECTIVITYCHECKMGR + if (!m_useConnectivityCheckMgr) { + NMLOG_DEBUG("Ignoring delegated internet-status event because delegation is disabled"); + return; + } + + Exchange::INetworkManager::InternetStatus prevState = Exchange::INetworkManager::INTERNET_UNKNOWN; + { + std::lock_guard lock(m_bridgedStatusMutex); + if (m_hasBridgedInternetStatus && m_lastBridgedInternetStatus == currState) { + NMLOG_DEBUG("Skipping duplicate delegated internet-status event state=%u", static_cast(currState)); + return; + } + if (m_hasBridgedInternetStatus) { + prevState = m_lastBridgedInternetStatus; + } + m_lastBridgedInternetStatus = currState; + m_hasBridgedInternetStatus = true; + } + + const string activeInterface = getDefaultInterface(); + NMLOG_INFO("Bridging ConnectivityCheckMgr internet-status event prev=%u curr=%u iface=%s", + static_cast(prevState), static_cast(currState), activeInterface.c_str()); + ReportInternetStatusChange(prevState, currState, activeInterface, reason); +#else + (void)currState; + (void)reason; +#endif + } + + int32_t NetworkManagerImplementation::logSSIDs(Logging level, const JsonArray &ssids) + { + LOG_ENTRY_FUNCTION(); + Logging inLevel; + GetLogLevel(inLevel); + if (level > inLevel) + return ssids.Length(); + + printf ("{\n"); fflush(stdout); + for (int i = 0; i < ssids.Length(); i++) + { + JsonObject ssid = ssids[i].Object(); + string json; + ssid.ToString(json); + printf("\t%s\n", json.c_str()); fflush(stdout); + } + printf("}\n"); fflush(stdout); + return ssids.Length(); + } + + void NetworkManagerImplementation::ReportAvailableSSIDs(const JsonArray &arrayofWiFiScanResults) + { + LOG_ENTRY_FUNCTION(); + string jsonOfWiFiScanResults; + string jsonOfFilterScanResults; + JsonArray filterResult = arrayofWiFiScanResults; + + arrayofWiFiScanResults.ToString(jsonOfWiFiScanResults); + NMLOG_DEBUG("Discovered %d SSIDs before filtering as,", filterResult.Length()); + logSSIDs(LOG_LEVEL_DEBUG, filterResult); + + // Snapshot filter vectors under lock, then release before calling filterScanResults + // to ensure exception-safety (std::stod can throw). + std::vector ssidsSnapshot; + std::vector frequenciesSnapshot; + m_filterVectorsLock.Lock(); + ssidsSnapshot = m_filterSsidslist; + frequenciesSnapshot = m_filterFrequencies; + m_filterVectorsLock.Unlock(); + + // Call filterScanResults outside the lock with snapshots (exception-safe) + filterScanResults(filterResult, ssidsSnapshot, frequenciesSnapshot); + filterResult.ToString(jsonOfFilterScanResults); + + NMLOG_INFO("Posting onAvailableSSIDs event with %d SSIDs as,", filterResult.Length()); + logSSIDs(LOG_LEVEL_INFO, filterResult); + + { + AvailableSSIDsData eventData{jsonOfFilterScanResults}; + enqueueEvent(NM_ON_AVAILABLESSIDS, std::move(eventData)); + } + } + + void NetworkManagerImplementation::startWiFiSignalQualityMonitor(int interval) + { + LOG_ENTRY_FUNCTION(); + if (m_isRunning.load()) { + NMLOG_INFO("WiFiSignalQualityMonitor Thread is already running."); + return; + } + try { + m_isRunning.store(true); + m_monitorThread = std::thread(&NetworkManagerImplementation::monitorThreadFunction, this, interval); + NMLOG_INFO("monitorThreadFunction thread creation successful"); + } catch (const std::exception& err) { + NMLOG_INFO("monitorThreadFunction thread creation failed: %s", err.what()); + } + } + + void NetworkManagerImplementation::stopWiFiSignalQualityMonitor() + { + LOG_ENTRY_FUNCTION(); + if (!m_isRunning.load()) + return; // No thread to stop + + { + std::lock_guard lock(m_condVariableMutex); + m_stopThread.store(true); + } + m_condVariable.notify_one(); + + if (m_monitorThread.joinable()) { + m_monitorThread.join(); + } + m_isRunning.store(false); + } + + /* The below implementation of GetWiFiSignalQuality is a temporary mitigation. Need to be revisited */ + uint32_t NetworkManagerImplementation::GetWiFiSignalQuality(string& ssid /* @out */, int& strength /* @out */, int& noise /* @out */, int& snr /* @out */, WiFiSignalQuality& quality /* @out */) + { + std::string key{}, value{}, bssid{}, band{}; + char buff[512] = {'\0'}; + FILE *fp = NULL; + + /* Get BSSID and SSID from wpa_cli status */ + fp = popen(SSID_COMMAND, "r"); + if (!fp) + { + NMLOG_ERROR("Failed in getting output from command %s", SSID_COMMAND); + return Core::ERROR_GENERAL; + } + + while ((!feof(fp)) && (fgets(buff, sizeof (buff), fp) != NULL)) + { + std::istringstream mystream(buff); + if(std::getline(std::getline(mystream, key, '=') >> std::ws, value)) + { + if (key == "ssid") { + ssid = value; + } + else if (key == "bssid") { + bssid = value; + } + if (!ssid.empty() && !bssid.empty()) + break; + } + } + pclose(fp); + + /* If BSSID is empty, WiFi is disconnected */ + if (bssid.empty()) { + NMLOG_WARNING("WiFi is disconnected (BSSID is empty)"); + quality = WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED; + ssid = ""; + strength = 0; + noise = 0; + snr = 0; + return Core::ERROR_NONE; + } + + /*Get real-time signal data from wpa_cli signal_poll */ + fp = popen(SIGNAL_POLL_COMMAND, "r"); + if (!fp) + { + NMLOG_ERROR("Failed in getting output from command %s", SIGNAL_POLL_COMMAND); + return Core::ERROR_GENERAL; + } + + // Collect raw values during parsing + std::string linkSpeed, rssiValue, noiseValue, avgRssiValue, freqValue; + while ((!feof(fp)) && (fgets(buff, sizeof (buff), fp) != NULL)) + { + std::istringstream mystream(buff); + if(std::getline(std::getline(mystream, key, '=') >> std::ws, value)) + { + if (key == "RSSI") { + rssiValue = value; + } + else if (key == "NOISE") { + noiseValue = value; + } + else if (key == "AVG_RSSI") { + avgRssiValue = value; + } + else if (key == "FREQUENCY") + { + freqValue = value; + } + else if (key == "LINKSPEED") + { + linkSpeed = value; + } + } + } + pclose(fp); + + // Helper to safely convert string to int + auto toInt = [](const std::string& str, int defaultVal = 0) -> int { + return str.empty() ? defaultVal : std::stoi(str); + }; + + // Perform conversions - use RSSI if available, otherwise fallback to AVG_RSSI + strength = toInt(!rssiValue.empty() ? rssiValue : avgRssiValue, 0); + noise = toInt(noiseValue, 0); + + // Determine band from frequency + if (!freqValue.empty()) { + int freq = std::stoi(freqValue); + band = (freq >= 2400 && freq < 5000) ? "2.4GHz" : + (freq >= 5000 && freq < 6000) ? "5GHz" : + (freq >= 6000) ? "6GHz" : "not known"; + } + + int16_t readRssi = strength; + int16_t readNoise = noise; + + /* Check the RSSI is within range between -10 and -100 dbm*/ + if (readRssi >= 0 || readRssi < -100) { + NMLOG_WARNING("Received RSSI (%d dBm) is out of valid range (-10 to -100 dBm); Resetting to default", readRssi); + if (readRssi >= 0) { + readRssi = -10; + } + else if (readRssi < -100) { + readRssi = -100; + } + } + + /* Check the Noise is within range between 0 and -96 dbm*/ + if((readNoise >= 0) || (readNoise < DEFAULT_NOISE)) + { + NMLOG_DEBUG("Received Noise (%d) from wifi driver is not valid; so clamping it", readNoise); + if (readNoise >= 0) { + readNoise = 0; + noise = 0; + } + else if (readNoise < DEFAULT_NOISE) { + readNoise = DEFAULT_NOISE; + noise = DEFAULT_NOISE; + } + } + + /*Calculate SNR = RSSI - Noise */ + int16_t calculatedSnr = readRssi - readNoise; + snr = calculatedSnr; + + /* mapping rssi value when the SNR value is not proper */ + if(!(calculatedSnr > 0 && calculatedSnr <= MAX_SNR_VALUE)) + { + NMLOG_WARNING("calculated SNR (%d) is not valid; Lets map with RSSI (%d)", calculatedSnr, strength); + calculatedSnr = strength; + /* Take the absolute value */ + calculatedSnr = (calculatedSnr < 0) ? -calculatedSnr : calculatedSnr; + + snr = calculatedSnr; + } + + NMLOG_INFO("SSID:%s, BSSID:%s, Band:%s, RSSI:%d, Noise:%d, SNR:%d", ssid.c_str(), bssid.c_str(), band.c_str(), strength, noise, snr); + NMLOG_INFO("bssid=%s,ssid=%s,rssi=%d,phyrate=%s,noise=%d,Band=%s", bssid.c_str(), ssid.c_str(), strength, linkSpeed.c_str(), noise, band.c_str()); + + if (calculatedSnr == 0) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED; + strength = 0; + } + else if (calculatedSnr > 0 && calculatedSnr < NM_WIFI_SNR_THRESHOLD_FAIR) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_WEAK; + } + else if (calculatedSnr >= NM_WIFI_SNR_THRESHOLD_FAIR && calculatedSnr < NM_WIFI_SNR_THRESHOLD_GOOD) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_FAIR; + } + else if (calculatedSnr >= NM_WIFI_SNR_THRESHOLD_GOOD && calculatedSnr < NM_WIFI_SNR_THRESHOLD_EXCELLENT) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_GOOD; + } + else + { + quality = WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT; + } + + return Core::ERROR_NONE; + } + + void NetworkManagerImplementation::processMonitor(uint16_t interval) + { + LOG_ENTRY_FUNCTION(); + pid_t pid = getpid(); + + string path = "/proc/"; + path += std::to_string(pid); + path += "/statm"; + const uint32_t pageSize = getpagesize(); + + do + { + int fd; + char buffer[128] = ""; + int processSize = 0; + int processRSS = 0; + int processShare = 0; + + if ((fd = open(path.c_str(), O_RDONLY)) >= 0) + { + ssize_t readAmount = 0; + if ((readAmount = read(fd, buffer, sizeof(buffer))) > 0) + { + ssize_t nulIndex = std::min(readAmount, static_cast(sizeof(buffer) - 1)); + buffer[nulIndex] = '\0'; + sscanf(buffer, "%d %d %d", &processSize, &processRSS, &processShare); + + /* Update the sizes */ + processSize *= pageSize; + processRSS *= pageSize; + processShare *= pageSize; + + /* Convert to KB */ + processSize >>= 10; + processRSS >>= 10; + processShare >>= 10; + } + close(fd); + NMLOG_INFO("VSS = %d KB RSS = %d KB", processSize, processRSS); + } + + std::unique_lock lock(m_processMonMutex); + // Wait for the specified interval or until notified to stop + if (m_processMonCondVar.wait_for(lock, std::chrono::seconds(interval), [this](){ return m_processMonThreadStop.load(); })) + { + NMLOG_INFO("Received stop signal"); + } + } while (!m_processMonThreadStop.load()); + + return; + } + + void NetworkManagerImplementation::monitorThreadFunction(int interval) + { + LOG_ENTRY_FUNCTION(); + static Exchange::INetworkManager::WiFiSignalQuality oldSignalQuality = Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED; + NMLOG_INFO("WiFiSignalQualityMonitor thread started ! (%d)", interval); + while (true) + { + std::string ssid{}; + int strength = 0; + int noise = 0; + int snr = 0; + Exchange::INetworkManager::WiFiSignalQuality newSignalQuality; + + GetWiFiSignalQuality(ssid, strength, noise, snr, newSignalQuality); + + if (oldSignalQuality != newSignalQuality) { + oldSignalQuality = newSignalQuality; + NetworkManagerImplementation::ReportWiFiSignalQualityChange(ssid, strength, noise, snr, newSignalQuality); + } + + if (newSignalQuality == Exchange::INetworkManager::WIFI_SIGNAL_DISCONNECTED) { + NMLOG_WARNING("WiFiSignalQualityChanged to disconnect - WiFiSignalQualityMonitor exiting"); + break; // Exit the loop + } + + std::unique_lock lock(m_condVariableMutex); + // Wait for the specified interval or until notified to stop + if (m_condVariable.wait_for(lock, std::chrono::seconds(interval), [this](){ return m_stopThread.load(); })) + { + NMLOG_INFO("WiFiSignalQualityMonitor received stop signal or timed out"); + break; + } + } + m_stopThread.store(false); + } + + void NetworkManagerImplementation::ReportWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid) + { + LOG_ENTRY_FUNCTION(); + + std::string reportSSID; + + /* start signal strength monitor when wifi connected */ + if(INetworkManager::WiFiState::WIFI_STATE_CONNECTED == state) + { + m_wlanConnected.store(true); + if(!ssid.empty()) + { + setLastConnectedSSID(ssid); + reportSSID = ssid; + } + else + { + reportSSID = getLastConnectedSSID(); + } + startWiFiSignalQualityMonitor(DEFAULT_WIFI_SIGNAL_TEST_INTERVAL_SEC); + } + else + { + stopWiFiSignalQualityMonitor(); + m_wlanConnected.store(false); /* Any other state is considered as WiFi not connected. */ + + if(INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED == state) + reportSSID = getLastConnectedSSID(); /* previously connected SSID, or empty */ + else + reportSSID = ssid; /* SSID currently being attempted */ + } + + NMLOG_INFO("Posting onWiFiStateChange (%d) ssid: %s", state, reportSSID.c_str()); +#if USE_TELEMETRY + string stateStr = Core::EnumerateType(state).Data(); + NMLOG_INFO("NM_WIFI_STATUS = %s", stateStr.c_str()); + logTelemetry("NM_WIFI_STATUS", stateStr); +#endif + { + WiFiStateChangeData eventData{state, reportSSID}; + enqueueEvent(NM_ON_WIFISTATE_CHANGE, std::move(eventData)); + } + } + + void NetworkManagerImplementation::ReportWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality) + { + LOG_ENTRY_FUNCTION(); + { + WiFiSignalQualityChangeData eventData{ssid, strength, noise, snr, quality}; + NMLOG_INFO("Posting onWiFiSignalQualityChange %d", strength); + enqueueEvent(NM_ON_WIFISIGNALQUALITY_CHANGE, std::move(eventData)); + } + } + + void NetworkManagerImplementation::logTelemetry(const std::string& eventName, const std::string& message) + { + LOG_ENTRY_FUNCTION(); +#if USE_TELEMETRY + T2ERROR t2error = t2_event_s(eventName.c_str(), const_cast(message.c_str())); + if (t2error != T2ERROR_SUCCESS) { + NMLOG_ERROR("t2_event_s(\"%s\", \"%s\") failed with error %d", + eventName.c_str(), message.c_str(), t2error); + } +#endif + } + + void NetworkManagerImplementation::OnPowerModePreChange( + const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) + { + LOG_ENTRY_FUNCTION(); + // Called from NetworkManagerPowerClient's power thread. + NMLOG_DEBUG("OnPowerModePreChange: current=%d new=%d", + static_cast(currentState), static_cast(newState)); + + using PowerState = Exchange::IPowerManager::PowerState; + + if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) + { + if (m_wlanEnabled.load() && m_wlanConnected.load()) + { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — disconnecting WiFi"); + + uint32_t rcWifiDown = WiFiDisconnect(); + if (rcWifiDown == Core::ERROR_NONE) + { + m_wlanDisconnectedForSleep.store(true); + } + else + { + NMLOG_ERROR("OnPowerModePreChange: WiFiDisconnect failed (rc=%u), will not reconnect on wakeup", rcWifiDown); + } + } + else + { + NMLOG_DEBUG("OnPowerModePreChange: going to DeepSleep — WiFi not connected, skipping disconnect"); + } +#ifdef ENABLE_ETHERNET_CONNECTION_HANDLING + if (m_ethEnabled.load() && m_ethConnected.load()) + { + NMLOG_INFO("OnPowerModePreChange: going to DeepSleep — deactivating Ethernet"); + + uint32_t rcEthDown = EthernetDeactivate(); + if (rcEthDown == Core::ERROR_NONE) + { + m_ethDisconnectedForSleep.store(true); + } + else + { + NMLOG_ERROR("OnPowerModePreChange: EthernetDeactivate failed (rc=%u), will not activate on wakeup", rcEthDown); + } + } + else + { + NMLOG_DEBUG("OnPowerModePreChange: going to DeepSleep — Ethernet not activated, skipping deactivate"); + } +#endif + } + else if (currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) + { + if (m_wlanDisconnectedForSleep.load()) + { + const std::string lastConnectedSSID = getLastConnectedSSID(); + if (!lastConnectedSSID.empty()) + { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — reconnecting to '%s'", lastConnectedSSID.c_str()); + uint32_t rcWifiUp = ConnectToKnownSSID(lastConnectedSSID); + if (rcWifiUp == Core::ERROR_NONE) + { + m_wlanDisconnectedForSleep.store(false); + } + else + { + NMLOG_ERROR("OnPowerModePreChange: ConnectToKnownSSID failed (rc=%u)", rcWifiUp); + } + } + else + { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — no last SSID, skipping reconnect"); + } + } + else + { + NMLOG_INFO("OnPowerModePreChange: waking from DeepSleep — WiFi was not connected or was already down before sleep, skipping reconnect"); + } + // DeepSleep wake (Network Standby OFF): re-verify connectivity so internet status is re-published. + if(!m_useConnectivityCheckMgr) + { + connectivityMonitor.switchToInitialCheck(); + } + } + sendAck(); + } + + void NetworkManagerImplementation::OnPowerModeChanged( + const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) + { + LOG_ENTRY_FUNCTION(); + NMLOG_INFO("OnPowerModeChanged: current=%d new=%d", + static_cast(currentState), static_cast(newState)); + if (currentState == Exchange::IPowerManager::PowerState::POWER_STATE_STANDBY_DEEP_SLEEP) { + + if (m_wlanEnabled.load() && m_wlanConnected.load()) + { + NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, requesting DHCP lease on wlan0"); + if (ReacquireDHCPLease("wlan0") != Core::ERROR_NONE) + { + NMLOG_ERROR("OnPowerModeChanged: ReacquireDHCPLease(wlan0) failed"); + } + } + if (m_ethEnabled.load() && m_ethConnected.load()) + { + NMLOG_INFO("OnPowerModeChanged: waking from DeepSleep, requesting DHCP lease on eth0"); + if (ReacquireDHCPLease("eth0") != Core::ERROR_NONE) + { + NMLOG_ERROR("OnPowerModeChanged: ReacquireDHCPLease(eth0) failed"); + } + } + // DeepSleep → Standby wake (Network Standby ON): re-verify connectivity so internet status is re-published. + if(!m_useConnectivityCheckMgr) + { + connectivityMonitor.switchToInitialCheck(); + } + } + } + + bool isIPv4LinkLocal(const std::string& addr) + { + struct in_addr sa{}; + return inet_pton(AF_INET, addr.c_str(), &sa) == 1 && + (ntohl(sa.s_addr) & 0xffff0000u) == 0xa9fe0000u; + } + + bool isIPv6LinkLocal(const std::string& addr) + { + struct in6_addr sa6{}; + return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 && + sa6.s6_addr[0] == 0xfe && (sa6.s6_addr[1] & 0xc0) == 0x80; + } + + bool isIPv6ULA(const std::string& addr) + { + struct in6_addr sa6{}; + return inet_pton(AF_INET6, addr.c_str(), &sa6) == 1 && + (sa6.s6_addr[0] & 0xfe) == 0xfc; + } + + /* Parse a MAC string into 6 bytes. Accepts both "AA:BB:CC:DD:EE:FF" and "aabbccddeeff". */ + static bool parseMac(const std::string& mac, uint8_t out[6]) + { + unsigned int b[6]; + if (mac.size() >= 17 && + sscanf(mac.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", + &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6) + { + for (int i = 0; i < 6; ++i) out[i] = static_cast(b[i]); + return true; + } + if (mac.size() >= 12 && + sscanf(mac.c_str(), "%02x%02x%02x%02x%02x%02x", + &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]) == 6) + { + for (int i = 0; i < 6; ++i) out[i] = static_cast(b[i]); + return true; + } + return false; + } + + bool isIPv6MacBased(const std::string& ipv6Addr, const std::string& macAddr) + { + struct in6_addr sa6{}; + uint8_t mac[6]; + if (inet_pton(AF_INET6, ipv6Addr.c_str(), &sa6) != 1 || !parseMac(macAddr, mac)) + return false; + + /* Build 8-byte EUI-64 identifier from 6-byte MAC: + mac[0..2] | ff:fe | mac[3..5], then flip the U/L bit (bit 1) of byte 0. */ + uint8_t eui64[8]; + eui64[0] = mac[0] ^ 0x02; // flip Universal/Local bit + eui64[1] = mac[1]; + eui64[2] = mac[2]; + eui64[3] = 0xff; + eui64[4] = 0xfe; + eui64[5] = mac[3]; + eui64[6] = mac[4]; + eui64[7] = mac[5]; + + /* Compare against the interface-ID (last 8 bytes) of the IPv6 address. */ + if (memcmp(&sa6.s6_addr[8], eui64, 8) == 0) + { + NMLOG_DEBUG("MAC %s based global v6 address %s", macAddr.c_str(), ipv6Addr.c_str()); + return true; + } + return false; + } + + bool NetworkManagerImplementation::lookupIpCache( + const std::string& iface, const std::string& ipFamily, + Exchange::INetworkManager::IPAddress& out) const + { + std::lock_guard lock(m_ipCacheMutex); + auto it = m_ipCacheMap.find({iface, ipFamily}); + if (it != m_ipCacheMap.end() && it->second.valid) { + out = it->second.toIPAddress(); + out.ipversion = ipFamily; + return true; + } + return false; + } + + std::set NetworkManagerImplementation::swapIpCache( + const std::string& iface, const std::string& ipFamily, + IpFamilyCache&& newCache) + { + std::set oldKeys; + std::lock_guard lock(m_ipCacheMutex); + IpFamilyCache& cache = m_ipCacheMap[{iface, ipFamily}]; + for (const auto& kv : cache.globalAddresses) + oldKeys.insert(kv.first); + cache = std::move(newCache); + return oldKeys; + } + + Exchange::INetworkManager::IPAddress IpFamilyCache::toIPAddress() const + { + LOG_ENTRY_FUNCTION(); + Exchange::INetworkManager::IPAddress addr{}; + /* Detect IP version from any available address. */ + bool isIPv6 = false; + { + const std::string* sample = nullptr; + if (!globalAddresses.empty()) + sample = &globalAddresses.begin()->first; + else if (!uniqueLocalAddresses.empty()) + sample = &(*uniqueLocalAddresses.begin()); + else if (!linkLocalAddresses.empty()) + sample = &(*linkLocalAddresses.begin()); + if (sample) { + struct in6_addr sa6{}; + isIPv6 = (inet_pton(AF_INET6, sample->c_str(), &sa6) == 1); + } + } + addr.ipversion = isIPv6 ? "IPv6" : "IPv4"; + addr.autoconfig = autoconfig; + addr.dhcpserver = dhcpserver; + addr.ula = uniqueLocalAddresses.empty() ? "" : *uniqueLocalAddresses.begin(); + addr.gateway = gateway; + addr.primarydns = primarydns; + addr.secondarydns = secondarydns; + /* Prefer non-MAC-based global; fall back to MAC-based if all are MAC-based. */ + const std::string* bestGlobal = nullptr; + uint32_t bestPrefix = 0; + const std::string* fallbackMac = nullptr; + uint32_t fallbackMacPrefix = 0; + for (const auto& kv : globalAddresses) { + if (kv.second.type == ADDR_GLOBAL) { + bestGlobal = &kv.first; + bestPrefix = kv.second.prefix; + break; + } + if (!fallbackMac) { + fallbackMac = &kv.first; + fallbackMacPrefix = kv.second.prefix; + } + } + if (bestGlobal) { + addr.ipaddress = *bestGlobal; + addr.prefix = bestPrefix; + } else if (fallbackMac) { + addr.ipaddress = *fallbackMac; + addr.prefix = fallbackMacPrefix; + } + return addr; + } + } +} diff --git a/plugin/NetworkManagerImplementation.h b/plugin/NetworkManagerImplementation.h new file mode 100644 index 000000000..d0c10ba16 --- /dev/null +++ b/plugin/NetworkManagerImplementation.h @@ -0,0 +1,514 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + + +#include "Module.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include "INetworkManager.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerConnectivity.h" +#ifdef USE_CONNECTIVITYCHECKMGR +#include "NetworkManagerConnectivityClient.h" +#endif +#include "NetworkManagerStunClient.h" +#include "NetworkManagerPowerClient.h" + +/* Forward declarations to avoid pulling GLib/libnm headers into this header */ +typedef struct _GMainContext GMainContext; + +/* + * Receiver thermal noise + BW factor + assumed noise figure (NF) (dB) + * for a 20MHz channel, + * = -174dBm/Hz + 10*log10(20MHz) + 5 + * = -174 + 73 + 5 = -96dBm + * Even if the channel width increases, the noise value will be increases by 3 dBm; (ie -93 dBm). + * So the minimum noise value cannot go beyond -96 dBm. + */ +#define DEFAULT_NOISE -96 +#define MAX_SNR_VALUE 180 + +#define DEFAULT_WIFI_SIGNAL_TEST_INTERVAL_SEC 60 +#define NM_PROCESS_MONITOR_INTERVAL_SEC 60 +#define NM_WIFI_SNR_THRESHOLD_EXCELLENT 40 +#define NM_WIFI_SNR_THRESHOLD_GOOD 25 +#define NM_WIFI_SNR_THRESHOLD_FAIR 18 +#define ROUTE_METRIC_PRIORITY_HIGH 1 +#define ROUTE_METRIC_PRIORITY_LOW 100 + +namespace WPEFramework +{ + namespace Plugin + { + /* Returns true if the given string is an IPv4 link-local address (169.254.0.0/16). */ + bool isIPv4LinkLocal(const std::string& addr); + + /* Returns true if the given string is an IPv6 link-local address (fe80::/10). */ + bool isIPv6LinkLocal(const std::string& addr); + + /* Returns true if the given string is an IPv6 Unique Local Address (ULA, fc00::/7). */ + bool isIPv6ULA(const std::string& addr); + + /* Returns true if the given global IPv6 address is derived (EUI-64) from the MAC. */ + bool isIPv6MacBased(const std::string& ipv6Addr, const std::string& macAddr); + + /* Sub-classification of global-scope addresses in the IP cache. */ + enum GlobalAddressType : uint8_t { + ADDR_GLOBAL, // non-MAC-based global (preferred by GetIPSettings) + ADDR_GLOBAL_MAC_BASED, // EUI-64 global derived from interface MAC (fallback) + }; + + struct GlobalAddressInfo { + uint32_t prefix; + GlobalAddressType type; + GlobalAddressInfo() : prefix(0), type(ADDR_GLOBAL) {} + GlobalAddressInfo(uint32_t p, GlobalAddressType t) : prefix(p), type(t) {} + }; + + /* Per-interface, per-address-family cache populated by libnm events. */ + struct IpFamilyCache { + bool valid = false; + std::map globalAddresses; // event-diffable global addresses + std::set linkLocalAddresses; // fe80::/10 or 169.254.x.x — not diffed for events + std::set uniqueLocalAddresses; // fc00::/7 (IPv6 only) — not diffed for events + std::string gateway; + std::string primarydns; + std::string secondarydns; + std::string dhcpserver; + bool autoconfig = false; + + Exchange::INetworkManager::IPAddress toIPAddress() const; + void clear() { *this = IpFamilyCache{}; } + }; + + class NetworkManagerImplementation : public Exchange::INetworkManager + , public INetworkPowerCallback + { + enum NetworkEvents + { + NETMGR_PING, + NETMGR_TRACE, + }; + + class ConnectivityConf : public Core::JSON::Container { + public: + ConnectivityConf& operator=(const ConnectivityConf&) = delete; + + ConnectivityConf() + : Core::JSON::Container() + , endpoint_1(_T("http://clients3.google.com/generate_204")) + , endpoint_2(_T("")) + , endpoint_3(_T("")) + , endpoint_4(_T("")) + , endpoint_5(_T("")) + , ConnectivityCheckInterval(6) + { + Add(_T("endpoint_1"), &endpoint_1); + Add(_T("endpoint_2"), &endpoint_2); + Add(_T("endpoint_3"), &endpoint_3); + Add(_T("endpoint_4"), &endpoint_4); + Add(_T("endpoint_5"), &endpoint_5); + Add(_T("interval"), &ConnectivityCheckInterval); + } + ~ConnectivityConf() override = default; + + public: + /* connectivity configuration */ + Core::JSON::String endpoint_1; + Core::JSON::String endpoint_2; + Core::JSON::String endpoint_3; + Core::JSON::String endpoint_4; + Core::JSON::String endpoint_5; + Core::JSON::DecUInt32 ConnectivityCheckInterval; + }; + + class Stun : public Core::JSON::Container { + public: + Stun& operator=(const Stun&) = delete; + + Stun() + : Core::JSON::Container() + , stunEndpoint(_T("stun.l.google.com")) + , port(19302) + , interval(30) + { + Add(_T("endpoint"), &stunEndpoint); + Add(_T("port"), &port); + Add(_T("interval"), &interval); + } + ~Stun() override = default; + + public: + /* stun configuration */ + Core::JSON::String stunEndpoint; + Core::JSON::DecUInt32 port; + Core::JSON::DecUInt32 interval; + }; + + class WiFiConfig : public Core::JSON::Container + { + public: + Core::JSON::String ssid; + Core::JSON::String password; + Core::JSON::DecUInt8 security; + + WiFiConfig() : Core::JSON::Container() + { + Add(_T("ssid"), &ssid); + Add(_T("password"), &password); + Add(_T("security"), &security); + } + + ~WiFiConfig() override = default; + WiFiConfig& operator=(const WiFiConfig&) = delete; + }; + + class Configuration : public Core::JSON::Container { + private: + Configuration(const Configuration&); + Configuration& operator=(const Configuration&); + + public: + Configuration() + : Core::JSON::Container() + { + Add(_T("connectivity"), &connectivityConf); + Add(_T("stun"), &stun); + Add(_T("loglevel"), &loglevel); + } + ~Configuration() override = default; + + public: + ConnectivityConf connectivityConf; + Stun stun; + Core::JSON::DecUInt32 loglevel; + }; + + enum NMPublishEvents { + NM_ON_INTERFACESTATE_CHANGE = 0, + NM_ON_ACTIVEINTERFACE_CHANGE, + NM_ON_IPADDRESS_CHANGE, + NM_ON_INTERNETSTATUS_CHANGE, + NM_ON_AVAILABLESSIDS, + NM_ON_WIFISTATE_CHANGE, + NM_ON_WIFISIGNALQUALITY_CHANGE + }; + + // Typed event data structures + struct InterfaceStateChangeData { + Exchange::INetworkManager::InterfaceState state; + string interface; + }; + + struct ActiveInterfaceChangeData { + string prevActiveInterface; + string currentActiveInterface; + }; + + struct IPAddressChangeData { + string interface; + string ipversion; + string ipaddress; + Exchange::INetworkManager::IPStatus status; + }; + + struct InternetStatusChangeData { + Exchange::INetworkManager::InternetStatus prevState; + Exchange::INetworkManager::InternetStatus currState; + string interface; + string reason; + }; + + struct AvailableSSIDsData { + string jsonResult; // Pre-serialized JSON string + }; + + struct WiFiStateChangeData { + Exchange::INetworkManager::WiFiState state; + string ssid; + }; + + struct WiFiSignalQualityChangeData { + string ssid; + int strength; + int noise; + int snr; + Exchange::INetworkManager::WiFiSignalQuality quality; + }; + + using EventDataVariant = std::variant< + std::monostate, + InterfaceStateChangeData, + ActiveInterfaceChangeData, + IPAddressChangeData, + InternetStatusChangeData, + AvailableSSIDsData, + WiFiStateChangeData, + WiFiSignalQualityChangeData + >; + + struct EventData { + NMPublishEvents event; + EventDataVariant data; + }; + + public: + NetworkManagerImplementation(); + ~NetworkManagerImplementation() override; + + // Do not allow copy/move constructors + NetworkManagerImplementation(const NetworkManagerImplementation &) = delete; + NetworkManagerImplementation &operator=(const NetworkManagerImplementation &) = delete; + + BEGIN_INTERFACE_MAP(NetworkManagerImplementation) + INTERFACE_ENTRY(Exchange::INetworkManager) + END_INTERFACE_MAP + + // Handle Notification registration/removal + uint32_t Register(INetworkManager::INotification *notification) override; + uint32_t Unregister(INetworkManager::INotification *notification) override; + + public: + // Below Control APIs will work with RDK or GNome NW. + /* @brief Get all the Available Interfaces */ + uint32_t GetAvailableInterfaces (IInterfaceDetailsIterator*& interfaces/* @out */) override; + + /* @brief Get the active Interface used for external world communication */ + uint32_t GetPrimaryInterface (string& interface /* @out */) override; + + /* @brief Enable/Disable the given interface */ + uint32_t SetInterfaceState(const string& interface/* @in */, const bool isEnabled/* @in */) override; + /* @brief Get the state of given interface */ + uint32_t GetInterfaceState(const string& interface/* @in */, bool& isEnabled/* @out */) override; + + /* @brief Get IP Address Of the Interface */ + uint32_t GetIPSettings(string& interface /* @inout */, const string& ipversion /* @in */, IPAddress& address /* @out */) override; + /* @brief Set IP Address Of the Interface */ + uint32_t SetIPSettings(const string& interface /* @in */, const IPAddress& address /* @in */) override; + + // WiFi Specific Methods + /* @brief Initiate a WIFI Scan; This is Async method and returns the scan results as Event */ + uint32_t StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) override; + uint32_t StopWiFiScan(void) override; + + uint32_t GetKnownSSIDs(IStringIterator*& ssids /* @out */) override; + uint32_t AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) override; + uint32_t RemoveKnownSSID(const string& ssid /* @in */) override; + uint32_t ConnectToKnownSSID(const string& ssid /* @in */) override; + + uint32_t WiFiConnect(const WiFiConnectTo& ssid /* @in */) override; + uint32_t WiFiDisconnect(void) override; + uint32_t EthernetDeactivate(void); + uint32_t ReacquireDHCPLease(const string& iface); + uint32_t GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) override; + + uint32_t StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) override; + uint32_t StopWPS(void) override; + uint32_t GetWifiState(WiFiState &state) override; + uint32_t GetWiFiSignalQuality(string& ssid /* @out */, int& strength /* @out */, int& noise /* @out */, int& snr /* @out */, WiFiSignalQuality& quality /* @out */) override; + + uint32_t SetStunEndpoint (string const endpoint /* @in */, const uint32_t port /* @in */, const uint32_t bindTimeout /* @in */, const uint32_t cacheTimeout /* @in */) override; + uint32_t GetStunEndpoint (string &endpoint /* @out */, uint32_t& port /* @out */, uint32_t& bindTimeout /* @out */, uint32_t& cacheTimeout /* @out */) const override; + + /* @brief Get ConnectivityTest Endpoints */ + uint32_t GetConnectivityTestEndpoints(IStringIterator*& endpoints/* @out */) const override; + /* @brief Set ConnectivityTest Endpoints */ + uint32_t SetConnectivityTestEndpoints(IStringIterator* const endpoints /* @in */) override; + + /* @brief Get Internet Connectivty Status */ + uint32_t IsConnectedToInternet(string &ipversion /* @inout */, string &interface /* @inout */, InternetStatus &result /* @out */, string& reason /* @out */) override; + /* @brief Get Authentication URL if the device is behind Captive Portal */ + uint32_t GetCaptivePortalURI(string &endpoints/* @out */) const override; + + /* @brief Get the Public IP used for external world communication */ + uint32_t GetPublicIP (string& interface /* @inout */, string &ipversion /* @inout */, string& ipaddress /* @out */) override; + + /* @brief Request for ping and get the response in as event. The GUID used in the request will be returned in the event. */ + uint32_t Ping (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const uint16_t timeOutInSeconds /* @in */, const string guid /* @in */, string& response /* @out */) override; + + /* @brief Request for trace get the response in as event. The GUID used in the request will be returned in the event. */ + uint32_t Trace (const string ipversion /* @in */, const string endpoint /* @in */, const uint32_t noOfRequest /* @in */, const string guid /* @in */, string& response /* @out */) override; + + uint32_t GetSupportedSecurityModes(ISecurityModeIterator*& security /* @out */) const override; + + /* @brief Set the dhcp hostname */ + uint32_t SetHostname(const string& hostname /* @in */) override; + + /* @brief Set the network manager plugin log level */ + uint32_t SetLogLevel(const Logging& level /* @in */) override; + uint32_t GetLogLevel(Logging& level /* @out */) override; + + /* @brief configure network manager plugin */ + uint32_t Configure(const string configLine) override; + + /* Events */ + void ReportInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface); + void ReportActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface); + void ReportIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status); + void ReportRouteChange(const string& interface, const string& ipversion); + void ReportRouteChange(const string& interface, const string& ipversion, const Exchange::INetworkManager::IPAddress& settings); + void ReportInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string& reason = string()); + void OnDelegatedInternetStatusChange(const Exchange::INetworkManager::InternetStatus currState, const string& reason = string()); + void ReportAvailableSSIDs(const JsonArray &arrayofWiFiScanResults); + void ReportWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid); + void ReportWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality); + void logTelemetry(const std::string& eventName, const std::string& message); + + // INetworkPowerCallback overrides + void OnPowerModePreChange(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) override; + void OnPowerModeChanged(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) override; + + private: + void platform_init(void); + void platform_deinit(void); + void platform_logging(const NetworkManagerLogger::LogLevel& level); + /* Resolve whether connectivity is delegated to ConnectivityCheckMgr: + * RFC flag Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.ConnectivityCheckMgr.Enable + * (when USE_CONNECTIVITYCHECKMGR is built in) takes precedence, then the config-line + * fallback key, then default false (built-in monitor). */ + bool resolveConnectivityCheckMgrEnabled(const Configuration& config) const; + void getInitialConnectionState(void); + void executeExternally(NetworkEvents event, const string commandToExecute, string& response); + void threadEventRegistration(bool iarmInit, bool iarmConnect); + void filterScanResults(JsonArray &ssids, const std::vector& filterSsidslist, const std::vector& filterFrequencies); + void startWiFiSignalQualityMonitor(int interval); + void stopWiFiSignalQualityMonitor(); + void monitorThreadFunction(int interval); + int32_t logSSIDs(Logging level, const JsonArray &ssids); + void processMonitor(uint16_t interval); + void eventThreadFunction(); + void enqueueEvent(NMPublishEvents event, EventDataVariant&& data); + void dispatchEvent(NMPublishEvents event, const EventDataVariant& data); + + private: + std::list _notificationCallbacks; + Core::CriticalSection _notificationLock; + Core::CriticalSection m_filterVectorsLock; + string m_publicIP; + stun::client stunClient; + string m_stunEndpoint; + uint16_t m_stunPort; + uint16_t m_stunBindTimeout; + uint16_t m_stunCacheTimeout; + std::thread m_registrationThread; + std::vector m_filterFrequencies; + std::vector m_filterSsidslist; + std::thread m_monitorThread; + + std::thread m_processMonThread; + std::mutex m_processMonMutex; + std::atomic m_processMonThreadStop{false}; + std::condition_variable m_processMonCondVar; + + std::thread m_eventThread; + std::queue m_eventQueue; + std::mutex m_eventMutex; + std::condition_variable m_eventCondVar; + std::atomic m_eventThreadStop{false}; + + std::atomic m_isRunning{false}; + std::atomic m_stopThread{false}; + std::mutex m_condVariableMutex; + std::condition_variable m_condVariable; + std::unique_ptr m_powerClient; + public: +#if defined(NM_BACKEND_GDBUS) || defined(NM_BACKEND_RDK) + IPAddress m_ethIPv4Address; + IPAddress m_wlanIPv4Address; + IPAddress m_ethIPv6Address; + IPAddress m_wlanIPv6Address; +#endif + bool lookupIpCache(const std::string& iface, const std::string& ipFamily, + Exchange::INetworkManager::IPAddress& out) const; + std::set swapIpCache(const std::string& iface, + const std::string& ipFamily, + IpFamilyCache&& newCache); + + std::atomic m_ethConnected; + std::atomic m_wlanConnected; + std::atomic m_ethEnabled; + std::atomic m_wlanEnabled; + std::atomic m_ethDisconnectedForSleep; + std::atomic m_wlanDisconnectedForSleep; + GMainContext *m_nmContext{nullptr}; /* isolated context for per-call NMClient creation */ + /* Runtime connectivity backend selection (replaces the old + * USE_CONNECTIVITY_CHECK_MGR compile-time macro). When + * m_useConnectivityCheckMgr is true, connectivity queries are + * delegated to ConnectivityCheckMgr via connectivityClient; + * otherwise the built-in connectivityMonitor is used. */ + bool m_useConnectivityCheckMgr {false}; + mutable ConnectivityMonitor connectivityMonitor; +#ifdef USE_CONNECTIVITYCHECKMGR + mutable std::unique_ptr connectivityClient; +#endif + std::mutex m_bridgedStatusMutex; + Exchange::INetworkManager::InternetStatus m_lastBridgedInternetStatus {Exchange::INetworkManager::INTERNET_UNKNOWN}; + bool m_hasBridgedInternetStatus {false}; + + string getDefaultInterface() const + { + std::lock_guard lock(m_defaultInterfaceMutex); + return m_defaultInterface; + } + + void setDefaultInterface(const string& iface) + { + std::lock_guard lock(m_defaultInterfaceMutex); + m_defaultInterface = iface; + } + + void setLastConnectedSSID(const std::string& ssid) + { + std::lock_guard lock(m_lastConnectedSSIDMutex); + m_lastConnectedSSID = ssid; + } + + std::string getLastConnectedSSID() const + { + std::lock_guard lock(m_lastConnectedSSIDMutex); + return m_lastConnectedSSID; + } + + private: + string m_defaultInterface; + mutable std::mutex m_defaultInterfaceMutex; + std::string m_lastConnectedSSID; + mutable std::mutex m_lastConnectedSSIDMutex; + std::map, IpFamilyCache> m_ipCacheMap; + mutable std::mutex m_ipCacheMutex; + }; + } +} diff --git a/plugin/NetworkManagerJsonEnum.h b/plugin/NetworkManagerJsonEnum.h new file mode 100644 index 000000000..d47768e1a --- /dev/null +++ b/plugin/NetworkManagerJsonEnum.h @@ -0,0 +1,106 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#pragma once + +#include +#include "INetworkManager.h" + +namespace WPEFramework { + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::InterfaceType) + { Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_ETHERNET, _TXT("ETHERNET") }, + { Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_WIFI, _TXT("WIFI") }, + { Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_P2P, _TXT("P2P") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::InterfaceType) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::IPVersion) + { Exchange::INetworkManager::IPVersion::IP_ADDRESS_V4, _TXT("IPv4") }, + { Exchange::INetworkManager::IPVersion::IP_ADDRESS_V6, _TXT("IPv6") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::IPVersion) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::InternetStatus) + { Exchange::INetworkManager::InternetStatus::INTERNET_NOT_AVAILABLE, _TXT("NO_INTERNET") }, + { Exchange::INetworkManager::InternetStatus::INTERNET_LIMITED, _TXT("LIMITED_INTERNET") }, + { Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL, _TXT("CAPTIVE_PORTAL") }, + { Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED, _TXT("FULLY_CONNECTED") }, + { Exchange::INetworkManager::InternetStatus::INTERNET_UNKNOWN, _TXT("UNKNOWN") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::InternetStatus) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::WiFiWPS) + { Exchange::INetworkManager::WiFiWPS::WIFI_WPS_PBC, _TXT("PBC") }, + { Exchange::INetworkManager::WiFiWPS::WIFI_WPS_PIN, _TXT("PIN") }, + { Exchange::INetworkManager::WiFiWPS::WIFI_WPS_SERIALIZED_PIN, _TXT("SERIALIZED_PIN") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::WiFiWPS) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::WiFiState) + { Exchange::INetworkManager::WiFiState::WIFI_STATE_UNINSTALLED, _TXT("WIFI_STATE_UNINSTALLED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_DISABLED, _TXT("WIFI_STATE_DISABLED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED, _TXT("WIFI_STATE_DISCONNECTED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_PAIRING, _TXT("WIFI_STATE_PAIRING") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTING, _TXT("WIFI_STATE_CONNECTING") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED, _TXT("WIFI_STATE_CONNECTED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_SSID_NOT_FOUND, _TXT("WIFI_STATE_SSID_NOT_FOUND") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_SSID_CHANGED, _TXT("WIFI_STATE_SSID_CHANGED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTION_LOST, _TXT("WIFI_STATE_CONNECTION_LOST") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTION_FAILED, _TXT("WIFI_STATE_CONNECTION_FAILED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTION_INTERRUPTED, _TXT("WIFI_STATE_CONNECTION_INTERRUPTED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_INVALID_CREDENTIALS, _TXT("WIFI_STATE_INVALID_CREDENTIALS") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_AUTHENTICATION_FAILED, _TXT("WIFI_STATE_AUTHENTICATION_FAILED") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_ERROR, _TXT("WIFI_STATE_ERROR") }, + { Exchange::INetworkManager::WiFiState::WIFI_STATE_INVALID, _TXT("WIFI_STATE_INVALID") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::WiFiState) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::WiFiSignalQuality) + { Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED, _TXT("Disconnected") }, + { Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_WEAK, _TXT("Weak") }, + { Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_FAIR, _TXT("Fair") }, + { Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_GOOD, _TXT("Good") }, + { Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT, _TXT("Excellent") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::WiFiSignalQuality) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::Logging) + { Exchange::INetworkManager::Logging::LOG_LEVEL_FATAL, _TXT("LOG_LEVEL_FATAL") }, + { Exchange::INetworkManager::Logging::LOG_LEVEL_ERROR, _TXT("LOG_LEVEL_ERROR") }, + { Exchange::INetworkManager::Logging::LOG_LEVEL_WARNING, _TXT("LOG_LEVEL_WARNING") }, + { Exchange::INetworkManager::Logging::LOG_LEVEL_INFO, _TXT("LOG_LEVEL_INFO") }, + { Exchange::INetworkManager::Logging::LOG_LEVEL_DEBUG, _TXT("LOG_LEVEL_DEBUG") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::Logging) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::InterfaceState) + { Exchange::INetworkManager::InterfaceState::INTERFACE_ADDED, _TXT("INTERFACE_ADDED") }, + { Exchange::INetworkManager::InterfaceState::INTERFACE_LINK_UP, _TXT("INTERFACE_LINK_UP") }, + { Exchange::INetworkManager::InterfaceState::INTERFACE_LINK_DOWN, _TXT("INTERFACE_LINK_DOWN") }, + { Exchange::INetworkManager::InterfaceState::INTERFACE_ACQUIRING_IP, _TXT("INTERFACE_ACQUIRING_IP") }, + { Exchange::INetworkManager::InterfaceState::INTERFACE_REMOVED, _TXT("INTERFACE_REMOVED") }, + { Exchange::INetworkManager::InterfaceState::INTERFACE_DISABLED, _TXT("INTERFACE_DISABLED") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::InterfaceState) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::IPStatus) + { Exchange::INetworkManager::IPStatus::IP_LOST, _TXT("LOST") }, + { Exchange::INetworkManager::IPStatus::IP_ACQUIRED, _TXT("ACQUIRED") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::IPStatus) + +ENUM_CONVERSION_BEGIN(Exchange::INetworkManager::WIFIFrequency) + { Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_ALL, _TXT("ALL") }, + { Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_2_4_GHZ, _TXT("2.4") }, + { Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_5_GHZ, _TXT("5") }, + { Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_6_GHZ, _TXT("6") }, +ENUM_CONVERSION_END(Exchange::INetworkManager::WIFIFrequency) + +} diff --git a/plugin/NetworkManagerJsonRpc.cpp b/plugin/NetworkManagerJsonRpc.cpp new file mode 100644 index 000000000..6e309e6ff --- /dev/null +++ b/plugin/NetworkManagerJsonRpc.cpp @@ -0,0 +1,1177 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManager.h" +#include "INetworkManager.h" +#include "NetworkManagerJsonEnum.h" + +#include + +#define LOG_INPARAM() { string json; parameters.ToString(json); NMLOG_INFO("params=%s", json.c_str() ); } +#define LOG_OUTPARAM() { string json; response.ToString(json); NMLOG_INFO("response=%s", json.c_str() ); } + +#define returnJson(rc) \ + { \ + if (Core::ERROR_NONE == rc) \ + response["success"] = true; \ + else \ + response["success"] = false; \ + LOG_OUTPARAM(); \ + return Core::ERROR_NONE; \ + } + + +using namespace NetworkManagerLogger; + +namespace WPEFramework +{ + namespace Plugin + { + /** + * Hook up all our JSON RPC methods + * + * Each method definition comprises of: + * * Input parameters + * * Output parameters + * * Method name + * * Function that implements that method + */ + void NetworkManager::RegisterAllMethods() + { + Register("GetLogLevel", &NetworkManager::GetLogLevel, this); + Register("SetLogLevel", &NetworkManager::SetLogLevel, this); + Register("GetAvailableInterfaces", &NetworkManager::GetAvailableInterfaces, this); + Register("GetPrimaryInterface", &NetworkManager::GetPrimaryInterface, this); + Register("SetInterfaceState", &NetworkManager::SetInterfaceState, this); + Register("GetInterfaceState", &NetworkManager::GetInterfaceState, this); + Register("GetIPSettings", &NetworkManager::GetIPSettings, this); + Register("SetIPSettings", &NetworkManager::SetIPSettings, this); + Register("GetStunEndpoint", &NetworkManager::GetStunEndpoint, this); + Register("SetStunEndpoint", &NetworkManager::SetStunEndpoint, this); + Register("GetConnectivityTestEndpoints", &NetworkManager::GetConnectivityTestEndpoints, this); + Register("SetConnectivityTestEndpoints", &NetworkManager::SetConnectivityTestEndpoints, this); + Register("IsConnectedToInternet", &NetworkManager::IsConnectedToInternet, this); + Register("GetCaptivePortalURI", &NetworkManager::GetCaptivePortalURI, this); + Register("GetPublicIP", &NetworkManager::GetPublicIP, this); + Register("Ping", &NetworkManager::Ping, this); + Register("Trace", &NetworkManager::Trace, this); + Register("SetHostname", &NetworkManager::SetHostname, this); + Register("StartWiFiScan", &NetworkManager::StartWiFiScan, this); + Register("StopWiFiScan", &NetworkManager::StopWiFiScan, this); + Register("GetKnownSSIDs", &NetworkManager::GetKnownSSIDs, this); + Register("AddToKnownSSIDs", &NetworkManager::AddToKnownSSIDs, this); + Register("RemoveKnownSSID", &NetworkManager::RemoveKnownSSID, this); + Register("ConnectToKnownSSID", &NetworkManager::ConnectToKnownSSID, this); + Register("WiFiConnect", &NetworkManager::WiFiConnect, this); + Register("WiFiDisconnect", &NetworkManager::WiFiDisconnect, this); + Register("GetConnectedSSID", &NetworkManager::GetConnectedSSID, this); + Register("StartWPS", &NetworkManager::StartWPS, this); + Register("StopWPS", &NetworkManager::StopWPS, this); + Register("GetWifiState", &NetworkManager::GetWifiState, this); + Register("GetWiFiSignalQuality", &NetworkManager::GetWiFiSignalQuality, this); + Register("GetSupportedSecurityModes", &NetworkManager::GetSupportedSecurityModes, this); + } + + /** + * Unregister all our JSON-RPC methods + */ + void NetworkManager::UnregisterAllMethods() + { + Unregister("SetLogLevel"); + Unregister("GetAvailableInterfaces"); + Unregister("GetPrimaryInterface"); + Unregister("SetInterfaceState"); + Unregister("GetInterfaceState"); + Unregister("GetIPSettings"); + Unregister("SetIPSettings"); + Unregister("GetStunEndpoint"); + Unregister("SetStunEndpoint"); + Unregister("GetConnectivityTestEndpoints"); + Unregister("SetConnectivityTestEndpoints"); + Unregister("IsConnectedToInternet"); + Unregister("GetCaptivePortalURI"); + Unregister("GetPublicIP"); + Unregister("Ping"); + Unregister("Trace"); + Unregister("SetHostname"); + Unregister("StartWiFiScan"); + Unregister("StopWiFiScan"); + Unregister("GetKnownSSIDs"); + Unregister("AddToKnownSSIDs"); + Unregister("RemoveKnownSSID"); + Unregister("ConnectToKnownSSID"); + Unregister("WiFiConnect"); + Unregister("WiFiDisconnect"); + Unregister("GetConnectedSSID"); + Unregister("StartWPS"); + Unregister("StopWPS"); + Unregister("GetWifiState"); + Unregister("GetWiFiSignalQuality"); + Unregister("GetSupportedSecurityModes"); + } + + uint32_t NetworkManager::SetLogLevel (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + + uint32_t rc = Core::ERROR_GENERAL; + LogLevel level = INFO_LEVEL; + if (parameters.HasLabel("level")) + { + level = static_cast (parameters["level"].Number()); + + NetworkManagerLogger::SetLevel(level); + + const Exchange::INetworkManager::Logging log = static_cast (level); + if (_networkManager) + rc = _networkManager->SetLogLevel(log); + else + rc = Core::ERROR_UNAVAILABLE; + } + returnJson(rc); + } + + uint32_t NetworkManager::GetLogLevel (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + + uint32_t rc = Core::ERROR_NONE; + LogLevel level = INFO_LEVEL; + NetworkManagerLogger::GetLevel(level); + response["level"] = static_cast (level); + + returnJson(rc); + } + + uint32_t NetworkManager::GetAvailableInterfaces (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + Exchange::INetworkManager::IInterfaceDetailsIterator* _interfaces{}; + + const auto tStart = std::chrono::steady_clock::now(); + + if (_networkManager) + rc = _networkManager->GetAvailableInterfaces(_interfaces); + else + rc = Core::ERROR_UNAVAILABLE; + + const auto tAfterComRpc = std::chrono::steady_clock::now(); + const long long comRpcUs = static_cast(std::chrono::duration_cast(tAfterComRpc - tStart).count()); + NMLOG_DEBUG("[PERF] GetAvailableInterfaces COM-RPC call took %lld us", comRpcUs); + if (comRpcUs >= 1000000) + NMLOG_WARNING("[PERF] GetAvailableInterfaces COM-RPC call took %lld us (>= 1s)", comRpcUs); + + if (Core::ERROR_NONE == rc) + { + if (_interfaces != nullptr) + { + JsonArray array; + Exchange::INetworkManager::InterfaceDetails entry{}; + while (_interfaces->Next(entry) == true) + { + JsonObject each; + Core::JSON::EnumType type{entry.type}; + each["type"] = type.Data(); + each["name"] = entry.name; + each["mac"] = entry.mac; + each["enabled"] = entry.enabled; + each["connected"] = entry.connected; + + array.Add(JsonValue(each)); + } + + _interfaces->Release(); + response["interfaces"] = array; + } + } + + const auto tEnd = std::chrono::steady_clock::now(); + NMLOG_DEBUG("[PERF] GetAvailableInterfaces iterator drain+serialize took %lld us, total %lld us", + static_cast(std::chrono::duration_cast(tEnd - tAfterComRpc).count()), + static_cast(std::chrono::duration_cast(tEnd - tStart).count())); + + returnJson(rc); + } + + uint32_t NetworkManager::GetPrimaryInterface (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string interface; + + if (_networkManager) + rc = _networkManager->GetPrimaryInterface(interface); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["interface"] = interface; + } + returnJson(rc); + } + + uint32_t NetworkManager::SetInterfaceState(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + if (parameters.HasLabel("interface") && parameters.HasLabel("enabled")) + { + const string interface = parameters["interface"].String(); + const bool enabled = parameters["enabled"].Boolean(); + + if ("wlan0" != interface && "eth0" != interface) + rc = Core::ERROR_BAD_REQUEST; + else if (_networkManager) + rc = _networkManager->SetInterfaceState(interface, enabled); + else + rc = Core::ERROR_UNAVAILABLE; + } + else + rc = Core::ERROR_BAD_REQUEST; + + returnJson(rc); + } + + uint32_t NetworkManager::GetInterfaceState(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + const auto tStart = std::chrono::steady_clock::now(); + + if (parameters.HasLabel("interface")) + { + const string interface = parameters["interface"].String(); + bool enabled; + + if ("wlan0" != interface && "eth0" != interface) + rc = Core::ERROR_BAD_REQUEST; + else if (_networkManager) + rc = _networkManager->GetInterfaceState(interface, enabled); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["enabled"] = enabled; + } + else + rc = Core::ERROR_BAD_REQUEST; + + const long long comRpcUs = static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - tStart).count()); + NMLOG_DEBUG("[PERF] GetInterfaceState COM-RPC call took %lld us", comRpcUs); + if (comRpcUs >= 1000000) + NMLOG_WARNING("[PERF] GetInterfaceState COM-RPC call took %lld us (>= 1s)", comRpcUs); + + returnJson(rc); + } + + uint32_t NetworkManager::GetIPSettings (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IPAddress address{}; + + string interface{}; + string ipversion{}; + + if (parameters.HasLabel("interface")) + interface = parameters["interface"].String(); + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + if (_networkManager) + rc = _networkManager->GetIPSettings(interface, ipversion, address); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["interface"] = interface; + response["ipversion"] = address.ipversion; + response["autoconfig"] = address.autoconfig; + if (!address.ipaddress.empty()) + { + response["ipaddress"] = address.ipaddress; + response["prefix"] = address.prefix; + response["ula"] = address.ula; + response["dhcpserver"] = address.dhcpserver; + response["gateway"] = address.gateway; + response["primarydns"] = address.primarydns; + response["secondarydns"] = address.secondarydns; + } + } + returnJson(rc); + } + + uint32_t NetworkManager::SetIPSettings(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IPAddress address{}; + + string interface = {}; + + if (!parameters.HasLabel("interface") || !parameters.HasLabel("ipversion") || !parameters.HasLabel("autoconfig")) + rc = Core::ERROR_BAD_REQUEST; + else + { + interface = parameters["interface"].String(); + address.ipversion = parameters["ipversion"].String(); + address.autoconfig = parameters["autoconfig"].Boolean(); + + if (!address.autoconfig) + { + address.ipaddress = parameters["ipaddress"].String(); + address.prefix = parameters["prefix"].Number(); + address.gateway = parameters["gateway"].String(); + address.primarydns = parameters["primarydns"].String(); + address.secondarydns = parameters["secondarydns"].String(); + } + + if (_networkManager) + rc = _networkManager->SetIPSettings(interface, address); + else + rc = Core::ERROR_UNAVAILABLE; + } + returnJson(rc); + } + + uint32_t NetworkManager::GetStunEndpoint(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string endpoint; + uint32_t port; + uint32_t timeout; + uint32_t cacheLifetime; + + if (_networkManager) + rc = _networkManager->GetStunEndpoint(endpoint, port, timeout, cacheLifetime); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["endpoint"] = endpoint; + response["port"] = port; + response["timeout"] = timeout; + response["cacheLifetime"] = cacheLifetime; + } + returnJson(rc); + } + + uint32_t NetworkManager::SetStunEndpoint(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string endpoint = parameters["endpoint"].String(); + uint32_t port = parameters["port"].Number(); + uint32_t bindTimeout = parameters["timeout"].Number(); + uint32_t cacheTimeout = parameters["cacheLifetime"].Number(); + + if (_networkManager) + rc = _networkManager->SetStunEndpoint(endpoint, port, bindTimeout, cacheTimeout); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::GetConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IStringIterator* endpoints = NULL; + + if (_networkManager) + rc = _networkManager->GetConnectivityTestEndpoints(endpoints); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + if (endpoints) + { + JsonArray array; + string entry{}; + while (endpoints->Next(entry) == true) { array.Add(entry); } + + endpoints->Release(); + response["endpoints"] = array; + } + } + returnJson(rc); + } + + uint32_t NetworkManager::SetConnectivityTestEndpoints(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + ::WPEFramework::RPC::IIteratorType* endpointsIter{}; + JsonArray array = parameters["endpoints"].Array(); + + if (0 == array.Length() || 5 < array.Length()) + { + NMLOG_DEBUG("minimum of 1 to maximum of 5 Urls are allowed"); + returnJson(rc); + } + + std::vector endpoints; + JsonArray::Iterator index(array.Elements()); + while (index.Next() == true) + { + if (Core::JSON::Variant::type::STRING == index.Current().Content()) + { + endpoints.push_back(index.Current().String().c_str()); + } + else + { + NMLOG_DEBUG("Unexpected variant type"); + returnJson(rc); + } + } + endpointsIter = (Core::Service::Create(endpoints)); + if(endpointsIter == nullptr){ + returnJson(rc); + } + + if (_networkManager) + rc = _networkManager->SetConnectivityTestEndpoints(endpointsIter); + else + rc = Core::ERROR_UNAVAILABLE; + + if (endpointsIter) + endpointsIter->Release(); + + returnJson(rc); + } + + uint32_t NetworkManager::IsConnectedToInternet(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::InternetStatus result; + string ipversion{}; + string interface{}; + string reason{}; + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + if (parameters.HasLabel("interface")) + interface = parameters["interface"].String(); + + if (_networkManager) + rc = _networkManager->IsConnectedToInternet(ipversion, interface, result, reason); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + Core::JSON::EnumType status(result); + response["ipversion"] = ipversion; + response["interface"] = interface; + response["connected"] = (Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED == result + || (m_useConnectivityCheckMgr && + Exchange::INetworkManager::InternetStatus::INTERNET_LIMITED == result)); + response["state"] = JsonValue(status); + response["status"] = status.Data(); + if (result == Exchange::INetworkManager::InternetStatus::INTERNET_NOT_AVAILABLE && !reason.empty()) + response["reason"] = reason; + } + returnJson(rc); + } + + uint32_t NetworkManager::GetCaptivePortalURI(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string uri; + if (_networkManager) + rc = _networkManager->GetCaptivePortalURI(uri); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + response["uri"] = uri; + + returnJson(rc); + } + + uint32_t NetworkManager::GetPublicIP(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string interface{}; + string ipaddress{}; + string ipversion = "IPv4"; + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + if (parameters.HasLabel("interface")) + interface = parameters["interface"].String(); + + if (_networkManager) + rc = _networkManager->GetPublicIP(interface, ipversion, ipaddress); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["interface"] = interface; + response["ipaddress"] = ipaddress; + response["ipversion"] = ipversion; + + m_publicIPAddress = ipaddress; + m_publicIPAddressType = ipversion; + if (!m_publicIPAddress.empty()) + { + PublishToThunderAboutInternet(); + } + } + returnJson(rc); + } + + void NetworkManager::PublishToThunderAboutInternet() + { + PluginHost::ISubSystem* subSystem = _service->SubSystems(); + if (subSystem != nullptr) + { + const PluginHost::ISubSystem::IInternet* internet(subSystem->Get()); + if (nullptr == internet) + { + NMLOG_INFO("Setting INTERNET ISubSystem"); + subSystem->Set(PluginHost::ISubSystem::INTERNET, this); + } + + subSystem->Release(); + } + else + { + NMLOG_ERROR("Failed to get ISubSystem interface"); + } + } + + uint32_t NetworkManager::Ping(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + string result{}; + string endpoint{}; + uint32_t rc = Core::ERROR_GENERAL; + if (parameters.HasLabel("endpoint")) + { + string guid{}; + string ipversion{"IPv4"}; + uint32_t noOfRequest = 1; + uint16_t timeOutInSeconds = 1; + + endpoint = parameters["endpoint"].String(); + + if (parameters.HasLabel("ipversion")) + ipversion = parameters["ipversion"].String(); + + if (parameters.HasLabel("count")) + noOfRequest = parameters["count"].Number(); + + if (parameters.HasLabel("timeout")) + timeOutInSeconds = parameters["timeout"].Number(); + + if (parameters.HasLabel("guid")) + guid = parameters["guid"].String(); + + if (_networkManager) + rc = _networkManager->Ping(ipversion, endpoint, noOfRequest, timeOutInSeconds, guid, result); + else + rc = Core::ERROR_UNAVAILABLE; + } + + if (Core::ERROR_NONE == rc) + { + JsonObject reply; + reply.FromString(result); + response = reply; + } + LOG_OUTPARAM(); + return rc; + } + + uint32_t NetworkManager::Trace(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string result{}; + + if (parameters.HasLabel("endpoint")) + { + const string ipversion = parameters["ipversion"].String(); + const string endpoint = parameters["endpoint"].String(); + const uint32_t noOfRequest = parameters["packets"].Number(); + const string guid = parameters["guid"].String(); + + if (_networkManager) + rc = _networkManager->Trace(ipversion, endpoint, noOfRequest, guid, result); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + JsonObject reply; + reply.FromString(result); + response = reply; + } + } + returnJson(rc); + } + + uint32_t NetworkManager::SetHostname (const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string hostname{}; + + if (parameters.HasLabel("hostname") && !parameters["hostname"].String().empty()) + { + hostname = parameters["hostname"].String(); + if (_networkManager) + rc = _networkManager->SetHostname(hostname); + else + rc = Core::ERROR_UNAVAILABLE; + } + else + { + NMLOG_ERROR("Invalid hostname length: %zu", hostname.length()); + rc = Core::ERROR_BAD_REQUEST; + } + + returnJson(rc); + } + + uint32_t NetworkManager::StartWiFiScan(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::IStringIterator* frequencies = nullptr; + Exchange::INetworkManager::IStringIterator* ssids = NULL; + + if (parameters.HasLabel("frequencies")) + { + JsonArray array = parameters["frequencies"].Array(); + std::vector frequencyList; + JsonArray::Iterator index(array.Elements()); + + while (index.Next() == true) + { + if (Core::JSON::Variant::type::STRING == index.Current().Content()) + { + frequencyList.push_back(index.Current().String()); + } + else + { + NMLOG_ERROR("Unexpected variant type in frequency array."); + returnJson(rc); + } + } + frequencies = Core::Service::Create(frequencyList); + if (frequencies == nullptr) { + returnJson(rc); + } + } + + if (parameters.HasLabel("ssids")) + { + JsonArray array = parameters["ssids"].Array(); + std::vector ssidslist; + JsonArray::Iterator index(array.Elements()); + + while (index.Next() == true) + { + if (Core::JSON::Variant::type::STRING == index.Current().Content()) + { + ssidslist.push_back(index.Current().String().c_str()); + } + else + { + NMLOG_ERROR("Unexpected variant type in SSID array."); + if (frequencies) + frequencies->Release(); + returnJson(rc); + } + } + ssids = (Core::Service::Create(ssidslist)); + if(ssids == nullptr){ + if (frequencies) + frequencies->Release(); + returnJson(rc); + } + } + + if (_networkManager) + rc = _networkManager->StartWiFiScan(frequencies, ssids); + else + rc = Core::ERROR_UNAVAILABLE; + + if (frequencies) + frequencies->Release(); + + if (ssids) + ssids->Release(); + + + returnJson(rc); + } + + uint32_t NetworkManager::StopWiFiScan(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + if (_networkManager) + rc = _networkManager->StopWiFiScan(); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::GetKnownSSIDs(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + ::WPEFramework::RPC::IIteratorType* _ssids{}; + + if (_networkManager) + rc = _networkManager->GetKnownSSIDs(_ssids); + + if (Core::ERROR_NONE == rc) + { + if (_ssids != nullptr) + { + JsonArray ssids; + string _resultItem_{}; + while (_ssids->Next(_resultItem_) == true) { ssids.Add() = _resultItem_; } + _ssids->Release(); + + response["ssids"] = ssids; + } + } + returnJson(rc); + } + + static inline void logWifiParameters(const JsonObject& parameters) + { + string json; + JsonObject ssidObj = JsonObject(parameters); + if (ssidObj.HasLabel("passphrase")) + ssidObj["passphrase"] = std::string("*******"); + if (ssidObj.HasLabel("private_key")) + ssidObj["private_key"] = std::string("*******"); + if (ssidObj.HasLabel("private_key_passwd")) + ssidObj["private_key_passwd"] = std::string("*******"); + ssidObj.ToString(json); + NMLOG_INFO("params=%s", json.c_str() ); + } + + uint32_t NetworkManager::AddToKnownSSIDs(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiConnectTo ssid{}; + logWifiParameters(parameters); + + if (parameters.HasLabel("ssid") && parameters.HasLabel("passphrase")) + { + ssid.ssid = parameters["ssid"].String(); + ssid.passphrase = parameters["passphrase"].String(); + ssid.security = static_cast (parameters["security"].Number()); + + if (_networkManager) + rc = _networkManager->AddToKnownSSIDs(ssid); + else + rc = Core::ERROR_UNAVAILABLE; + } + + returnJson(rc); + } + + uint32_t NetworkManager::RemoveKnownSSID(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string ssid{}; + + if (parameters.HasLabel("ssid")) + ssid = parameters["ssid"].String(); + + if (_networkManager) + rc = _networkManager->RemoveKnownSSID(ssid); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::ConnectToKnownSSID(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string ssid{}; + + if (parameters.HasLabel("ssid")) + ssid = parameters["ssid"].String(); + + if (ssid.empty()) { + NMLOG_WARNING("ssid not provided or empty in ConnectToKnownSSID request!"); + rc = Core::ERROR_BAD_REQUEST; + } + else if (_networkManager) { + rc = _networkManager->ConnectToKnownSSID(ssid); + } + else { + rc = Core::ERROR_UNAVAILABLE; + } + + returnJson(rc); + } + + uint32_t NetworkManager::WiFiConnect(const JsonObject& parameters, JsonObject& response) + { + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiConnectTo ssid{}; + logWifiParameters(parameters); + + if (parameters.HasLabel("ssid")) + { + ssid.ssid = parameters["ssid"].String(); + + if (parameters.HasLabel("passphrase")) + ssid.passphrase = parameters["passphrase"].String(); + + if (parameters.HasLabel("bssid")) + ssid.bssid = parameters["bssid"].String(); + if (parameters.HasLabel("frequency")) + { + int freq = parameters["frequency"].Number(); + if (freq < 1 || freq > 3) { + NMLOG_WARNING("Invalid frequency value: %d (valid: 1=2.4GHz, 2=5GHz, 3=6GHz)", freq); + rc = Core::ERROR_BAD_REQUEST; + returnJson(rc); + } + ssid.frequency = static_cast (freq); + } + + if (parameters.HasLabel("security")) + ssid.security= static_cast (parameters["security"].Number()); + + // Check Security modes + if (parameters.HasLabel("eap")) + ssid.eap = parameters["eap"].String(); + if (parameters.HasLabel("eap_identity")) + ssid.eap_identity = parameters["eap_identity"].String(); + if (parameters.HasLabel("ca_cert")) + ssid.ca_cert = parameters["ca_cert"].String(); + if (parameters.HasLabel("client_cert")) + ssid.client_cert = parameters["client_cert"].String(); + if (parameters.HasLabel("private_key")) + ssid.private_key = parameters["private_key"].String(); + if (parameters.HasLabel("private_key_passwd")) + ssid.private_key_passwd = parameters["private_key_passwd"].String(); + if (parameters.HasLabel("persist")) + ssid.persist = parameters["persist"].Boolean(); + else + ssid.persist = true; + } + else + NMLOG_WARNING("ssid not included in wifi connect request !"); + + if (_networkManager) + rc = _networkManager->WiFiConnect(ssid); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::WiFiDisconnect(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + if (_networkManager) + rc = _networkManager->WiFiDisconnect(); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::GetConnectedSSID(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + + if (_networkManager) + rc = _networkManager->GetConnectedSSID(ssidInfo); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + response["ssid"] = ssidInfo.ssid; + response["bssid"] = ssidInfo.bssid; + response["security"] = JsonValue(ssidInfo.security); + response["strength"] = ssidInfo.strength; + response["frequency"] = ssidInfo.frequency; + response["rate"] = ssidInfo.rate; + response["noise"] = ssidInfo.noise; + } + returnJson(rc); + } + + uint32_t NetworkManager::StartWPS(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string wps_pin{}; + Core::JSON::EnumType method; + + if (parameters.HasLabel("method")) + { + if (parameters["method"].Content() == WPEFramework::Core::JSON::Variant::type::STRING) + method.FromString(parameters["method"].String()); + else if (parameters["method"].Content() == WPEFramework::Core::JSON::Variant::type::NUMBER) + method = static_cast (parameters["method"].Number()); + + if ((Exchange::INetworkManager::WIFI_WPS_PIN == method) && parameters.HasLabel("pin")) + { + wps_pin = parameters["pin"].String(); + } + } + + if (_networkManager) + rc = _networkManager->StartWPS(method, wps_pin); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::StopWPS(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + + if (_networkManager) + rc = _networkManager->StopWPS(); + else + rc = Core::ERROR_UNAVAILABLE; + + returnJson(rc); + } + + uint32_t NetworkManager::GetWifiState(const JsonObject& parameters, JsonObject& response) + { + Exchange::INetworkManager::WiFiState state; + uint32_t rc = Core::ERROR_GENERAL; + + LOG_INPARAM(); + if (_networkManager) + rc = _networkManager->GetWifiState(state); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + Core::JSON::EnumType iState{state}; + response["state"] = JsonValue(state); + response["status"] = iState.Data(); + } + + returnJson(rc); + } + + uint32_t NetworkManager::GetWiFiSignalQuality(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + string ssid{}; + int strength = 0; + int noise = 0; + int snr = 0; + Exchange::INetworkManager::WiFiSignalQuality quality{}; + + if (_networkManager) + rc = _networkManager->GetWiFiSignalQuality(ssid, strength, noise, snr, quality); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + Core::JSON::EnumType iquality(quality); + response["ssid"] = ssid; + response["quality"] = iquality.Data(); + response["snr"] = snr; + response["strength"] = strength; + response["noise"] = noise; + } + returnJson(rc); + } + + uint32_t NetworkManager::GetSupportedSecurityModes(const JsonObject& parameters, JsonObject& response) + { + LOG_INPARAM(); + uint32_t rc = Core::ERROR_GENERAL; + Exchange::INetworkManager::ISecurityModeIterator* securityModes{}; + + if (_networkManager) + rc = _networkManager->GetSupportedSecurityModes(securityModes); + else + rc = Core::ERROR_UNAVAILABLE; + + if (Core::ERROR_NONE == rc) + { + if (securityModes != nullptr) + { + JsonObject modes{}; + Exchange::INetworkManager::WIFISecurityModeInfo _resultItem_{}; + while (securityModes->Next(_resultItem_) == true) + { + modes.Set(_resultItem_.securityName.c_str(), JsonValue(_resultItem_.security)); + } + response["security"] = modes; + securityModes->Release(); + } + } + returnJson(rc); + } + + void NetworkManager::onInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface) + { + Core::JSON::EnumType iState{state}; + JsonObject parameters; + parameters["state"] = JsonValue(state); + parameters["status"] = iState.Data(); + parameters["interface"] = interface; + + LOG_INPARAM(); + Notify(_T("onInterfaceStateChange"), parameters); + } + + void NetworkManager::onActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface) + { + JsonObject parameters; + parameters["prevActiveInterface"] = prevActiveInterface; + parameters["currentActiveInterface"] = currentActiveinterface; + + LOG_INPARAM(); + Notify(_T("onActiveInterfaceChange"), parameters); + } + + void NetworkManager::onIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status) + { + Core::JSON::EnumType iStatus{status}; + JsonObject parameters; + parameters["interface"] = interface; + parameters["ipversion"] = ipversion; + parameters["ipaddress"] = ipaddress; + parameters["status"] = iStatus.Data(); + + LOG_INPARAM(); + Notify(_T("onIPAddressChange"), parameters); + } + + void NetworkManager::onRouteChange(const string interface, const string ipversion, const string ipaddress, const string gateway, const string primarydns) + { + JsonObject parameters; + parameters["interface"] = interface; + parameters["ipversion"] = ipversion; + parameters["ipaddress"] = ipaddress; + parameters["gateway"] = gateway; + parameters["primarydns"] = primarydns; + + LOG_INPARAM(); + Notify(_T("onRouteChange"), parameters); + } + + void NetworkManager::onInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string reason) + { + JsonObject parameters; + Core::JSON::EnumType prevStatus(prevState); + Core::JSON::EnumType currStatus(currState); + parameters["prevState"] = JsonValue(prevState); + parameters["prevStatus"] = prevStatus.Data(); + parameters["state"] = JsonValue(currState); + parameters["status"] = currStatus.Data(); + parameters["interface"] = interface; + if (currState == Exchange::INetworkManager::INTERNET_NOT_AVAILABLE && !reason.empty()) { + parameters["reason"] = reason; + } + + LOG_INPARAM(); + Notify(_T("onInternetStatusChange"), parameters); + /* if new state is fully connectd set the internet subsystem only once */ + static bool isInternetSet = false; + if(!isInternetSet && currState == Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED) + { + PublishToThunderAboutInternet(); + isInternetSet = true; + } + } + + void NetworkManager::onAvailableSSIDs(const string jsonOfScanResults) + { + JsonObject parameters; + JsonArray scanResults; + scanResults.FromString(jsonOfScanResults); + parameters["ssids"] = scanResults; + + NMLOG_INFO("Event with %d SSIDs as, %s", scanResults.Length(), jsonOfScanResults.c_str()); + Notify(_T("onAvailableSSIDs"), parameters); + } + + void NetworkManager::onWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid) + { + JsonObject parameters; + Core::JSON::EnumType iState{state}; + parameters["state"] = JsonValue(state); + parameters["status"] = iState.Data(); + parameters["ssid"] = ssid; + + LOG_INPARAM(); + Notify(_T("onWiFiStateChange"), parameters); + } + + void NetworkManager::onWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality) + { + Core::JSON::EnumType iquality(quality); + JsonObject parameters; + parameters["ssid"] = ssid; + parameters["quality"] = iquality.Data(); + parameters["snr"] = snr; + parameters["strength"] = strength; + parameters["noise"] = noise; + + LOG_INPARAM(); + Notify(_T("onWiFiSignalQualityChange"), parameters); + } + } +} diff --git a/plugin/NetworkManagerLogger.cpp b/plugin/NetworkManagerLogger.cpp new file mode 100644 index 000000000..99aa51bde --- /dev/null +++ b/plugin/NetworkManagerLogger.cpp @@ -0,0 +1,157 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManagerLogger.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef USE_RDK_LOGGER +#include "rdk_debug.h" +#endif + +namespace NetworkManagerLogger { + static LogLevel gDefaultLogLevel = INFO_LEVEL; + + +#ifdef USE_RDK_LOGGER + rdk_LogLevel mapTordkLogLevel(LogLevel level) + { + rdk_LogLevel rdklevel = RDK_LOG_INFO; + switch (level) + { + case FATAL_LEVEL: + rdklevel = RDK_LOG_FATAL; + break; + case ERROR_LEVEL: + rdklevel = RDK_LOG_ERROR; + break; + case WARNING_LEVEL: + rdklevel = RDK_LOG_WARN; + break; + case INFO_LEVEL: + rdklevel = RDK_LOG_INFO; + break; + case DEBUG_LEVEL: + rdklevel = RDK_LOG_DEBUG; + break; + } + return rdklevel; + } +#endif + + const char* trimPath(const char* s) + { + if (!s) + return s; + + const char* t = strrchr(s, (int) '/'); + if (t) t++; + if (!t) t = s; + + return t; + } + + void Init() + { +#ifdef USE_RDK_LOGGER + rdk_logger_init(0 == access("/opt/debug.ini", R_OK) ? "/opt/debug.ini" : "/etc/debug.ini"); +#endif + } + + void logPrint(LogLevel level, const char* file, const char* func, int line, const char* format, ...) + { + // Gate on level before formatting so disabled logs incur no vsnprintf cost. +#ifdef USE_RDK_LOGGER + if (!rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, mapTordkLogLevel(level))) + return; +#else + if (gDefaultLogLevel < level) + return; +#endif + + size_t n = 0; + const short kFormatMessageSize = 1024; + char formattedLog[kFormatMessageSize] = {0}; + + va_list args; + + va_start(args, format); + n = vsnprintf(formattedLog, (kFormatMessageSize - 1), format, args); + va_end(args); + + if (n > (kFormatMessageSize - 1)) + { + formattedLog[kFormatMessageSize - 4] = '.'; + formattedLog[kFormatMessageSize - 3] = '.'; + formattedLog[kFormatMessageSize - 2] = '.'; + } + formattedLog[kFormatMessageSize - 1] = '\0'; +#ifdef USE_RDK_LOGGER + RDK_LOG(mapTordkLogLevel(level), RDKLOGGER_MODULE_NAME, "[%s +%d] %s : %s\n", trimPath(file), line, func, formattedLog); +#else + const char* levelMap[] = {"Fatal", "Error", "Warn", "Info", "Debug"}; + struct timeval tv; + struct tm* lt; + const char* fileName = trimPath(file); + + gettimeofday(&tv, NULL); + lt = localtime(&tv.tv_sec); + + printf("%.2d:%.2d:%.2d.%.6lld [%-5s] [PID=%d] [TID=%d] [%s +%d] %s : %s\n", lt->tm_hour, lt->tm_min, lt->tm_sec, (long long int)tv.tv_usec, levelMap[level], getpid(), gettid(), fileName, line, func, formattedLog); + fflush(stdout); +#endif + } + + void SetLevel(LogLevel level) + { + gDefaultLogLevel = level; + NMLOG_INFO("NetworkManager logLevel:%d", level); +#ifdef USE_RDK_LOGGER + rdk_logger_set_logLevel(RDKLOGGER_MODULE_NAME, mapTordkLogLevel(level)); +#endif + } + + void GetLevel(LogLevel& level) + { +#ifdef USE_RDK_LOGGER + // Query RDK Logger for the current effective level by probing from most to least verbose + if (rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, RDK_LOG_DEBUG)) + level = DEBUG_LEVEL; + else if (rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, RDK_LOG_INFO)) + level = INFO_LEVEL; + else if (rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, RDK_LOG_WARN)) + level = WARNING_LEVEL; + else if (rdk_logger_is_logLevel_enabled(RDKLOGGER_MODULE_NAME, RDK_LOG_ERROR)) + level = ERROR_LEVEL; + else + level = FATAL_LEVEL; + gDefaultLogLevel = level; +#else + level = gDefaultLogLevel; +#endif + } +} // namespace NetworkManagerLogger diff --git a/NetworkManagerLogger.h b/plugin/NetworkManagerLogger.h similarity index 57% rename from NetworkManagerLogger.h rename to plugin/NetworkManagerLogger.h index 2441c9920..b5484c35c 100644 --- a/NetworkManagerLogger.h +++ b/plugin/NetworkManagerLogger.h @@ -1,25 +1,29 @@ -/* - * If not stated otherwise in this file or this component's LICENSE file the - * following copyright and licenses apply: - * - * Copyright 2020 RDK Management - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ #ifndef NM_LOGGER_H #define NM_LOGGER_H +#ifdef USE_RDK_LOGGER +#define RDKLOGGER_MODULE_NAME "LOG.RDK.NWMGR" +#endif + #include #include #include @@ -30,11 +34,11 @@ namespace NetworkManagerLogger { /** * Logging level with an increasing order of refinement - * (TRACE_LEVEL = Finest logging) + * (DEBUG_LEVEL = Finest logging) * It is essental to start with 0 and increase w/o gaps as the value * can be used for indexing in a mapping table. */ -enum LogLevel {FATAL_LEVEL = 0, ERROR_LEVEL, WARNING_LEVEL, INFO_LEVEL, VERBOSE_LEVEL, TRACE_LEVEL}; +enum LogLevel {FATAL_LEVEL = 0, ERROR_LEVEL, WARNING_LEVEL, INFO_LEVEL, DEBUG_LEVEL}; /** * @brief Init logging @@ -47,6 +51,11 @@ void Init(); */ void SetLevel(LogLevel level); +/** + * @brief To set log level while runtime + */ +void GetLevel(LogLevel& level); + /** * @brief Log a message * The function is defined by logging backend. @@ -55,13 +64,14 @@ void SetLevel(LogLevel level); void logPrint(LogLevel level, const char* file, const char* func, int line, const char* format, ...) __attribute__ ((format (printf, 5, 6))); -#define NMLOG_TRACE(FMT, ...) logPrint(NetworkManagerLogger::TRACE_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) -#define NMLOG_VERBOSE(FMT, ...) logPrint(NetworkManagerLogger::VERBOSE_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) +#define NMLOG_DEBUG(FMT, ...) logPrint(NetworkManagerLogger::DEBUG_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) #define NMLOG_INFO(FMT, ...) logPrint(NetworkManagerLogger::INFO_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) #define NMLOG_WARNING(FMT, ...) logPrint(NetworkManagerLogger::WARNING_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) #define NMLOG_ERROR(FMT, ...) logPrint(NetworkManagerLogger::ERROR_LEVEL, __FILE__, __func__, __LINE__, FMT, ##__VA_ARGS__) #define NMLOG_FATAL(FMT, ...) logPrint(NetworkManagerLogger::FATAL_LEVEL, __FILE__,__func__, __LINE__, FMT, ##__VA_ARGS__) +#define LOG_ENTRY_FUNCTION() { NMLOG_INFO("Entering %s", __func__); } + } // namespace NetworkManagerLogger #endif // NM_LOGGER_H diff --git a/plugin/NetworkManagerPowerClient.cpp b/plugin/NetworkManagerPowerClient.cpp new file mode 100644 index 000000000..94a8694b4 --- /dev/null +++ b/plugin/NetworkManagerPowerClient.cpp @@ -0,0 +1,304 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManagerPowerClient.h" +#include "NetworkManagerLogger.h" +#include + +using namespace WPEFramework; +using namespace WPEFramework::Exchange; +using namespace WPEFramework::Plugin; + +// --------------------------------------------------------------------------- +// NetworkManagerPowerClient +// --------------------------------------------------------------------------- + +NetworkManagerPowerClient::NetworkManagerPowerClient(INetworkPowerCallback& callback) + : mCallback(callback) + , mPreChangeNotification(*this) + , mChangedNotification(*this) +{ + NMLOG_INFO("NetworkManagerPowerClient ctor"); + if (auto r = Open(RPC::CommunicationTimeOut, Connector(), "org.rdk.PowerManager"); r == Core::ERROR_NONE) { + // Connected; Operational() will be called by the framework when the proxy is ready + } else { + NMLOG_ERROR("failed to open link to PowerManager (error %u)", r); + } + NMLOG_INFO("NetworkManagerPowerClient ctor ends"); +} + +NetworkManagerPowerClient::~NetworkManagerPowerClient() +{ + NMLOG_INFO("shutting down"); + // Stop the power-event thread first so any in-flight work completes + // before we release the COM-RPC proxy. + mStopThread = true; + mQueueCv.notify_one(); + if (mPowerThread.joinable()) { + mPowerThread.join(); + } + unregisterEvents(); + Close(Core::infinite); +} + +bool NetworkManagerPowerClient::IsValid() const +{ + LOG_ENTRY_FUNCTION(); + + return mPowerManager != nullptr; +} + +bool NetworkManagerPowerClient::getNetworkStandbyMode() const +{ + LOG_ENTRY_FUNCTION(); + + bool standbyMode = false; + if (IsValid()) { + if (auto r = mPowerManager->GetNetworkStandbyMode(standbyMode); r != Core::ERROR_NONE) { + NMLOG_ERROR("GetNetworkStandbyMode failed (%u)", r); + } + } + return standbyMode; +} + +void NetworkManagerPowerClient::sendPowerModePreChangeComplete(int transactionId) +{ + LOG_ENTRY_FUNCTION(); + + if (IsValid()) { + if (mClientId == 0) { + NMLOG_ERROR("sendPowerModePreChangeComplete called with invalid clientId=0, skipping"); + return; + } + NMLOG_DEBUG("sending PowerModePreChangeComplete for transactionId=%d, mClientId=%u", transactionId, mClientId); + mPowerManager->PowerModePreChangeComplete(mClientId, transactionId); + } +} + +void NetworkManagerPowerClient::sendDelayPowerModeChange(int transactionId, int seconds) +{ + LOG_ENTRY_FUNCTION(); + + if (IsValid()) { + if (mClientId == 0) { + NMLOG_ERROR("sendDelayPowerModeChange called with invalid clientId=0, skipping"); + return; + } + if (auto r = mPowerManager->DelayPowerModeChangeBy(mClientId, transactionId, seconds); r != Core::ERROR_NONE) { + NMLOG_ERROR("DelayPowerModeChangeBy failed (%u)", r); + } + } +} + +void NetworkManagerPowerClient::Operational(bool upAndRunning) +{ + NMLOG_DEBUG("Operational(%s)", upAndRunning ? "true" : "false"); + if (upAndRunning) { + if (!IsValid()) { + mPowerManager = Interface(); + registerEvents(); + // Start the dedicated power-event thread after registration so it + // is ready to handle events as soon as they can arrive. + mStopThread = false; + mPowerThread = std::thread(&NetworkManagerPowerClient::powerThreadLoop, this); + } + } else { + // Stop the power-event thread before unregistering so any in-flight + // event that was already enqueued is drained. + mStopThread = true; + mQueueCv.notify_one(); + if (mPowerThread.joinable()) { + mPowerThread.join(); + } + unregisterEvents(); + } +} + +void NetworkManagerPowerClient::registerEvents() +{ + NMLOG_DEBUG("registering events"); + if (!IsValid()) { + NMLOG_ERROR("not in valid state, skipping event registration"); + return; + } + if (auto r = mPowerManager->AddPowerModePreChangeClient("org.rdk.NetworkManager", mClientId); r != Core::ERROR_NONE) { + NMLOG_ERROR("AddPowerModePreChangeClient failed (%u) — skipping pre-change sink", r); + // mClientId stays 0; do NOT register mPreChangeNotification + } else { + NMLOG_INFO("registered as pre-change client, mClientId=%u", mClientId); + if (auto r2 = mPowerManager->Register(&mPreChangeNotification); r2 != Core::ERROR_NONE) { + NMLOG_ERROR("register(preChange) failed (%u)", r2); + } + } + if (auto r = mPowerManager->Register(&mChangedNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("register(changed) failed (%u)", r); + } +} + +void NetworkManagerPowerClient::unregisterEvents() +{ + NMLOG_DEBUG("unregistering events"); + if (!IsValid()) { + NMLOG_ERROR("not in valid state, skipping event unregistration"); + return; + } + // NOTE: RemovePowerModePreChangeClient MUST be called before Unregister + if (mClientId != 0) { + if (auto r = mPowerManager->RemovePowerModePreChangeClient(mClientId); r != Core::ERROR_NONE) { + NMLOG_ERROR("removePowerModePreChangeClient failed (%u)", r); + } + if (auto r = mPowerManager->Unregister(&mPreChangeNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("unregister(preChange) failed (%u)", r); + } + mClientId = 0; + } + if (auto r = mPowerManager->Unregister(&mChangedNotification); r != Core::ERROR_NONE) { + NMLOG_ERROR("unregister(changed) failed (%u)", r); + } + + mPowerManager->Release(); + mPowerManager = nullptr; +} + +// --------------------------------------------------------------------------- +// Power event thread +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::powerThreadLoop() +{ + NMLOG_DEBUG("power event thread started"); + while (true) { + PowerEvent event{}; + { + std::unique_lock lock(mQueueMutex); + mQueueCv.wait(lock, [this]{ return !mEventQueue.empty() || mStopThread.load(); }); + + if (mStopThread) { + // Drain remaining events with fast acks before exiting so + // PowerManager is never left waiting on a stale transaction. + // CHANGED events have no ack protocol — skip them. + std::vector pending; + while (!mEventQueue.empty()) { + pending.push_back(mEventQueue.front()); + mEventQueue.pop(); + } + lock.unlock(); + for (const auto& e : pending) { + if (e.type == PowerEvent::EventType::PRE_CHANGE) { + sendPowerModePreChangeComplete(e.transactionId); + } + } + break; + } + + event = mEventQueue.front(); + mEventQueue.pop(); + } + // Lock released — process event on this thread (blocking is fine here) + + const bool toDeepSleep = (event.newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP); + const bool fromDeepSleep = (event.currentState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP); + + if (event.type == PowerEvent::EventType::CHANGED) { + // Wakeup notification — no ack required. + if (fromDeepSleep && event.standbyMode) { + NMLOG_INFO("power thread — wakeup from DeepSleep standby ON"); + mCallback.OnPowerModeChanged(event.currentState, event.newState); + } else { + NMLOG_DEBUG("power thread — CHANGED event, no action (fromDeepSleep=%d networkStandbyMode=%d)", + fromDeepSleep, event.standbyMode); + } + continue; + } + + // PRE_CHANGE event processing below + auto sendAck = [transactionId = event.transactionId, this]() { + sendPowerModePreChangeComplete(transactionId); + }; + + if ((toDeepSleep || fromDeepSleep) && !event.standbyMode) { + // Deep-sleep transition with Network Standby OFF: delegate to + // NetworkManagerImplementation (WiFiDisconnect / reconnect) which + // will call sendAck() when done. + NMLOG_INFO("power thread — %s DeepSleep standby OFF", + toDeepSleep ? "to" : "from"); + mCallback.OnPowerModePreChange(event.currentState, event.newState, sendAck); + } else { + // standby ON or non-DeepSleep: no WiFi action needed, ack immediately. + NMLOG_DEBUG("power thread ack (standbyMode=%d toDeepSleep=%d fromDeepSleep=%d)", + event.standbyMode, toDeepSleep, fromDeepSleep); + sendAck(); + } + } + NMLOG_INFO("power event thread stopped"); +} + +// --------------------------------------------------------------------------- +// PreChangeNotification +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::PreChangeNotification::OnPowerModePreChange( + const PowerState currentState, const PowerState newState, + const int transactionId, const int stateChangeAfter) +{ + NMLOG_DEBUG("OnPowerModePreChange current=%d new=%d txId=%d after=%ds", + static_cast(currentState), static_cast(newState), transactionId, stateChangeAfter); + + // Query standby mode + const bool standbyMode = mClient.getNetworkStandbyMode(); + + // Cache for use by ChangedNotification + mClient.mLastChangeStandbyMode = standbyMode; + + // Reserve a delay window now (before returning) so PowerManager knows to + // wait at least 5 s. + if (newState == PowerState::POWER_STATE_STANDBY_DEEP_SLEEP && !standbyMode) { + mClient.sendDelayPowerModeChange(transactionId, 5); + } + + // Enqueue and return immediately so the COM-RPC dispatcher thread is freed. + { + std::lock_guard lock(mClient.mQueueMutex); + mClient.mEventQueue.push(PowerEvent{PowerEvent::EventType::PRE_CHANGE, + currentState, newState, standbyMode, transactionId}); + } + mClient.mQueueCv.notify_one(); +} + +// --------------------------------------------------------------------------- +// ChangedNotification +// --------------------------------------------------------------------------- + +void NetworkManagerPowerClient::ChangedNotification::OnPowerModeChanged( + const PowerState currentState, const PowerState newState) +{ + NMLOG_DEBUG("OnPowerModeChanged current=%d new=%d", + static_cast(currentState), static_cast(newState)); + + // Use the cached standby mode + const bool standbyMode = mClient.mLastChangeStandbyMode; + + // Enqueue and return immediately so the COM-RPC dispatcher thread is freed. + { + std::lock_guard lock(mClient.mQueueMutex); + mClient.mEventQueue.push(PowerEvent{PowerEvent::EventType::CHANGED, + currentState, newState, standbyMode, 0}); + } + mClient.mQueueCv.notify_one(); +} diff --git a/plugin/NetworkManagerPowerClient.h b/plugin/NetworkManagerPowerClient.h new file mode 100644 index 000000000..73949e0eb --- /dev/null +++ b/plugin/NetworkManagerPowerClient.h @@ -0,0 +1,175 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include "Module.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WPEFramework { +namespace Plugin { + +/** + * Callback interface that decouples NetworkManagerPowerClient from + * NetworkManagerImplementation. The implementation receives power state + * transitions and must call sendAck() exactly once per OnPowerModePreChange. + */ +struct INetworkPowerCallback { + virtual ~INetworkPowerCallback() = default; + + /** + * Called when a power mode pre-change event arrives that involves + * POWER_STATE_STANDBY_DEEP_SLEEP (either as the new state or the current + * state). The implementation MUST call sendAck() exactly once. + */ + virtual void OnPowerModePreChange(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState, + std::function sendAck) = 0; + + /** + * Called when a power mode changed event arrives (informational only; + * no ack required). + */ + virtual void OnPowerModeChanged(const Exchange::IPowerManager::PowerState currentState, + const Exchange::IPowerManager::PowerState newState) = 0; +}; + +/** + * - Inherits SmartInterfaceType for automatic + * reconnect / Operational() lifecycle callbacks. + * - Registers as an AddPowerModePreChangeClient so it participates + * in the pre-change ack protocol. + * - Delegates DeepSleep transitions to INetworkPowerCallback. + * - Sends a fast-path PowerModePreChangeComplete for all other transitions. + * + * Lifecycle: + * Construction → Open() connects to PowerManager (async). + * Operational(true) → registers events; IsValid() returns true. + * Operational(false) → unregisters events and releases proxy. + * Destruction → unregisterEvents() then Close() . + */ +class NetworkManagerPowerClient : protected RPC::SmartInterfaceType { +public: + using PowerState = Exchange::IPowerManager::PowerState; + + explicit NetworkManagerPowerClient(INetworkPowerCallback& callback); + ~NetworkManagerPowerClient() override; + + NetworkManagerPowerClient(const NetworkManagerPowerClient&) = delete; + NetworkManagerPowerClient& operator=(const NetworkManagerPowerClient&) = delete; + + /** Returns true when the PowerManager COMRPC proxy is available. */ + bool IsValid() const; + + /** Queries the current Network Standby mode from PowerManager. */ + bool getNetworkStandbyMode() const; + + /** Sends PowerModePreChangeComplete to PowerManager. */ + void sendPowerModePreChangeComplete(int transactionId); + + /** Requests a delay window extension via DelayPowerModeChangeBy. */ + void sendDelayPowerModeChange(int transactionId, int seconds); + +private: + // ----------------------------------------------------------------------- + // IModePreChangeNotification sink + // ----------------------------------------------------------------------- + class PreChangeNotification : public Exchange::IPowerManager::IModePreChangeNotification { + public: + explicit PreChangeNotification(NetworkManagerPowerClient& client) + : mClient(client) {} + + void OnPowerModePreChange(const PowerState currentState, const PowerState newState, + const int transactionId, const int stateChangeAfter) override; + + BEGIN_INTERFACE_MAP(PreChangeNotification) + INTERFACE_ENTRY(Exchange::IPowerManager::IModePreChangeNotification) + END_INTERFACE_MAP + + private: + NetworkManagerPowerClient& mClient; + }; + + // ----------------------------------------------------------------------- + // IModeChangedNotification sink + // ----------------------------------------------------------------------- + class ChangedNotification : public Exchange::IPowerManager::IModeChangedNotification { + public: + explicit ChangedNotification(NetworkManagerPowerClient& client) : mClient(client) {} + + void OnPowerModeChanged(const PowerState currentState, const PowerState newState) override; + + BEGIN_INTERFACE_MAP(ChangedNotification) + INTERFACE_ENTRY(Exchange::IPowerManager::IModeChangedNotification) + END_INTERFACE_MAP + + private: + NetworkManagerPowerClient& mClient; + }; + + // ----------------------------------------------------------------------- + // SmartInterfaceType lifecycle callback + // ----------------------------------------------------------------------- + void Operational(bool upAndRunning) override; + + void registerEvents(); + void unregisterEvents(); + void powerThreadLoop(); + + + // ----------------------------------------------------------------------- + // Members + // ----------------------------------------------------------------------- + INetworkPowerCallback& mCallback; + Exchange::IPowerManager* mPowerManager{nullptr}; + Core::Sink mPreChangeNotification; + Core::Sink mChangedNotification; + uint32_t mClientId{0}; + + // Power-event thread: receives events enqueued by the COM-RPC dispatcher + // thread and processes them (WiFiDisconnect etc.) without blocking the + // dispatcher. + struct PowerEvent { + enum class EventType { PRE_CHANGE, CHANGED }; + + EventType type; + PowerState currentState; + PowerState newState; + bool standbyMode; + int transactionId; + }; + + std::thread mPowerThread; + std::queue mEventQueue; + std::mutex mQueueMutex; + std::condition_variable mQueueCv; + std::atomic mStopThread{false}; + // Cached standby mode from the last PRE_CHANGE event. + std::atomic mLastChangeStandbyMode{false}; +}; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/NetworkManagerSecretAgent.cpp b/plugin/NetworkManagerSecretAgent.cpp new file mode 100644 index 000000000..c63f977ae --- /dev/null +++ b/plugin/NetworkManagerSecretAgent.cpp @@ -0,0 +1,378 @@ + +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2022 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "NetworkManagerLogger.h" +#include "NetworkManagerSecretAgent.h" + +namespace WPEFramework +{ + namespace Plugin + { + std::condition_variable m_SecretsAgentCv; + std::mutex m_SecretsAgentMutex; + bool cvCondition = false; // avoid spurious wakeup + static const gchar interfaceXml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + + static void handleSecretsAgentMethods( GDBusConnection *connection, const gchar *sender, + const gchar *object_path, const gchar *interface_name, const gchar *method_name, + GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { + + NMLOG_DEBUG("handleSecretsAgentMethods called for sender: %s", sender); + char *string = g_variant_print(parameters, TRUE); + if(string != NULL) { + NMLOG_DEBUG("GVariant: %s", string); + g_free(string); + } + + if (g_strcmp0(method_name, "GetSecrets") == 0) + { + GVariant *settingConn = NULL; + GVariant *connectionPath = NULL; + GVariant *settingName = NULL; + GVariant *hints = NULL; + GVariant *flagVar = NULL; + guint flags=0; + + g_variant_get(parameters, "(@a{sa{sv}}@o@s@as@u)", &settingConn, &connectionPath, &settingName, &hints, &flagVar); + + if (g_variant_is_of_type (connectionPath, G_VARIANT_TYPE_OBJECT_PATH)) { + const char* connPath = g_variant_get_string(connectionPath, NULL); + NMLOG_DEBUG("connection path %s", connPath); + } + + if (g_variant_is_of_type (flagVar, G_VARIANT_TYPE_UINT32)) + { + std::string flagStr; + flags = g_variant_get_uint32(flagVar); + if(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_WPS_PBC_ACTIVE) + flagStr += ", wps_pbc_active"; + if(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) { + flagStr += ", allow_interaction"; + /* wait for 10 sec mean time in the background networkmanager will do wps operation */ + SecretAgent::wait(10); // 10 sec + } + if(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_USER_REQUESTED) + flagStr += ", user_requested"; + + NMLOG_INFO("SecretAgent GetSecrets Flags = %u %s", flags, flagStr.c_str()); + } + + if(settingConn) + g_variant_unref(settingConn); + if(connectionPath) + g_variant_unref(connectionPath); + if(hints) + g_variant_unref(hints); + if(flagVar) + g_variant_unref(flagVar); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(a{sa{sv}})", NULL)); + } + else if (g_strcmp0(method_name, "CancelGetSecrets") == 0) + { + NMLOG_DEBUG("CancelGetSecrets method called"); + g_dbus_method_invocation_return_value(invocation, NULL); + } + else if (g_strcmp0(method_name, "SaveSecrets") == 0) + { + NMLOG_DEBUG("SaveSecrets method called"); + g_dbus_method_invocation_return_value(invocation, NULL); + } + else if (g_strcmp0(method_name, "DeleteSecrets") == 0) + { + NMLOG_DEBUG("DeleteSecrets method called"); + g_dbus_method_invocation_return_value(invocation, NULL); + } + + NMLOG_DEBUG("method: %s", method_name); + } + + static const GDBusInterfaceVTable interfaceVtable = { + .method_call = handleSecretsAgentMethods, + .get_property = NULL, + .set_property = NULL, + }; + + SecretAgent::SecretAgent() + { + GError *error = NULL; + isSecretAgentLoopRunning = false; + isSecurityAgentRegistered = false; + NMLOG_INFO("SecretAgent Constructor"); + GDBusconn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!GDBusconn) { + NMLOG_ERROR("Failed to connect to system bus: %s", error->message); + g_error_free(error); + return; + } + + startSecurityAgent(); + } + + SecretAgent::~SecretAgent() + { + NMLOG_INFO("SecretAgent Destructor"); + stopSecurityAgent(); + if(GDBusconn) + g_object_unref(GDBusconn); + } + + void SecretAgent::wait(int timeoutInSec) + { + NMLOG_INFO("wait started %d Sec", timeoutInSec); + std::unique_lock lock(m_SecretsAgentMutex); + if (m_SecretsAgentCv.wait_for(lock, std::chrono::seconds(timeoutInSec), [] { return cvCondition; })) { + NMLOG_INFO("SecretAgent received a cancel request. skipping %d sec wait", timeoutInSec); + // Reset the flag to avoid spurious wakeup on a condition variable + cvCondition = false; + } else { + NMLOG_INFO("SecretAgent wait time %d sec complete", timeoutInSec); + } + } + + void SecretAgent::stopWait() + { + NMLOG_INFO("stopWait Entry"); + { + std::unique_lock lock(m_SecretsAgentMutex); + cvCondition = true; + } + m_SecretsAgentCv.notify_one(); + NMLOG_INFO("stopWait Exit"); + } + + void SecretAgent::SecretAgentLoop(SecretAgent* SecretAgentPtr) + { + GError *error = NULL; + GMainContext *secretAgentContext = NULL; + SecretAgent* agent = static_cast(SecretAgentPtr); + secretAgentContext = g_main_context_new(); + if(secretAgentContext == NULL) + { + NMLOG_ERROR("Failed to create Security Agent gmain loop context"); + return; + } + + g_main_context_push_thread_default(secretAgentContext); + agent->agentGmainLoop = g_main_loop_new(secretAgentContext, FALSE); + if(agent->agentGmainLoop == NULL) + { + NMLOG_ERROR("Failed to create Security Agent gmain loop"); + g_main_context_pop_thread_default(secretAgentContext); + g_main_context_unref(secretAgentContext); + return; + } + + GDBusNodeInfo *node_info = g_dbus_node_info_new_for_xml(interfaceXml, &error); + if (!node_info) { + NMLOG_ERROR("Failed to parse XML: %s", error->message); + g_error_free(error); + return; + } + + guint agentRegID = g_dbus_connection_register_object( + agent->GDBusconn, + "/org/freedesktop/NetworkManager/SecretAgent", + g_dbus_node_info_lookup_interface(node_info, "org.freedesktop.NetworkManager.SecretAgent"), // Interface, + &interfaceVtable, + NULL, + NULL, + &error); + + if (!agentRegID) { + NMLOG_ERROR("Failed to register object:"); + if (error != NULL) + { + NMLOG_ERROR("Error : %s", error->message); + g_error_free(error); + } + g_dbus_node_info_unref(node_info); + return; + } + + NMLOG_INFO("Object registered with ID: %u", agentRegID); + NMLOG_DEBUG("Security Agent gmain loop running..."); + g_main_loop_run(agent->agentGmainLoop); + + g_main_loop_unref(agent->agentGmainLoop); + g_dbus_connection_unregister_object(agent->GDBusconn, agentRegID); + g_dbus_node_info_unref(node_info); + g_main_context_pop_thread_default(secretAgentContext); + g_main_context_unref(secretAgentContext); + agent->isSecretAgentLoopRunning = false; + NMLOG_WARNING("Security Agent loop ended !"); + } + + void SecretAgent::startSecurityAgent() + { + NMLOG_DEBUG("staring Security Agent..."); + if(isSecretAgentLoopRunning) + { + NMLOG_INFO("Security Agent already running."); + return; + } + + isSecretAgentLoopRunning = true; + agentThrID = std::thread(&SecretAgentLoop, this); + } + + void SecretAgent::stopSecurityAgent() + { + stopWait(); + if (agentGmainLoop) { + g_main_loop_quit(agentGmainLoop); + } + + if (agentThrID.joinable()) { + agentThrID.join(); + } + + NMLOG_INFO("Security Agent stopped."); + } + + bool SecretAgent::RegisterAgent() + { + if(isSecurityAgentRegistered) + { + NMLOG_INFO("'rdk.nm.agent' alreday registered to NetworkManager"); + return true; + } + + GError *error = NULL; + GDBusProxy *nmAgentProxy = g_dbus_proxy_new_for_bus_sync ( + G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/AgentManager", + "org.freedesktop.NetworkManager.AgentManager", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_error_free(error); + if(nmAgentProxy) + g_object_unref(nmAgentProxy); + return false; + } + + GVariant *result = g_dbus_proxy_call_sync( nmAgentProxy, "Register", + g_variant_new("(s)", "rdk.nm.agent"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + + if (error != NULL) { + NMLOG_ERROR("Failed to register agent: %s", error->message); + g_error_free(error); + return false; + } + + if (result != NULL) { + g_variant_unref(result); + } + + NMLOG_INFO("'rdk.nm.agent' registered to NetworkManager"); + isSecurityAgentRegistered = true; + + if(nmAgentProxy) + g_object_unref(nmAgentProxy); + return true; + } + + bool SecretAgent::UnregisterAgent() + { + if(!isSecurityAgentRegistered) + { + NMLOG_INFO("'rdk.nm.agent' not registered to NetworkManager"); + return true; + } + + GError *error = NULL; + GDBusProxy *nmAgentProxy = g_dbus_proxy_new_for_bus_sync ( + G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/AgentManager", + "org.freedesktop.NetworkManager.AgentManager", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_error_free(error); + if(nmAgentProxy) + g_object_unref(nmAgentProxy); + return false; + } + + GVariant *result = g_dbus_proxy_call_sync(nmAgentProxy, "Unregister", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + + if (error != NULL) { + NMLOG_FATAL("Failed to call Unregister agent: %s", error->message); + g_error_free(error); + return false; + } + if (result != NULL) { + g_variant_unref(result); + } + + NMLOG_INFO("'rdk.nm.agent' successfully Unregister"); + isSecurityAgentRegistered = false; + if(nmAgentProxy) + g_object_unref(nmAgentProxy); + return true; + } + + } // WPEFramework +} // Plugin diff --git a/NetworkManagerGnomeUtils.h b/plugin/NetworkManagerSecretAgent.h similarity index 50% rename from NetworkManagerGnomeUtils.h rename to plugin/NetworkManagerSecretAgent.h index a66d6643c..816f05196 100644 --- a/NetworkManagerGnomeUtils.h +++ b/plugin/NetworkManagerSecretAgent.h @@ -18,33 +18,36 @@ */ #pragma once -#include -#include -#include +#include #include -#include -#include "Module.h" +#include +#include + +#include "NetworkManagerLogger.h" +#include "INetworkManager.h" namespace WPEFramework { namespace Plugin { - class nmUtils + class SecretAgent { - public: - static uint32_t GetInterfacesName(string &wifiInterface, string ðernetInterface); - static uint8_t wifiSecurityModeFromAp(guint32 flags, guint32 wpaFlags, guint32 rsnFlags); - static std::string wifiFrequencyFromAp(guint32 apFreq); - static std::string getSecurityModeString(guint32 flags, guint32 wpaFlags, guint32 rsnFlags); - static JsonObject apToJsonObject(NMAccessPoint *ap); - static void printActiveSSIDsOnly(NMDeviceWifi *wifiDevice); + SecretAgent(); + ~SecretAgent(); + void startSecurityAgent(); + void stopSecurityAgent(); + bool RegisterAgent(); + bool UnregisterAgent(); + static void SecretAgentLoop(SecretAgent* agent); + static void wait(int timeout); + static void stopWait(); + private: + std::atomic isSecretAgentLoopRunning; + GMainLoop *agentGmainLoop = nullptr; + GDBusConnection *GDBusconn; + std::thread agentThrID; + std::atomic isSecurityAgentRegistered; }; - - } -} - -/* - // Set up a timer to check the device state reason every second - g_timeout_add_seconds(1, check_device_state_reason, NULL); - */ + } // Plugin +} // WPEFramework diff --git a/NetworkManagerStunClient.cpp b/plugin/NetworkManagerStunClient.cpp similarity index 90% rename from NetworkManagerStunClient.cpp rename to plugin/NetworkManagerStunClient.cpp index 999f14e88..65eaa8c12 100644 --- a/NetworkManagerStunClient.cpp +++ b/plugin/NetworkManagerStunClient.cpp @@ -1,3 +1,21 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2021 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ #include "NetworkManagerStunClient.h" #include #include @@ -51,7 +69,7 @@ namespace details { int m_fd; }; - #ifdef _STUN_DEBUG +#ifdef _STUN_DEBUG void dump_buffer(char const * prefix, buffer const & buff) { if (prefix) @@ -61,14 +79,7 @@ namespace details { printf("\n"); return; } - #endif - - #ifdef _STUN_DEBUG - #define STUN_TRACE(format, ...) printf("STUN:" format __VA_OPT__(,) __VA_ARGS__) - #else - #define STUN_TRACE(format, ...) - #endif - +#endif void throw_error(char const * format, ...) { @@ -126,7 +137,7 @@ namespace details { details::throw_error("failed to find ip for interface:%s", iface.c_str()); return iface_info; } - STUN_TRACE("local_addr:%s\n", sockaddr_to_string(iface_info).c_str()); + NMLOG_DEBUG("local_addr:%s", sockaddr_to_string(iface_info).c_str()); return iface_info; } @@ -290,7 +301,6 @@ client::client() , m_cache_timeout(30) , m_last_cache_time() , m_last_result() - , m_verbose(true) , m_fd(-1) { } @@ -350,7 +360,7 @@ bool client::bind( dirty = true; } - verbose("client::bind enter: server=%s port=%u iface=%s ipv6=%u timeout=%u cache_timeout=%u dirty=%u\n", + NMLOG_DEBUG("client::bind enter: server=%s port=%u iface=%s ipv6=%u timeout=%u cache_timeout=%u dirty=%u", hostname.c_str(), port, interface.c_str(), proto == stun::protocol::af_inet6, bind_timeout, cache_timeout, dirty); if(m_cache_timeout > 0 /*asking if caching is enabled*/ @@ -360,18 +370,18 @@ bool client::bind( auto time_in_cache = std::chrono::duration_cast( std::chrono::steady_clock::now() - m_last_cache_time); - verbose("client::bind cache time=%lld\n", time_in_cache.count()); + NMLOG_DEBUG("client::bind cache time=%lld", (long long int) time_in_cache.count()); if(time_in_cache.count() < m_cache_timeout) { result = m_last_result; - verbose("client::bind returning cached result: %s\n", result.public_ip.c_str()); + NMLOG_DEBUG("client::bind returning cached result: %s", result.public_ip.c_str()); return true; } else { - verbose("client::client::bind cached result expired\n"); + NMLOG_DEBUG("client::client::bind cached result expired"); } } @@ -387,7 +397,7 @@ bool client::bind( std::chrono::milliseconds wait_time(interval_wait_time); for (int i = 0; i < num_attempts && total_time < m_bind_timeout; ++i) { - verbose("client::bind sending bind request\n"); + NMLOG_DEBUG("client::bind sending bind request"); std::unique_ptr binding_response = send_binding_request(wait_time); @@ -415,25 +425,25 @@ bool client::bind( m_last_cache_time = std::chrono::steady_clock::now(); - verbose("client::bind success: public_ip=%s\n", result.public_ip.c_str()); + NMLOG_DEBUG("client::bind success: public_ip=%s", result.public_ip.c_str()); ret_ok = true; } else { - verbose("client::bind failed: ip missing from binding response\n"); + NMLOG_DEBUG("client::bind failed: ip missing from binding response"); } } else { - verbose("client::bind failed: no response received from server\n"); + NMLOG_INFO("client::bind failed: no response received from server"); } } } #ifdef __cpp_exceptions catch (std::exception const & err) { - verbose("client::bind failed: %s\n", err.what()); + NMLOG_WARNING("client::bind failed: %s", err.what()); } #endif @@ -454,13 +464,13 @@ void client::create_udp_socket(int inet_family) if (inet_family != AF_INET && inet_family != AF_INET6) details::throw_error("invalid inet family:%d", inet_family); - verbose("creating udp/%s socket\n", details::family_to_string(inet_family)); + NMLOG_DEBUG("creating udp/%s socket", details::family_to_string(inet_family)); int soc = socket(inet_family, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (soc < 0) details::throw_error("error creating socket. %s", strerror(errno)); - #ifdef _SUN_USE_MSGHDR + #ifdef _STUN_USE_MSGHDR int optval = 1; setsockopt(soc, IPPROTO_IP, IP_PKTINFO, &optval, sizeof(int)); #endif @@ -468,7 +478,7 @@ void client::create_udp_socket(int inet_family) if (!m_interface.empty()) { sockaddr_storage local_addr = details::get_interface_address(m_interface, inet_family); - verbose("binding to local interface %s/%s\n", m_interface.c_str(), + NMLOG_DEBUG("binding to local interface %s/%s", m_interface.c_str(), sockaddr_to_string(local_addr).c_str()); int ret = ::bind(soc, reinterpret_cast(&local_addr), details::socket_length(local_addr)); @@ -479,18 +489,16 @@ void client::create_udp_socket(int inet_family) sockaddr_to_string(local_addr).c_str(), strerror(err)); } else { - if (m_verbose) { sockaddr_storage local_endpoint; socklen_t socklen = sizeof(sockaddr_storage); int ret = getsockname(soc, reinterpret_cast(&local_endpoint), &socklen); if (ret == 0) - verbose("local endpoint %s/%d\n", sockaddr_to_string(local_endpoint).c_str(), + NMLOG_DEBUG("local endpoint %s/%d", sockaddr_to_string(local_endpoint).c_str(), details::sockaddr_get_port(local_endpoint)); - } } } else - verbose("no local interface supplied to bind to\n"); + NMLOG_DEBUG("no local interface supplied to bind to"); if (m_fd != -1) close(m_fd); @@ -506,13 +514,13 @@ message * client::send_message(sockaddr_storage const & remote_addr, message con buffer bytes = req.encode(); - STUN_TRACE("remote_addr:%s\n", sockaddr_to_string(remote_addr).c_str()); + NMLOG_DEBUG("remote_addr:%s", sockaddr_to_string(remote_addr).c_str()); #ifdef _STUN_DEBUG details::dump_buffer("STUN >>> ", bytes); #endif - verbose("sending messsage\n"); + NMLOG_DEBUG("sending messsage"); ssize_t n = sendto(m_fd, &bytes[0], bytes.size(), 0, (sockaddr *) &remote_addr, details::socket_length(remote_addr)); if (n < 0) @@ -537,10 +545,10 @@ message * client::send_message(sockaddr_storage const & remote_addr, message con timeout.tv_sec = (timeout.tv_usec / kMicrosecondsPerSecond); timeout.tv_usec -= (timeout.tv_sec * kMicrosecondsPerSecond); } - verbose("waiting for response, timeout set to %lus - %luus\n", timeout.tv_sec, timeout.tv_usec); + NMLOG_DEBUG("waiting for response, timeout set to %lus - %luus", timeout.tv_sec, timeout.tv_usec); int ret = select(m_fd + 1, &rfds, nullptr, nullptr, &timeout); if (ret == 0) { - STUN_TRACE("select timeout out\n"); + NMLOG_DEBUG("select timeout out"); return nullptr; } @@ -608,18 +616,6 @@ message * client::send_message(sockaddr_storage const & remote_addr, message con return decoder::decode_message(bytes, nullptr); } -void client::verbose(char const * format, ...) -{ - if (!m_verbose) - return; - va_list ap; - va_start(ap, format); - printf("STUN:"); - vprintf(format, ap); - va_end(ap); - return; -} - network_access_type client::discover_network_access_type(server const & srv) { std::chrono::milliseconds wait_time(250); @@ -672,7 +668,7 @@ std::unique_ptr client::send_binding_request(std::chrono::milliseconds std::unique_ptr client::send_binding_request(sockaddr_storage const & addr, std::chrono::milliseconds wait_time) { - this->verbose("sending binding request with wait time:%lld ms\n", wait_time.count()); + NMLOG_DEBUG("sending binding request with wait time:%lld ms", (long long int) wait_time.count()); this->create_udp_socket(addr.ss_family); std::unique_ptr binding_request(message_factory::create_binding_request()); std::unique_ptr binding_response(this->send_message(addr, *binding_request, wait_time)); diff --git a/NetworkManagerStunClient.h b/plugin/NetworkManagerStunClient.h similarity index 86% rename from NetworkManagerStunClient.h rename to plugin/NetworkManagerStunClient.h index c2f69297a..c1a1203ac 100644 --- a/NetworkManagerStunClient.h +++ b/plugin/NetworkManagerStunClient.h @@ -1,3 +1,21 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ #ifndef STUN_CLIENT_H #define STUN_CLIENT_H @@ -8,6 +26,7 @@ #include #include #include +#include "NetworkManagerLogger.h" namespace stun { @@ -157,11 +176,7 @@ class client { network_access_type discover_network_access_type(server const & srv); - inline void set_verbose(bool b) { - m_verbose = b; - } private: - void verbose(char const * format, ...) __attribute__((format(printf, 2, 3))); void create_udp_socket(int inet_family); std::unique_ptr send_binding_request(std::chrono::milliseconds wait_time); @@ -180,7 +195,6 @@ class client { uint16_t m_cache_timeout; std::chrono::time_point m_last_cache_time; bind_result m_last_result; - bool m_verbose; int m_fd; }; diff --git a/NetworkManagerTimer.h b/plugin/NetworkManagerTimer.h similarity index 99% rename from NetworkManagerTimer.h rename to plugin/NetworkManagerTimer.h index 5fbeea352..5c8b5df17 100644 --- a/NetworkManagerTimer.h +++ b/plugin/NetworkManagerTimer.h @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2020 RDK Management +* Copyright 2023 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugin/gnome/NetworkManagerGnomeEvents.cpp b/plugin/gnome/NetworkManagerGnomeEvents.cpp new file mode 100644 index 000000000..56a71625e --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeEvents.cpp @@ -0,0 +1,1099 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Module.h" +#include "NetworkManagerGnomeEvents.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerGnomeUtils.h" +#include "NetworkManagerImplementation.h" +#include "INetworkManager.h" +#include +#include + +#ifdef ENABLE_MIGRATION_MFRMGR_SUPPORT +#include "NetworkManagerGnomeMfrMgr.h" +#endif + +namespace WPEFramework +{ + namespace Plugin + { + + extern NetworkManagerImplementation* _instance; + static GnomeNetworkManagerEvents *_nmEventInstance = nullptr; + + /* Gnome-owned interface-state cache (raw NMDeviceState + MAC per interface). + Written only by the event monitor; read by the proxy's pure readers. */ + static std::map _ifaceStateCache; + static std::mutex _ifaceStateCacheMutex; + + bool GnomeNetworkManagerEvents::isInterfaceStateEnabled(NMDeviceState state) + { + return (state >= NM_DEVICE_STATE_UNAVAILABLE); + } + + bool GnomeNetworkManagerEvents::isInterfaceStateConnected(NMDeviceState state) + { + return (state > NM_DEVICE_STATE_DISCONNECTED && state < NM_DEVICE_STATE_DEACTIVATING); + } + + void GnomeNetworkManagerEvents::updateInterfaceStateCache(const std::string& iface, NMDeviceState state, const char* mac) + { + if (iface.empty()) + return; + std::lock_guard lock(_ifaceStateCacheMutex); + InterfaceStateInfo& info = _ifaceStateCache[iface]; + info.state = state; + info.present = true; + if (mac != nullptr && mac[0] != '\0') + info.mac = mac; // keep in sync when NM reports a new/randomized HW address + } + + void GnomeNetworkManagerEvents::removeInterfaceStateCache(const std::string& iface) + { + std::lock_guard lock(_ifaceStateCacheMutex); + _ifaceStateCache.erase(iface); + } + + bool GnomeNetworkManagerEvents::getInterfaceStateCache(const std::string& iface, InterfaceStateInfo& out) + { + std::lock_guard lock(_ifaceStateCacheMutex); + auto it = _ifaceStateCache.find(iface); + if (it == _ifaceStateCache.end() || !it->second.present) + return false; + out = it->second; + return true; + } + + static void primaryConnectionCb(NMClient *client, GParamSpec *param, NMEvents *nmEvents) + { + NMActiveConnection *primaryConn; + const char *activeConnId = NULL; + const char *connectionTyp = NULL; + primaryConn = nm_client_get_primary_connection(client); + nmEvents->activeConn = primaryConn; + std::string newIface ="unknown"; + if (primaryConn) + { + activeConnId = nm_active_connection_get_id(primaryConn); + connectionTyp = nm_active_connection_get_connection_type(primaryConn); + NMLOG_INFO("active connection - %s (%s)", activeConnId, connectionTyp); + + if (0 == strncmp("802-3-ethernet", connectionTyp, sizeof("802-3-ethernet")) && string("eth0_missing") != nmUtils::ethIface()) + newIface = nmUtils::ethIface(); + else if(0 == strncmp("802-11-wireless", connectionTyp, sizeof("802-11-wireless")) && string("wlan0_missing") != nmUtils::wlanIface()) + newIface = nmUtils::wlanIface(); + else { + NMLOG_WARNING("Active connection not valid: Ethernet/WiFi ID: %s", connectionTyp); + return; // if not good don't report the evnet + } + + GnomeNetworkManagerEvents::onActiveInterfaceChangeCb(newIface); + } + else + { + GnomeNetworkManagerEvents::onActiveInterfaceChangeCb(newIface); + NMLOG_WARNING("now there's no active connection"); + } + } + + /* Refresh the per-interface/per-family IP cache from current libnm state and + emit acquired/lost events for address-set differences. + + Build a fresh IpFamilyCache from current libnm state for one device/family, + swap it into _instance under the cache mutex, then emit acquired/lost events + for any address-set differences outside the lock. */ + static void refreshIpFamilyCache(NMDevice* device, bool isIPv6) + { + if (!device || !NM_IS_DEVICE(device) || !_instance) + return; + + const char* iface = nm_device_get_iface(device); + if (!iface) return; + std::string ifname = iface; + + bool isEth = (ifname == nmUtils::ethIface()); + bool isWlan = (ifname == nmUtils::wlanIface()); + if (!isEth && !isWlan) return; + + /* Build the new snapshot locally (no locks held during NM calls). + * Skip the NM read when the device is in a disconnected/down state + * so that newCache stays empty and the diff emits IP_LOST for every + * address still in the cache. This also prevents spurious + * "IP acquired" events from intermediate NM signals (nameserver, + * gateway clearing) that fire after the cache has been emptied + * but before NM clears addresses on the config object. */ + NMDeviceState devState = nm_device_get_state(device); + bool skipRead = (devState <= NM_DEVICE_STATE_DISCONNECTED); + IpFamilyCache newCache; + NMActiveConnection* conn = skipRead ? nullptr : nm_device_get_active_connection(device); + if (conn) { + /* autoconfig: method "auto" or "dhcp" → true */ + NMConnection* nmConn = NM_CONNECTION(nm_active_connection_get_connection(conn)); + if (nmConn) { + NMSettingIPConfig* ipSetting = isIPv6 + ? NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip6_config(nmConn)) + : NM_SETTING_IP_CONFIG(nm_connection_get_setting_ip4_config(nmConn)); + if (ipSetting) { + const char* method = nm_setting_ip_config_get_method(ipSetting); + newCache.autoconfig = method && + (g_strcmp0(method, "auto") == 0 || g_strcmp0(method, "dhcp") == 0); + } + } + } + + /* IP config read is device-level and does not require an active connection. */ + NMIPConfig* ipConfig = skipRead ? nullptr + : (isIPv6 ? nm_device_get_ip6_config(device) + : nm_device_get_ip4_config(device)); + + if (ipConfig) { + GPtrArray* ipAddresses = nm_ip_config_get_addresses(ipConfig); + std::string macAddr; + if (isIPv6) { + const char* hw = nm_device_get_hw_address(device); + if (hw) macAddr = hw; + } + if (ipAddresses) { + for (guint i = 0; i < ipAddresses->len; i++) { + NMIPAddress* addr = (NMIPAddress*)g_ptr_array_index(ipAddresses, i); + if (!addr) continue; + const char* addrStr = nm_ip_address_get_address(addr); + if (!addrStr) continue; + std::string addrString = addrStr; + uint32_t prefix = nm_ip_address_get_prefix(addr); + if (isIPv6) { + if (isIPv6LinkLocal(addrString)) { + newCache.linkLocalAddresses.insert(addrString); + } else if (isIPv6ULA(addrString)) { + newCache.uniqueLocalAddresses.insert(addrString); + } else { + GlobalAddressType type = (!macAddr.empty() && isIPv6MacBased(addrString, macAddr)) + ? ADDR_GLOBAL_MAC_BASED : ADDR_GLOBAL; + newCache.globalAddresses.emplace(addrString, GlobalAddressInfo{prefix, type}); + } + } else { + if (isIPv4LinkLocal(addrString)) { + newCache.linkLocalAddresses.insert(addrString); + } else { + newCache.globalAddresses.emplace(addrString, GlobalAddressInfo{prefix, ADDR_GLOBAL}); + } + } + } + } + + const char* gw = nm_ip_config_get_gateway(ipConfig); + if (gw) newCache.gateway = gw; + + const char* const* dnsArr = nm_ip_config_get_nameservers(ipConfig); + if (dnsArr && dnsArr[0]) { + newCache.primarydns = dnsArr[0]; + if (dnsArr[1]) newCache.secondarydns = dnsArr[1]; + } + + NMDhcpConfig* dhcpConfig = isIPv6 + ? nm_device_get_dhcp6_config(device) + : nm_device_get_dhcp4_config(device); + if (dhcpConfig) { + const char* server = nm_dhcp_config_get_one_option(dhcpConfig, "dhcp_server_identifier"); + if (server) newCache.dhcpserver = server; + } + + newCache.valid = true; + } + + /* Swap new snapshot into instance cache; collect old global address keys for diff. */ + std::set newKeys; + for (const auto& address : newCache.globalAddresses) { + newKeys.insert(address.first); + } + + std::string family = isIPv6 ? "IPv6" : "IPv4"; + const bool routeReady = newCache.valid + && !newCache.globalAddresses.empty() + && !newCache.gateway.empty() + && !newCache.primarydns.empty(); + Exchange::INetworkManager::IPAddress routeAddress; + if (routeReady) { + routeAddress = newCache.toIPAddress(); + routeAddress.ipversion = family; + } + + std::set oldKeys = _instance->swapIpCache( + ifname, family, std::move(newCache)); + + /* Emit address acquired/lost events from global-address key diff (outside the lock). */ + for (const auto& key : newKeys) { + if (oldKeys.find(key) == oldKeys.end()) { + _instance->ReportIPAddressChange(ifname, family, key, Exchange::INetworkManager::IP_ACQUIRED); + } + } + for (const auto& key : oldKeys) { + if (newKeys.find(key) == newKeys.end()) { + _instance->ReportIPAddressChange(ifname, family, key, Exchange::INetworkManager::IP_LOST); + } + } + + /* Coalesced "route ready" event: emit once address + gateway + primary DNS + are all populated for this family. The notify::addresses / + notify::gateway / notify::nameservers subscriptions on NMIPConfig all + funnel here, so a single emission per snapshot covers all three. + Each family emits independently — dual-stack consumers will see one + event per family. */ + if (routeReady) { + _instance->ReportRouteChange(ifname, family, routeAddress); + } + } + + static void ip4ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, false); + } + + static void ip6ChangedCb(NMIPConfig *ipConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, true); + } + + /* Called when DHCP options change mid-lease (e.g. renewed with different server/options). */ + static void dhcp4OptionsCb(NMDhcpConfig *dhcpConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, false); + } + + static void dhcp6OptionsCb(NMDhcpConfig *dhcpConfig, GParamSpec *pspec, gpointer userData) + { + NMDevice *device = (NMDevice*)userData; + if (!device || !NM_IS_DEVICE(device)) return; + refreshIpFamilyCache(device, true); + } + + /* Called when the ip4-config or ip6-config object on a device is replaced + (e.g. after reconnect). Re-attaches notify handlers to the new object. */ + static void ip4ConfigChangedCb(NMDevice *device, GParamSpec *pspec, gpointer userData) + { + if (!device || !NM_IS_DEVICE(device)) return; + NMIPConfig* ipv4Config = nm_device_get_ip4_config(device); + if (ipv4Config) { + g_signal_handlers_disconnect_by_func(ipv4Config, (gpointer)ip4ChangedCb, device); + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); + } + /* Re-attach DHCP options handler to the (possibly new) DHCP config object. */ + NMDhcpConfig* dhcp4 = nm_device_get_dhcp4_config(device); + if (dhcp4) { + g_signal_handlers_disconnect_by_func(dhcp4, (gpointer)dhcp4OptionsCb, device); + g_signal_connect(dhcp4, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); + } + refreshIpFamilyCache(device, false); + } + + static void ip6ConfigChangedCb(NMDevice *device, GParamSpec *pspec, gpointer userData) + { + if (!device || !NM_IS_DEVICE(device)) return; + NMIPConfig* ipv6Config = nm_device_get_ip6_config(device); + if (ipv6Config) { + g_signal_handlers_disconnect_by_func(ipv6Config, (gpointer)ip6ChangedCb, device); + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); + } + /* Re-attach DHCP options handler to the (possibly new) DHCP config object. */ + NMDhcpConfig* dhcp6 = nm_device_get_dhcp6_config(device); + if (dhcp6) { + g_signal_handlers_disconnect_by_func(dhcp6, (gpointer)dhcp6OptionsCb, device); + g_signal_connect(dhcp6, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); + } + refreshIpFamilyCache(device, true); + } + + void GnomeNetworkManagerEvents::deviceStateChangeCb(NMDevice *device, GParamSpec *pspec, NMEvents *nmEvents) + { + static bool isEthDisabled = false; + static bool isWlanDisabled = false; + if(device == nullptr) + return; + NMDeviceState deviceState; + deviceState = nm_device_get_state(device); + const char* iface = nm_device_get_iface(device); + if (!iface) return; + std::string ifname = iface; + NMDeviceStateReason reason = nm_device_get_state_reason(device); + updateInterfaceStateCache(ifname, deviceState, nm_device_get_hw_address(device)); + if(ifname == nmUtils::wlanIface()) + { + if(!NM_IS_DEVICE_WIFI(device)) { + NMLOG_FATAL("not a wifi device !"); + return; + } + std::string wifiState; + /* SSID NM is activating (pairing/connecting); empty once the activation is torn down */ + std::string attemptingSSID = ""; + if(NMActiveConnection *wifiActiveConn = nm_device_get_active_connection(device)) + { + const char* connId = nm_active_connection_get_id(wifiActiveConn); + if(connId != NULL) + attemptingSSID = connId; + } + switch (reason) + { + case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: + wifiState = "WIFI_STATE_UNINSTALLED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED, attemptingSSID); + break; + case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: + wifiState = "WIFI_STATE_SSID_NOT_FOUND"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, attemptingSSID); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: // supplicant took too long to authenticate + case NM_DEVICE_STATE_REASON_NO_SECRETS: + wifiState = "WIFI_STATE_AUTHENTICATION_FAILED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED, attemptingSSID); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: // 802.1x supplicant failed + wifiState = "WIFI_STATE_ERROR"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_ERROR, attemptingSSID); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: // 802.1x supplicant configuration failed + wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED, attemptingSSID); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: // 802.1x supplicant disconnected + wifiState = "WIFI_STATE_INVALID_CREDENTIALS"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS, attemptingSSID); + break; + default: + { + switch (deviceState) + { + case NM_DEVICE_STATE_UNKNOWN: + wifiState = "WIFI_STATE_UNINSTALLED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED, attemptingSSID); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface()); + break; + case NM_DEVICE_STATE_UNMANAGED: + wifiState = "WIFI_STATE_DISABLED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISABLED, attemptingSSID); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface()); + isWlanDisabled = true; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + wifiState = "WIFI_STATE_DISCONNECTED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISCONNECTED, attemptingSSID); + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, nmUtils::wlanIface()); + break; + case NM_DEVICE_STATE_PREPARE: + wifiState = "WIFI_STATE_PAIRING"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_PAIRING, attemptingSSID); + break; + case NM_DEVICE_STATE_CONFIG: + wifiState = "WIFI_STATE_CONNECTING"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTING, attemptingSSID); + break; + case NM_DEVICE_STATE_IP_CONFIG: + wifiState = "NM_DEVICE_STATE_IP_CONFIG"; + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, nmUtils::wlanIface()); + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTED, attemptingSSID); + break; + case NM_DEVICE_STATE_IP_CHECK: + wifiState = "NM_DEVICE_STATE_IP_CHECK"; + //GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, nmUtils::wlanIface()); + break; + case NM_DEVICE_STATE_SECONDARIES: + wifiState = "NM_DEVICE_STATE_SECONDARIES"; + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, nmUtils::wlanIface()); + break; + case NM_DEVICE_STATE_ACTIVATED: + wifiState = "WIFI_STATE_CONNECTED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTED, attemptingSSID); +#if USE_TELEMETRY + { + static std::string lastWlanGatewayMac; + std::string gatewayMac = nmUtils::getGatewayMacAddress(device); + if (!gatewayMac.empty() && lastWlanGatewayMac != gatewayMac) { + lastWlanGatewayMac = gatewayMac; + NMLOG_INFO("NM_WIFI_GW_MAC = %s", gatewayMac.c_str()); + if (_instance != nullptr) + { + _instance->logTelemetry("NM_WIFI_GW_MAC", gatewayMac); + } + } + } +#endif + break; + case NM_DEVICE_STATE_DEACTIVATING: + wifiState = "WIFI_STATE_CONNECTION_LOST"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST, attemptingSSID); + break; + case NM_DEVICE_STATE_FAILED: + wifiState = "WIFI_STATE_CONNECTION_FAILED"; + GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED, attemptingSSID); + break; + case NM_DEVICE_STATE_NEED_AUTH: + //GnomeNetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED); + wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; + break; + default: + wifiState = "Un handiled: " ; + wifiState += std::to_string(deviceState); + } + + if(isWlanDisabled && deviceState > NM_DEVICE_STATE_UNMANAGED) + { + isWlanDisabled = false; + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, nmUtils::wlanIface()); + } + + break; + } + } + NMLOG_INFO("wifi state: %s; reason: %d", wifiState.c_str(), static_cast(reason)); + } + else if(ifname == nmUtils::ethIface()) + { + switch (deviceState) + { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::ethIface()); + isEthDisabled = true; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, nmUtils::ethIface()); + break; + case NM_DEVICE_STATE_PREPARE: + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, nmUtils::ethIface()); + break; + case NM_DEVICE_STATE_IP_CONFIG: + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, nmUtils::ethIface()); + break; + case NM_DEVICE_STATE_ACTIVATED: +#if USE_TELEMETRY + { + static std::string lastEthGatewayMac; + std::string gatewayMac = nmUtils::getGatewayMacAddress(device); + if (!gatewayMac.empty() && lastEthGatewayMac != gatewayMac) { + lastEthGatewayMac = gatewayMac; + NMLOG_INFO("NM_ETHERNET_GW_MAC = %s", gatewayMac.c_str()); + if (_instance != nullptr) + _instance->logTelemetry("NM_ETHERNET_GW_MAC", gatewayMac); + } + } +#endif + break; + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_DEACTIVATING: + default: + NMLOG_WARNING("Unhandiled state change %d", deviceState); + } + + /* + * Post interface added event: + * When the Ethernet interface is removed, its state goes to UNKNOWN or UNMANAGED. + * normaly interface is always up and we are not removing it + */ + if(isEthDisabled && deviceState > NM_DEVICE_STATE_UNMANAGED) + { + isEthDisabled = false; + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, nmUtils::ethIface()); + } + + NMLOG_INFO("eth0 state: %d; reason: %d", static_cast(deviceState), static_cast(reason)); + } + } + + static void deviceAddedCB(NMClient *client, NMDevice *device, NMEvents *nmEvents) + { + if( ((device != NULL) && NM_IS_DEVICE(device)) ) + { + const char* iface = nm_device_get_iface(device); + if (!iface) return; + std::string ifname = iface; + if(ifname == nmUtils::wlanIface()) { + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, nmUtils::wlanIface()); + NMLOG_INFO("WIFI device added: %s", ifname.c_str()); + } + else if(ifname == nmUtils::ethIface()) { + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, nmUtils::ethIface()); + NMLOG_INFO("ETHERNET device added: %s", ifname.c_str()); + } + + /* ip events added only for eth0 and wlan0 */ + if(ifname == nmUtils::ethIface() || ifname == nmUtils::wlanIface()) + { + GnomeNetworkManagerEvents::updateInterfaceStateCache(ifname, nm_device_get_state(device), nm_device_get_hw_address(device)); + g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(GnomeNetworkManagerEvents::deviceStateChangeCb), nmEvents); + g_signal_connect(device, "notify::ip4-config", G_CALLBACK(ip4ConfigChangedCb), nmEvents); + g_signal_connect(device, "notify::ip6-config", G_CALLBACK(ip6ConfigChangedCb), nmEvents); + NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); + NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); + if (ipv4Config) { + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); + } + + if (ipv6Config) { + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); + } + + /* Subscribe to DHCP option changes so dhcpserver stays current mid-lease. */ + NMDhcpConfig* dhcp4Added = nm_device_get_dhcp4_config(device); + NMDhcpConfig* dhcp6Added = nm_device_get_dhcp6_config(device); + if (dhcp4Added) + g_signal_connect(dhcp4Added, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); + if (dhcp6Added) + g_signal_connect(dhcp6Added, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); + + /* Seed the IP cache so GetIPSettings works immediately if the + device already has an address (e.g. hotplug in activated state). */ + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + + if (NM_IS_DEVICE_WIFI(device)) + { + // Register signal handler for WiFi scanning events to detect when scan operations complete + nmEvents->wifiDevice = NM_DEVICE_WIFI(device); + NMLOG_INFO("WIFI device added, adding signal handler for last-scan"); + g_signal_connect(nmEvents->wifiDevice, "notify::" NM_DEVICE_WIFI_LAST_SCAN, G_CALLBACK(GnomeNetworkManagerEvents::onAvailableSSIDsCb), nmEvents); + } + } + } + else + NMLOG_DEBUG("device error null"); + } + + static void deviceRemovedCB(NMClient *client, NMDevice *device, NMEvents *nmEvents) + { + if( ((device != NULL) && NM_IS_DEVICE(device)) ) + { + const char* iface = nm_device_get_iface(device); + if (!iface) return; + std::string ifname = iface; + if(ifname == nmUtils::wlanIface()) { + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::wlanIface()); + NMLOG_INFO("WIFI device removed: %s", ifname.c_str()); + } + else if(ifname == nmUtils::ethIface()) { + GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, nmUtils::ethIface()); + NMLOG_INFO("ETHERNET device removed: %s", ifname.c_str()); + } + else { + return; // not a tracked interface + } + + /* Device is gone: drop it from the cache so the reads omit it + (matches the original live-device-list behaviour). */ + GnomeNetworkManagerEvents::removeInterfaceStateCache(ifname); + + /* Disconnect all device-level signals (state, ip4/ip6-config changes). */ + g_signal_handlers_disconnect_by_data(device, nmEvents); + + /* Disconnect IP config property signals (addresses, gateway, nameservers). */ + NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); + NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); + if (ipv4Config) + g_signal_handlers_disconnect_by_func(ipv4Config, (gpointer)ip4ChangedCb, device); + if (ipv6Config) + g_signal_handlers_disconnect_by_func(ipv6Config, (gpointer)ip6ChangedCb, device); + + /* Disconnect DHCP option signals. */ + NMDhcpConfig* dhcp4 = nm_device_get_dhcp4_config(device); + NMDhcpConfig* dhcp6 = nm_device_get_dhcp6_config(device); + if (dhcp4) + g_signal_handlers_disconnect_by_func(dhcp4, (gpointer)dhcp4OptionsCb, device); + if (dhcp6) + g_signal_handlers_disconnect_by_func(dhcp6, (gpointer)dhcp6OptionsCb, device); + + /* Clear IP cache for the removed device (emits IP_LOST for any cached addresses). */ + if (_instance) { + for (const char* family : {"IPv4", "IPv6"}) { + IpFamilyCache empty; + std::set oldKeys = _instance->swapIpCache(ifname, family, std::move(empty)); + for (const auto& key : oldKeys) { + _instance->ReportIPAddressChange(ifname, family, key, Exchange::INetworkManager::IP_LOST); + } + } + } + } + + // guint disconnected_count = g_signal_handlers_disconnect_matched( _nmEventInstance->activeConn, + // G_SIGNAL_MATCH_FUNC, + // 0, 0, NULL, + // (gpointer)onActiveConnectionStateChanged, + // NULL ); + // NMLOG_ERROR("Disconnected %u signal handlers\n", disconnected_count); + } + + static void clientStateChangedCb (NMClient *client, GParamSpec *pspec, gpointer user_data) + { + + switch (nm_client_get_state (client)) { + case NM_STATE_DISCONNECTED: + NMLOG_WARNING("internet connection down"); + break; + case NM_STATE_CONNECTED_GLOBAL: + NMLOG_DEBUG("global internet connection success"); + break; + default: + break; + } + } + + static void managerRunningCb (NMClient *client, GParamSpec *pspec, gpointer user_data) + { + if (nm_client_get_nm_running (client)) { + NMLOG_INFO("network manager daemon is running"); + } else { + NMLOG_FATAL("network manager daemon not running !"); + // TODO check need any client reconnection or not ? + } + } + + void* GnomeNetworkManagerEvents::networkMangerEventMonitor(void *arg) + { + if(arg == nullptr) + { + NMLOG_FATAL("function argument error: nm event monitor failed"); + return nullptr; + } + + NMEvents *nmEvents = static_cast(arg); + primaryConnectionCb(nmEvents->client, NULL, nmEvents); + g_signal_connect (nmEvents->client, "notify::" NM_CLIENT_NM_RUNNING,G_CALLBACK (managerRunningCb), nmEvents); + g_signal_connect(nmEvents->client, "notify::" NM_CLIENT_STATE, G_CALLBACK (clientStateChangedCb),nmEvents); + g_signal_connect(nmEvents->client, "notify::" NM_CLIENT_PRIMARY_CONNECTION, G_CALLBACK(primaryConnectionCb), nmEvents); + + const GPtrArray *devices = nullptr; + devices = nm_client_get_devices(nmEvents->client); + + g_signal_connect(nmEvents->client, NM_CLIENT_DEVICE_ADDED, G_CALLBACK(deviceAddedCB), nmEvents); + g_signal_connect(nmEvents->client, NM_CLIENT_DEVICE_REMOVED, G_CALLBACK(deviceRemovedCB), nmEvents); + + if(devices == nullptr) + { + NMLOG_ERROR("Failed to get device list."); + return nullptr; + } + + for (u_int count = 0; count < devices->len; count++) + { + NMDevice *device = NM_DEVICE(g_ptr_array_index(devices, count)); + if( ((device != NULL) && NM_IS_DEVICE(device)) ) + { + const char* iface = nm_device_get_iface(device); + if (!iface) continue; + std::string ifname = iface; + if((ifname == nmUtils::ethIface()) || (ifname == nmUtils::wlanIface())) + { + NMDeviceState devState = nm_device_get_state(device); + GnomeNetworkManagerEvents::updateInterfaceStateCache(ifname, devState, nm_device_get_hw_address(device)); + + if(devState > NM_DEVICE_STATE_DISCONNECTED && devState <= NM_DEVICE_STATE_ACTIVATED) + { + // posting device state change event if interface already connected + GnomeNetworkManagerEvents::deviceStateChangeCb(device, nullptr, nullptr); + } + + /* Register device state change event */ + g_signal_connect(device, "notify::" NM_DEVICE_STATE, G_CALLBACK(GnomeNetworkManagerEvents::deviceStateChangeCb), nmEvents); + g_signal_connect(device, "notify::ip4-config", G_CALLBACK(ip4ConfigChangedCb), nmEvents); + g_signal_connect(device, "notify::ip6-config", G_CALLBACK(ip6ConfigChangedCb), nmEvents); + if(NM_IS_DEVICE_WIFI(device)) { + nmEvents->wifiDevice = NM_DEVICE_WIFI(device); + g_signal_connect(nmEvents->wifiDevice, "notify::" NM_DEVICE_WIFI_LAST_SCAN, G_CALLBACK(GnomeNetworkManagerEvents::onAvailableSSIDsCb), nmEvents); + } + + NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); + NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); + if (ipv4Config) { + g_signal_connect(ipv4Config, "notify::addresses", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::gateway", G_CALLBACK(ip4ChangedCb), device); + g_signal_connect(ipv4Config, "notify::nameservers", G_CALLBACK(ip4ChangedCb), device); + } + else + NMLOG_WARNING("IPv4 config is null for device: %s, No IPv4 monitor", ifname.c_str()); + + if (ipv6Config) { + g_signal_connect(ipv6Config, "notify::addresses", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::gateway", G_CALLBACK(ip6ChangedCb), device); + g_signal_connect(ipv6Config, "notify::nameservers", G_CALLBACK(ip6ChangedCb), device); + } + else + NMLOG_WARNING("IPv6 config is null for device: %s, No IPv6 monitor", ifname.c_str()); + + /* Subscribe to DHCP option changes so dhcpserver stays current mid-lease. */ + NMDhcpConfig* dhcp4Init = nm_device_get_dhcp4_config(device); + NMDhcpConfig* dhcp6Init = nm_device_get_dhcp6_config(device); + if (dhcp4Init) + g_signal_connect(dhcp4Init, "notify::options", G_CALLBACK(dhcp4OptionsCb), device); + if (dhcp6Init) + g_signal_connect(dhcp6Init, "notify::options", G_CALLBACK(dhcp6OptionsCb), device); + + /* Seed the IP cache from current state for already-connected devices. */ + refreshIpFamilyCache(device, false); + refreshIpFamilyCache(device, true); + } + else + NMLOG_DEBUG("device type not eth/wifi %s", ifname.c_str()); + } + else + NMLOG_WARNING("device error null"); + } + + NMLOG_INFO("registered all networkmnager dbus events"); + g_main_loop_run(nmEvents->loop); + // Clean up all signal handlers after thread has stopped + if(_nmEventInstance != nullptr) + { + _nmEventInstance->cleanupSignalHandlers(); + } + //g_main_loop_unref(nmEvents->loop); + return nullptr; + } + + bool GnomeNetworkManagerEvents::startNetworkMangerEventMonitor() + { + NMLOG_DEBUG("starting gnome event monitor"); + if (NULL == nmEvents.client) { + NMLOG_ERROR("Client Connection NULL DBUS event Failed!"); + return false; + } + if(!isEventThrdActive) { + isEventThrdActive = true; + // Create event monitor thread + eventThrdID = g_thread_new("nm_event_thrd", GnomeNetworkManagerEvents::networkMangerEventMonitor, &nmEvents); + } + return true; + } + + void GnomeNetworkManagerEvents::stopNetworkMangerEventMonitor() + { + // g_signal_handlers_disconnect_by_func(client, G_CALLBACK(primaryConnectionCb), NULL); + if (!isEventThrdActive) { + return; + } + if (nmEvents.loop != NULL) { + g_main_loop_quit(nmEvents.loop); + } + + if (eventThrdID) { + g_thread_join(eventThrdID); // Wait for the thread to finish + eventThrdID = NULL; // Reset the thread ID + NMLOG_WARNING("gnome event monitor stopped"); + } + isEventThrdActive = false; + + } + + void GnomeNetworkManagerEvents::cleanupSignalHandlers() + { + if(nmEvents.client == nullptr) { + return; // Already cleaned up + } + + NMLOG_DEBUG("Cleaning up signal handlers"); + + // Disconnect all client signals + g_signal_handlers_disconnect_by_data(nmEvents.client, &nmEvents); + + // Clean up device signals + const GPtrArray *devices = nm_client_get_devices(nmEvents.client); + if (devices) { + for (guint i = 0; i < devices->len; i++) { + NMDevice *device = NM_DEVICE(g_ptr_array_index(devices, i)); + if (device && NM_IS_DEVICE(device)) { + g_signal_handlers_disconnect_by_data(device, &nmEvents); + + // Clean up IP config signals + NMIPConfig *ipv4Config = nm_device_get_ip4_config(device); + NMIPConfig *ipv6Config = nm_device_get_ip6_config(device); + if (ipv4Config) { + g_signal_handlers_disconnect_by_func(ipv4Config, (gpointer)ip4ChangedCb, device); + } + if (ipv6Config) { + g_signal_handlers_disconnect_by_func(ipv6Config, (gpointer)ip6ChangedCb, device); + } + + // Clean up DHCP option signals + NMDhcpConfig* dhcp4 = nm_device_get_dhcp4_config(device); + NMDhcpConfig* dhcp6 = nm_device_get_dhcp6_config(device); + if (dhcp4) + g_signal_handlers_disconnect_by_func(dhcp4, (gpointer)dhcp4OptionsCb, device); + if (dhcp6) + g_signal_handlers_disconnect_by_func(dhcp6, (gpointer)dhcp6OptionsCb, device); + } + } + } + + NMLOG_DEBUG("Signal handlers cleanup complete"); + } + + GnomeNetworkManagerEvents::~GnomeNetworkManagerEvents() + { + NMLOG_INFO("~GnomeNetworkManagerEvents"); + stopNetworkMangerEventMonitor(); + if(nmEvents.client != nullptr) { + g_object_unref(nmEvents.client); + nmEvents.client = nullptr; + } + if (nmEvents.loop != NULL) { + g_main_loop_unref(nmEvents.loop); + nmEvents.loop = NULL; + } + } + + GnomeNetworkManagerEvents* GnomeNetworkManagerEvents::getInstance() + { + static GnomeNetworkManagerEvents instance; + return &instance; + } + + GnomeNetworkManagerEvents::GnomeNetworkManagerEvents() + { + NMLOG_DEBUG("GnomeNetworkManagerEvents"); + GError *error = NULL; + eventThrdID = nullptr; + isEventThrdActive = false; + doScanNotify = false; + + nmEvents.client = nm_client_new(NULL, &error); + if(!nmEvents.client || error ) + { + if (error) { + NMLOG_ERROR("Could not connect to NetworkManager: %s", error->message); + g_error_free(error); + } + NMLOG_INFO("networkmanger client connection failed"); + return; + } + + NMLOG_INFO("libnm client connection success version: %s", nm_client_get_version(nmEvents.client)); + nmEvents.loop = g_main_loop_new(NULL, FALSE); + if(nmEvents.loop == NULL) { + NMLOG_FATAL("GMain loop failed Fatal Error: Event will not work"); + return; + } + _nmEventInstance = this; + } + + /* Gnome networkmanger new events */ + + void GnomeNetworkManagerEvents::onActiveInterfaceChangeCb(std::string newIface) + { + static std::string oldIface = "Unknown"; + + if(oldIface != newIface) + { + if(_instance != nullptr) + _instance->ReportActiveInterfaceChange(oldIface, newIface); + NMLOG_INFO("old interface - %s new interface - %s", oldIface.c_str(), newIface.c_str()); + oldIface = newIface; + + /* Default-route owner changed (e.g. eth0↔wlan0 failover). The new primary + already has its IP/gateway/DNS in the cache from prior refreshIpFamilyCache, + so emit a coalesced route-ready event for both families — ReportRouteChange + is a no-op for whichever family isn't fully populated. */ + if (_instance != nullptr && !newIface.empty() && newIface != "Unknown") { + _instance->ReportRouteChange(newIface, "IPv4"); + _instance->ReportRouteChange(newIface, "IPv6"); + } + } + } + + void GnomeNetworkManagerEvents::onInterfaceStateChangeCb(uint8_t newState, std::string iface) + { + std::string state {}; + switch (newState) + { + case Exchange::INetworkManager::INTERFACE_ADDED: + state = "INTERFACE_ADDED"; + break; + case Exchange::INetworkManager::INTERFACE_LINK_UP: + state = "INTERFACE_LINK_UP"; + break; + case Exchange::INetworkManager::INTERFACE_LINK_DOWN: + state = "INTERFACE_LINK_DOWN"; + break; + case Exchange::INetworkManager::INTERFACE_ACQUIRING_IP: + state = "INTERFACE_ACQUIRING_IP"; + break; + case Exchange::INetworkManager::INTERFACE_REMOVED: + state = "INTERFACE_REMOVED"; + break; + case Exchange::INetworkManager::INTERFACE_DISABLED: + state = "INTERFACE_DISABLED"; + break; + default: + state = "Unknown"; + } + + NMLOG_DEBUG("%s interface state changed - %s", iface.c_str(), state.c_str()); + if(_instance != nullptr && (iface == nmUtils::wlanIface() || iface == nmUtils::ethIface())) + _instance->ReportInterfaceStateChange(static_cast(newState), iface); + } + + void GnomeNetworkManagerEvents::onWIFIStateChanged(uint8_t state, std::string ssid) + { + if(_instance != nullptr) + { + _instance->ReportWiFiStateChange(static_cast(state), ssid); +#ifdef ENABLE_MIGRATION_MFRMGR_SUPPORT + // Handle WiFi state changes for MfrMgr integration + NetworkManagerMfrManager* mfrManager = NetworkManagerMfrManager::getInstance(); + if(mfrManager != nullptr) { + if(state == Exchange::INetworkManager::WIFI_STATE_CONNECTED) + { + NMLOG_DEBUG("WiFi connected - triggering MfrMgr save"); + mfrManager->saveWiFiSettingsToMfr(); + } + } +#endif + } + } + + bool GnomeNetworkManagerEvents::apToJsonObject(NMAccessPoint *ap, JsonObject& ssidObj) + { + GBytes *ssid = NULL; + int strength = 0; + double freq; + int security; + guint32 flags, wpaFlags, rsnFlags, apFreq; + if(ap == nullptr) + return false; + ssid = nm_access_point_get_ssid(ap); + if (ssid) + { + char *ssidStr = nullptr; + ssidStr = nm_utils_ssid_to_utf8((const guint8*)g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); + string ssidString = "---"; + if(ssidStr) + { + ssidString = ssidStr; + free(ssidStr); + } + ssidObj["ssid"] = ssidString; + const char *bssidPtr = nm_access_point_get_bssid(ap); + std::string bssid = bssidPtr ? bssidPtr : ""; + if (!bssid.empty()) { + ssidObj["bssid"] = bssid; + } + else + { + NMLOG_WARNING("BSSID is null for SSID: %s", ssidString.c_str()); + ssidObj["bssid"] = ""; + } + strength = nm_access_point_get_strength(ap); + apFreq = nm_access_point_get_frequency(ap); + flags = nm_access_point_get_flags(ap); + wpaFlags = nm_access_point_get_wpa_flags(ap); + rsnFlags = nm_access_point_get_rsn_flags(ap); + freq = nmUtils::wifiFrequencyFromAp(apFreq); + security = nmUtils::wifiSecurityModeFromAp(ssidString, flags, wpaFlags, rsnFlags, false); + + ssidObj["security"] = security; + ssidObj["strength"] = nmUtils::convertPercentageToSignalStrength(strength); + ssidObj["frequency"] = freq; + return true; + } + // else + // NMLOG_DEBUG("hidden ssid found, bssid: %s", nm_access_point_get_bssid(ap)); + return false; + } + + void GnomeNetworkManagerEvents::onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData) + { + if(!NM_IS_DEVICE_WIFI(wifiDevice)) + { + NMLOG_ERROR("Not a wifi object "); + return; + } + + const GPtrArray *accessPoints = nm_device_wifi_get_access_points(wifiDevice); + + if (accessPoints == nullptr) { + NMLOG_ERROR("scanning result No access points found !"); + _nmEventInstance->doScanNotify = false; + return; + } + + if(!_nmEventInstance->doScanNotify) + { + NMLOG_DEBUG("scan result received; notify disabled, skipping"); + return; + } + + NMLOG_INFO("No of AP Available = %d", static_cast(accessPoints->len)); + + JsonArray ssidList = JsonArray(); + for (guint i = 0; i < accessPoints->len; i++) + { + JsonObject ssidObj{}; + NMAccessPoint *ap = static_cast(accessPoints->pdata[i]); + if(GnomeNetworkManagerEvents::apToJsonObject(ap, ssidObj)) + ssidList.Add(ssidObj); + } + + if(_instance != nullptr) { + _nmEventInstance->doScanNotify = false; + _instance->ReportAvailableSSIDs(ssidList); + } + } + + void GnomeNetworkManagerEvents::setwifiScanOptions(bool doNotify) + { + doScanNotify.store(doNotify); + if(!doScanNotify) + { + NMLOG_DEBUG("stop periodic wifi scan result notify"); + } + } + + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeEvents.h b/plugin/gnome/NetworkManagerGnomeEvents.h new file mode 100644 index 000000000..acf78a9cf --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeEvents.h @@ -0,0 +1,88 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include +#include + +namespace WPEFramework +{ + namespace Plugin + { + + typedef struct { + NMClient *client; + GMainLoop *loop; + NMDevice *device; + NMDeviceWifi *wifiDevice; + NMActiveConnection *activeConn; + std::string ifnameWlan0; + std::string ifnameEth0; + } NMEvents; + + class GnomeNetworkManagerEvents + { + + public: + /* Gnome-owned cache of live NM device state, kept out of the + backend-agnostic NetworkManagerImplementation (holds a libnm type). */ + struct InterfaceStateInfo { + NMDeviceState state = NM_DEVICE_STATE_UNKNOWN; + std::string mac; + bool present = false; + }; + + static void onInterfaceStateChangeCb(uint8_t newState, std::string iface); // ReportInterfaceStateChange + static void onActiveInterfaceChangeCb(std::string newInterface); // ReportActiveInterfaceChange + static void onAvailableSSIDsCb(NMDeviceWifi *wifiDevice, GParamSpec *pspec, gpointer userData); // ReportAvailableSSIDs + static void onWIFIStateChanged(uint8_t state, std::string ssid = ""); // ReportWiFiStateChange + static void deviceStateChangeCb(NMDevice *device, GParamSpec *pspec, NMEvents *nmEvents); + + /* Interface-state cache: sole writer is the event monitor; readers + (GetAvailableInterfaces / GetInterfaceState) are pure. */ + static void updateInterfaceStateCache(const std::string& iface, NMDeviceState state, const char* mac); + static void removeInterfaceStateCache(const std::string& iface); + static bool getInterfaceStateCache(const std::string& iface, InterfaceStateInfo& out); + /* Canonical enabled/connected definitions shared by both read APIs. */ + static bool isInterfaceStateEnabled(NMDeviceState state); + static bool isInterfaceStateConnected(NMDeviceState state); + + public: + static GnomeNetworkManagerEvents* getInstance(); + bool startNetworkMangerEventMonitor(); + void stopNetworkMangerEventMonitor(); + void setwifiScanOptions(bool doNotify); + + private: + static void* networkMangerEventMonitor(void *arg); + static bool apToJsonObject(NMAccessPoint *ap, JsonObject& ssidObj); + void cleanupSignalHandlers(); + GnomeNetworkManagerEvents(); + ~GnomeNetworkManagerEvents(); + std::atomicisEventThrdActive = {false}; + std::atomicdoScanNotify = {false}; + NMEvents nmEvents; + GThread *eventThrdID; + }; + + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeMfrMgr.cpp b/plugin/gnome/NetworkManagerGnomeMfrMgr.cpp new file mode 100644 index 000000000..12a4368d3 --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeMfrMgr.cpp @@ -0,0 +1,498 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManagerGnomeMfrMgr.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerImplementation.h" +#include "INetworkManager.h" +#include "NetworkManagerGnomeUtils.h" +#include "gdbus/NetworkManagerGdbusMgr.h" +#include "gdbus/NetworkManagerGdbusUtils.h" +#include "mfrMgr.h" +#include +#include + +#include +#include "libIBus.h" + +namespace WPEFramework +{ + namespace Plugin + { + extern NetworkManagerImplementation* _instance; + + static std::mutex iarm_mutex; + static bool iarm_initialized = false; + + static bool ensureIARMConnection() + { + std::lock_guard lock(iarm_mutex); + + if (!iarm_initialized) { + IARM_Result_t ret = IARM_Bus_Init("NetworkMfrMgr"); + if (ret != IARM_RESULT_SUCCESS) { + NMLOG_ERROR("IARM_Bus_Init failed: %d", ret); + return false; + } + + ret = IARM_Bus_Connect(); + if (ret != IARM_RESULT_SUCCESS) { + NMLOG_ERROR("IARM_Bus_Connect failed: %d", ret); + return false; + } + + iarm_initialized = true; + NMLOG_DEBUG("IARM Bus connection established for MfrMgr"); + } + return true; + } + + // Helper function to retrieve current WiFi connection details using GDBus utilities + static bool getCurrentWiFiConnectionDetails(std::string& ssid, std::string& passphrase, int& security) + { + GError *error = NULL; + GDBusProxy *connection_proxy = NULL; + GDBusProxy *device_proxy = NULL; + bool result = false; + + NMLOG_DEBUG("Retrieving WiFi connection details using GDBus utilities"); + + // Create DbusMgr instance for proxy management + DbusMgr dbusMgr; + + // Get WiFi device information using existing utility + deviceInfo devInfo; + if (!GnomeUtils::getDeviceInfoByIfname(dbusMgr, nmUtils::wlanIface(), devInfo)) { + NMLOG_ERROR("WiFi device '%s' not found", nmUtils::wlanIface()); + return false; + } + + NMLOG_DEBUG("Found WiFi device at path: %s", devInfo.path.c_str()); + + // Create device proxy to check state and refresh ActiveConnection property + device_proxy = dbusMgr.getNetworkManagerDeviceProxy(devInfo.path.c_str()); + if (!device_proxy) { + NMLOG_ERROR("Failed to create device proxy"); + return false; + } + + // Check device state first + GVariant *state_variant = g_dbus_proxy_get_cached_property(device_proxy, "State"); + if (state_variant) { + guint32 device_state = g_variant_get_uint32(state_variant); + NMLOG_DEBUG("WiFi device state: %u (100=activated, 30=disconnected)", device_state); + g_variant_unref(state_variant); + + // NM_DEVICE_STATE_ACTIVATED = 100 + if (device_state != NM_DEVICE_STATE_ACTIVATED) { + NMLOG_WARNING("WiFi device not in activated state (state=%u)", device_state); + } + } + + // Force refresh ActiveConnection property from D-Bus instead of using cached value + std::string active_conn_path; + error = NULL; + GVariant *props_variant = g_dbus_proxy_call_sync(device_proxy, + "org.freedesktop.DBus.Properties.Get", + g_variant_new("(ss)", + "org.freedesktop.NetworkManager.Device", + "ActiveConnection"), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!props_variant) { + NMLOG_ERROR("Failed to get ActiveConnection property: %s", error ? error->message : "unknown"); + if (error) g_error_free(error); + g_object_unref(device_proxy); + return false; + } + + GVariant *active_conn_variant = NULL; + g_variant_get(props_variant, "(v)", &active_conn_variant); + + if (!active_conn_variant) { + NMLOG_ERROR("ActiveConnection property variant is NULL"); + g_variant_unref(props_variant); + g_object_unref(device_proxy); + return false; + } + + const gchar *active_conn_path_str = g_variant_get_string(active_conn_variant, NULL); + + if (active_conn_path_str) { + active_conn_path = active_conn_path_str; + } + + g_variant_unref(active_conn_variant); + g_variant_unref(props_variant); + g_object_unref(device_proxy); + + NMLOG_DEBUG("ActiveConnection path: %s", active_conn_path.c_str()); + + // Check if device has an active connection + if (active_conn_path.empty() || active_conn_path == "/") { + NMLOG_ERROR("No active connection on WiFi device (path: %s)", active_conn_path.c_str()); + return false; + } + + // Create active connection proxy using DbusMgr + GDBusProxy *active_conn_proxy = dbusMgr.getNetworkManagerActiveConnProxy(active_conn_path.c_str()); + if (!active_conn_proxy) { + NMLOG_ERROR("Failed to create active connection proxy"); + return false; + } + + // Get connection path from active connection + GVariant *conn_path_variant = g_dbus_proxy_get_cached_property(active_conn_proxy, "Connection"); + if (!conn_path_variant) { + NMLOG_ERROR("Failed to get connection path from active connection"); + g_object_unref(active_conn_proxy); + return false; + } + + const gchar *connection_path = g_variant_get_string(conn_path_variant, NULL); + if (connection_path == NULL) { + NMLOG_ERROR("Connection path variant did not contain a valid string"); + g_variant_unref(conn_path_variant); + g_object_unref(active_conn_proxy); + return false; + } + NMLOG_DEBUG("Connection settings path: %s", connection_path); + + // Create connection proxy using DbusMgr + connection_proxy = dbusMgr.getNetworkManagerSettingsConnectionProxy(connection_path); + g_variant_unref(conn_path_variant); + g_object_unref(active_conn_proxy); + + if (!connection_proxy) { + NMLOG_ERROR("Failed to create connection proxy"); + return false; + } + + // Get connection settings (without secrets) + GVariant *settings_variant = g_dbus_proxy_call_sync(connection_proxy, + "GetSettings", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if (!settings_variant) { + NMLOG_ERROR("Failed to get connection settings: %s", error ? error->message : "unknown"); + if (error) g_error_free(error); + g_object_unref(connection_proxy); + return false; + } + + // Parse settings to get SSID and security type + GVariantIter *settings_iter; + g_variant_get(settings_variant, "(a{sa{sv}})", &settings_iter); + + gchar *setting_name; + GVariant *setting_dict; + security = NET_WIFI_SECURITY_NONE; + bool has_wireless_security = false; + + while (g_variant_iter_loop(settings_iter, "{s@a{sv}}", &setting_name, &setting_dict)) { + if (g_strcmp0(setting_name, "802-11-wireless") == 0) { + // Extract SSID + GVariant *ssid_variant = g_variant_lookup_value(setting_dict, "ssid", G_VARIANT_TYPE_BYTESTRING); + if (ssid_variant) { + gsize ssid_len; + const guint8 *ssid_data = static_cast(g_variant_get_fixed_array(ssid_variant, &ssid_len, sizeof(guint8))); + ssid = std::string(reinterpret_cast(ssid_data), ssid_len); + NMLOG_DEBUG("Retrieved SSID: %s", ssid.c_str()); + g_variant_unref(ssid_variant); + } + } else if (g_strcmp0(setting_name, "802-11-wireless-security") == 0) { + has_wireless_security = true; + // Extract key-mgmt + GVariant *key_mgmt_variant = g_variant_lookup_value(setting_dict, "key-mgmt", G_VARIANT_TYPE_STRING); + if (key_mgmt_variant) { + const gchar *key_mgmt = g_variant_get_string(key_mgmt_variant, NULL); + NMLOG_DEBUG("Key management: %s", key_mgmt); + + if (g_strcmp0(key_mgmt, "sae") == 0) { + security = NET_WIFI_SECURITY_WPA3_SAE; + } else if (g_strcmp0(key_mgmt, "wpa-psk") == 0) { + security = NET_WIFI_SECURITY_WPA2_PSK_AES; + } else if (g_strcmp0(key_mgmt, "wpa-eap") == 0) { + security = NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES; + } else if (g_strcmp0(key_mgmt, "none") == 0) { + security = NET_WIFI_SECURITY_NONE; + } else { + security = NET_WIFI_SECURITY_WPA2_PSK_AES; + } + + g_variant_unref(key_mgmt_variant); + } + } + } + + g_variant_iter_free(settings_iter); + g_variant_unref(settings_variant); + + // Request secrets if PSK-based security + if (has_wireless_security && (security == NET_WIFI_SECURITY_WPA2_PSK_AES || security == NET_WIFI_SECURITY_WPA3_SAE)) { + NMLOG_DEBUG("Requesting secrets for PSK-based connection"); + + // Use synchronous call instead of async to avoid cancellation complexity + error = NULL; + GVariant *secrets = g_dbus_proxy_call_sync(connection_proxy, + "GetSecrets", + g_variant_new("(s)", "802-11-wireless-security"), + G_DBUS_CALL_FLAGS_NONE, + 5000, // 5 second timeout + NULL, + &error); + + if (error) { + NMLOG_WARNING("Failed to get secrets: %s", error->message); + g_error_free(error); + result = false; + } else if (secrets) { + // Parse the returned secrets + // GetSecrets returns (a{sa{sv}}) - dict of dicts + GVariantIter *iter; + g_variant_get(secrets, "(a{sa{sv}})", &iter); + + gchar *setting_name_key; + GVariant *setting_dict_value; + bool found_psk = false; + + // Iterate through settings + while (g_variant_iter_loop(iter, "{s@a{sv}}", &setting_name_key, &setting_dict_value)) { + if (g_strcmp0(setting_name_key, "802-11-wireless-security") == 0) { + // Found wireless security settings + GVariantIter *setting_iter; + g_variant_get(setting_dict_value, "a{sv}", &setting_iter); + + gchar *key; + GVariant *value; + + // Look for psk key + while (g_variant_iter_loop(setting_iter, "{sv}", &key, &value)) { + if (g_strcmp0(key, "psk") == 0) { + const gchar *psk = g_variant_get_string(value, NULL); + if (psk) { + passphrase = psk; + NMLOG_DEBUG("Successfully retrieved PSK"); + found_psk = true; + } + break; + } + } + g_variant_iter_free(setting_iter); + break; + } + } + + g_variant_iter_free(iter); + g_variant_unref(secrets); + result = found_psk; + } else { + NMLOG_WARNING("GetSecrets returned NULL"); + result = false; + } + } else { + // No secrets needed for open networks + result = true; + } + + // Cleanup + g_object_unref(connection_proxy); + + NMLOG_INFO("Retrieved WiFi connection details - SSID: %s, Security: %d, Has Passphrase: %s", + ssid.c_str(), security, passphrase.empty() ? "no" : "yes"); + + return result && !ssid.empty(); + } + + NetworkManagerMfrManager* NetworkManagerMfrManager::getInstance() + { + static NetworkManagerMfrManager instance; + return &instance; + } + + NetworkManagerMfrManager::NetworkManagerMfrManager() + { + // Initialize IARM connection + if (ensureIARMConnection()) { + NMLOG_DEBUG("NetworkManagerMfrManager initialized with IARM connection"); + } else { + NMLOG_ERROR("NetworkManagerMfrManager initialization failed - IARM connection error"); + } + } + + NetworkManagerMfrManager::~NetworkManagerMfrManager() + { + std::lock_guard lock(iarm_mutex); + if (iarm_initialized) { + iarm_initialized = false; + NMLOG_DEBUG("IARM Bus connection terminated"); + } + NMLOG_DEBUG("NetworkManagerMfrManager destroyed"); + } + + bool NetworkManagerMfrManager::saveWiFiSettingsToMfr() + { + // Launch asynchronous operation to save current WiFi connection details + std::thread saveThread([this]() { + std::string ssid, passphrase; + int security; + + if (!getCurrentWiFiConnectionDetails(ssid, passphrase, security)) { + NMLOG_ERROR("Failed to retrieve current WiFi connection details for MfrMgr save"); + return; + } + + NMLOG_INFO("Retrieved current WiFi connection details for MfrMgr save - SSID: %s, Security: %d", ssid.c_str(), security); + + // Save the retrieved details synchronously + bool result = this->saveWiFiSettingsToMfrSync(ssid, passphrase, security); + if (result) { + NMLOG_DEBUG("Background WiFi connection details retrieval and save completed successfully for SSID: %s", ssid.c_str()); + } else { + NMLOG_ERROR("Background WiFi connection details retrieval and save failed for SSID: %s", ssid.c_str()); + } + }); + + // Detach the thread to run independently + saveThread.detach(); + + NMLOG_DEBUG("WiFi connection details retrieval and save operation queued for background execution"); + return true; // Return immediately, actual retrieval and save happens asynchronously + } + + bool NetworkManagerMfrManager::saveWiFiSettingsToMfrSync(const std::string& ssid, const std::string& passphrase, int security) + { + int securityMode; + if (!ensureIARMConnection()) { + NMLOG_ERROR("IARM connection not available for saving WiFi settings"); + return false; + } + + NMLOG_INFO("Saving WiFi settings to MfrMgr via IARM - SSID: %s, Security: %d", ssid.c_str(), security); + + IARM_BUS_MFRLIB_API_WIFI_Credentials_Param_t param{0}; + IARM_BUS_MFRLIB_API_WIFI_Credentials_Param_t setParam{0}; + IARM_Result_t ret; + param.requestType = WIFI_GET_CREDENTIALS; + ret = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_WIFI_Credentials, + (void*)¶m, sizeof(param)); + if(security == Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_NONE) + securityMode = NET_WIFI_SECURITY_NONE; + else if(security == Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE) + securityMode = NET_WIFI_SECURITY_WPA3_SAE; + else + securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + if (ret == IARM_RESULT_SUCCESS) + { + if ((strcmp (param.wifiCredentials.cSSID, ssid.c_str()) == 0) && + (strcmp (param.wifiCredentials.cPassword, passphrase.c_str()) == 0) && + (param.wifiCredentials.iSecurityMode == securityMode)) + { + NMLOG_INFO("Same ssid info not storing it stored ssid %s new ssid %s", param.wifiCredentials.cSSID, ssid.c_str()); + return true; + } + else + { + NMLOG_INFO("ssid info is different continue to store ssid %s new ssid %s", param.wifiCredentials.cSSID, ssid.c_str()); + } + } + + // Copy SSID + strncpy(setParam.wifiCredentials.cSSID, ssid.c_str(), sizeof(setParam.wifiCredentials.cSSID)); + + // Copy passphrase - only if not empty + if (!passphrase.empty()) { + strncpy(setParam.wifiCredentials.cPassword, passphrase.c_str(), sizeof(setParam.wifiCredentials.cPassword)); + NMLOG_DEBUG("WiFi passphrase set for MfrMgr save"); + } else { + param.wifiCredentials.cPassword[0] = '\0'; // Ensure empty string + NMLOG_DEBUG("Empty passphrase - setting empty string in MfrMgr"); + } + + setParam.wifiCredentials.iSecurityMode = securityMode; + + setParam.requestType = WIFI_SET_CREDENTIALS; + NMLOG_INFO(" Set Params param.requestType = %d, param.wifiCredentials.cSSID = %s, param.wifiCredentials.iSecurityMode = %d", setParam.requestType, setParam.wifiCredentials.cSSID, setParam.wifiCredentials.iSecurityMode); + // Make IARM Bus call to save credentials + ret = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_WIFI_Credentials, + (void*)&setParam, sizeof(setParam)); + if(ret == IARM_RESULT_SUCCESS) + { + memset(¶m,0,sizeof(param)); + param.requestType = WIFI_GET_CREDENTIALS; + ret = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_WIFI_Credentials, (void*)¶m, sizeof(param)); + if(security == Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_NONE) + securityMode = NET_WIFI_SECURITY_NONE; + else if(security == Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE) + securityMode = NET_WIFI_SECURITY_WPA3_SAE; + else + securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + + if (ret == IARM_RESULT_SUCCESS) + { + if ((strcmp (param.wifiCredentials.cSSID, ssid.c_str()) == 0) && + (strcmp (param.wifiCredentials.cPassword, passphrase.c_str()) == 0) && + (param.wifiCredentials.iSecurityMode == securityMode)) + { + NMLOG_INFO("Successfully stored the credentials and verified stored ssid %s current ssid %s and security_mode %d", param.wifiCredentials.cSSID, ssid.c_str(), param.wifiCredentials.iSecurityMode); + return true; + } + else + { + NMLOG_INFO("failure in storing wifi credentials ssid %s", ssid.c_str()); + } + } + } + else + { + NMLOG_ERROR("IARM Bus call failed for WiFi credentials save: %d", ret); + return false; + } + + NMLOG_INFO("Successfully saved WiFi settings to MfrMgr via IARM - SSID: %s, Security: %d", ssid.c_str(), security); + return true; + } + + bool NetworkManagerMfrManager::clearWiFiSettingsFromMfr() + { + if (!ensureIARMConnection()) { + NMLOG_ERROR("IARM connection not available for clearing WiFi settings"); + return false; + } + + NMLOG_DEBUG("Clearing WiFi settings from MfrMgr via IARM"); + + // Make IARM Bus call to clear credentials + IARM_Result_t ret = IARM_Bus_Call(IARM_BUS_MFRLIB_NAME,IARM_BUS_MFRLIB_API_WIFI_EraseAllData,0,0); + if (ret != IARM_RESULT_SUCCESS) { + NMLOG_ERROR("IARM Bus call failed for WiFi credentials clear: %d", ret); + return false; + } + + NMLOG_INFO("Successfully cleared WiFi settings from MfrMgr via IARM"); + return true; + } + + } // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeMfrMgr.h b/plugin/gnome/NetworkManagerGnomeMfrMgr.h new file mode 100644 index 000000000..3bc8c422f --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeMfrMgr.h @@ -0,0 +1,66 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include + +typedef enum _SsidSecurity +{ + NET_WIFI_SECURITY_NONE = 0, + NET_WIFI_SECURITY_WEP_64, + NET_WIFI_SECURITY_WEP_128, + NET_WIFI_SECURITY_WPA_PSK_TKIP, + NET_WIFI_SECURITY_WPA_PSK_AES, + NET_WIFI_SECURITY_WPA2_PSK_TKIP, + NET_WIFI_SECURITY_WPA2_PSK_AES, + NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA_WPA2_PSK, + NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE, + NET_WIFI_SECURITY_WPA3_PSK_AES, + NET_WIFI_SECURITY_WPA3_SAE, + NET_WIFI_SECURITY_NOT_SUPPORTED = 99, +} SsidSecurity; + +namespace WPEFramework +{ + namespace Plugin + { + class NetworkManagerMfrManager + { + public: + static NetworkManagerMfrManager* getInstance(); + bool saveWiFiSettingsToMfr(); + bool clearWiFiSettingsFromMfr(); + + private: + NetworkManagerMfrManager(); + ~NetworkManagerMfrManager(); + + // Disable copy constructor and assignment operator + NetworkManagerMfrManager(const NetworkManagerMfrManager&) = delete; + NetworkManagerMfrManager& operator=(const NetworkManagerMfrManager&) = delete; + + bool saveWiFiSettingsToMfrSync(const std::string& ssid, const std::string& passphrase, int security); + }; + } // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeProxy.cpp b/plugin/gnome/NetworkManagerGnomeProxy.cpp new file mode 100644 index 000000000..7c877f5fe --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeProxy.cpp @@ -0,0 +1,975 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include "NetworkManagerImplementation.h" +#include "NetworkManagerGnomeWIFI.h" +#include "NetworkManagerGnomeEvents.h" +#include "NetworkManagerGnomeUtils.h" +#include +#include +#include +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using namespace std; + +namespace WPEFramework +{ + namespace Plugin + { + /* + * Per-call NMClient helpers (same pattern as wifiManager). + * A fresh NMClient is created for each proxy API call and destroyed + * immediately after use, so no D-Bus signals accumulate between calls. + */ + static NMClient* createProxyClient(GMainContext *ctx) + { + GError *error = NULL; + g_main_context_push_thread_default(ctx); + NMClient *client = nm_client_new(NULL, &error); + g_main_context_pop_thread_default(ctx); + if (!client) { + if (error) { + NMLOG_ERROR("Failed to create NMClient: %s", error->message); + g_error_free(error); + } + } + return client; + } + + static void deleteProxyClient(NMClient *client) + { + if (!client) + return; + GMainContext *context = g_main_context_ref(nm_client_get_main_context(client)); + GObject *contextBusyWatcher = nm_client_get_context_busy_watcher(client); + g_object_add_weak_pointer(contextBusyWatcher, (gpointer *)&contextBusyWatcher); + g_clear_object(&client); + while (contextBusyWatcher) { + g_main_context_iteration(context, TRUE); + } + g_main_context_unref(context); + } + + wifiManager *wifi = nullptr; + GnomeNetworkManagerEvents *nmEvent = nullptr; + NetworkManagerImplementation* _instance = nullptr; + + void NetworkManagerInternalEventHandler(const char *owner, int eventId, void *data, size_t len) + { + return; + } + + static NMDeviceState ifaceState(NMClient *client, const char* interface) + { + NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; + NMDevice *device = NULL; + if(client == NULL) + return deviceState; + + if(string("eth0_missing") == interface || string("wlan0_missing") == interface) + { + NMLOG_DEBUG("interface %s is not valid", interface); + return deviceState; + } + + device = nm_client_get_device_by_iface(client, interface); + if (device == NULL) { + NMLOG_FATAL("libnm doesn't have device corresponding to %s", interface); + return deviceState; + } + + deviceState = nm_device_get_state(device); + return deviceState; + } + + static bool setHostname(NMConnection *connection, const std::string& hostname) + { + GError *error = NULL; + NMSettingIPConfig *sIPv4 = NULL; + NMSettingIPConfig *sIPv6 = NULL; + bool needChange = false; + + if(connection == NULL) { + NMLOG_ERROR("Connection is NULL"); + return false; + } + + sIPv4 = nm_connection_get_setting_ip4_config(connection); + if (sIPv4) { + const char* existingHostname = nm_setting_ip_config_get_dhcp_hostname(sIPv4); + gboolean sendHostname = nm_setting_ip_config_get_dhcp_send_hostname(sIPv4); + + if (!sendHostname || !existingHostname || hostname != existingHostname) { + needChange = true; + } + } + else + needChange = true; + + sIPv6 = nm_connection_get_setting_ip6_config(connection); + if (sIPv6) { + const char* existingHostname = nm_setting_ip_config_get_dhcp_hostname(sIPv6); + gboolean sendHostname = nm_setting_ip_config_get_dhcp_send_hostname(sIPv6); + + if (!sendHostname || !existingHostname || hostname != existingHostname) { + needChange = true; + } + } + else + needChange = true; + + if (!needChange) { + NMLOG_DEBUG("Hostname already set to '%s', no changes needed", hostname.c_str()); + return true; + } + + NMLOG_DEBUG("Setting hostname to '%s' for connection '%s'", hostname.c_str(), nm_connection_get_id(connection)); + + if (!sIPv4) { + sIPv4 = NM_SETTING_IP_CONFIG(nm_setting_ip4_config_new()); + nm_connection_add_setting(connection, NM_SETTING(sIPv4)); + } + g_object_set(sIPv4, + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, + NULL); + + if (!sIPv6) { + sIPv6 = NM_SETTING_IP_CONFIG(nm_setting_ip6_config_new()); + nm_connection_add_setting(connection, NM_SETTING(sIPv6)); + } + g_object_set(sIPv6, + NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), + NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, + NULL); + + nm_remote_connection_commit_changes(NM_REMOTE_CONNECTION(connection), TRUE, NULL, &error); + if(error) { + NMLOG_ERROR("Failed to commit changes: %s", error->message); + g_error_free(error); + return false; + } + + return true; + } + + static bool modifyDefaultConnConfig(NMClient *client) + { + const GPtrArray *connections = NULL; + NMConnection *connection = NULL; + std::string hostname{}; + + if (client == nullptr) { + NMLOG_ERROR("NMClient is NULL"); + return false; + } + + // read persistent hostname if exist + if(!nmUtils::readPersistentHostname(hostname)) + { + hostname = nmUtils::deviceHostname(); // default hostname as default hostname + } + + // Validate hostname is non-empty regardless of source (persistent or default) + if(hostname.empty()) + { + NMLOG_WARNING("Hostname is empty. No modification will be made to NM connections."); + return false; + } + + connections = nm_client_get_connections(client); + if (connections == NULL || connections->len == 0) { + NMLOG_ERROR("Could not get nm connections"); + return false; + } + + for (uint32_t i = 0; i < connections->len; i++) + { + connection = NM_CONNECTION(connections->pdata[i]); + if(connection == NULL) + { + NMLOG_ERROR("Connection at index %d is NULL", i); + continue; + } + + const char *iface = nm_connection_get_interface_name(connection); + if(!iface) + { + NMLOG_WARNING("Failed to get connection interface name !"); + continue; + } + + std::string interface = iface; + if(interface != nmUtils::ethIface() && interface != nmUtils::wlanIface()) { + NMLOG_DEBUG("Skipping non-ethernet/wifi connection type: %s", interface.c_str()); + continue; + } + + if(!setHostname(connection, hostname)) { + NMLOG_WARNING("Failed to set hostname for connection at index %d", i); + } + } + + return true; + } + + /* @brief Set the dhcp hostname */ + uint32_t NetworkManagerImplementation::SetHostname(const string& hostname /* @in */) + { + if (m_nmContext == nullptr) { + NMLOG_ERROR("NMContext is NULL"); + return Core::ERROR_GENERAL; + } + + if(hostname.length() < 1 || hostname.length() > 32) + { + NMLOG_ERROR("Invalid hostname length: %zu", hostname.length()); + return Core::ERROR_BAD_REQUEST; + } + + NMClient *client = createProxyClient(m_nmContext); + if (client == nullptr) { + NMLOG_ERROR("Failed to create NMClient for SetHostname"); + return Core::ERROR_GENERAL; + } + + const GPtrArray *connections = nm_client_get_connections(client); + if (connections == NULL || connections->len == 0) + { + NMLOG_ERROR("Could not get nm connections"); + deleteProxyClient(client); + return Core::ERROR_GENERAL; + } + + NMLOG_INFO("Setting DHCP hostname to %s", hostname.c_str()); + + for (uint32_t i = 0; i < connections->len; i++) + { + NMConnection *connection = NM_CONNECTION(connections->pdata[i]); + if(connection != NULL) + { + const char *iface = nm_connection_get_interface_name(connection); + if(!iface) + { + NMLOG_WARNING("Failed to get connection interface name !"); + continue; + } + + std::string interface = iface; + if(interface != nmUtils::ethIface() && interface != nmUtils::wlanIface()) { + NMLOG_DEBUG("Skipping non-ethernet/wifi connection type: %s", interface.c_str()); + continue; + } + + if(!setHostname(connection, hostname)) + { + NMLOG_ERROR("Failed to set hostname for connection at index %d", i); + deleteProxyClient(client); + return Core::ERROR_GENERAL; + } + } + else + NMLOG_ERROR("Connection at index %d is NULL", i); + } + + deleteProxyClient(client); + + // Write the hostname to persistent storage + nmUtils::writePersistentHostname(hostname); + + return Core::ERROR_NONE; + } + + void NetworkManagerImplementation::platform_deinit() + { + if(m_nmContext) { g_main_context_unref(m_nmContext); m_nmContext = nullptr; } + } + + void NetworkManagerImplementation::platform_logging(const NetworkManagerLogger::LogLevel& level) + { + /* set networkmanager daemon log level based on current plugin log level */ + if(NetworkManagerLogger::DEBUG_LEVEL != level) + return; + nmUtils::setNetworkManagerlogLevelToTrace(); + } + + void NetworkManagerImplementation::platform_init() + { + ::_instance = this; + + // Create an isolated GMainContext for per-call NMClient creation. + m_nmContext = g_main_context_new(); + + // Create a temporary client for one-time init work + NMClient *initClient = createProxyClient(m_nmContext); + if (initClient == NULL) { + NMLOG_FATAL("Error initializing NMClient during platform_init"); + g_main_context_unref(m_nmContext); + m_nmContext = nullptr; + return; + } + + nmUtils::getDeviceProperties(); // get interface name form '/etc/device.proprties' + modifyDefaultConnConfig(initClient); + NMDeviceState ethState = ifaceState(initClient, nmUtils::ethIface()); + if(ethState > NM_DEVICE_STATE_DISCONNECTED && ethState < NM_DEVICE_STATE_DEACTIVATING) + setDefaultInterface(nmUtils::ethIface()); + else + setDefaultInterface(nmUtils::wlanIface()); + + NMLOG_INFO("default interface is %s", getDefaultInterface().c_str()); + + deleteProxyClient(initClient); + + // getInitialConnectionState function not called here, as event monitor will report the initial state + nmEvent = GnomeNetworkManagerEvents::getInstance(); + nmEvent->startNetworkMangerEventMonitor(); + wifi = wifiManager::getInstance(); + } + + uint32_t NetworkManagerImplementation::GetAvailableInterfaces (Exchange::INetworkManager::IInterfaceDetailsIterator*& interfacesItr/* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + std::vector interfaceList; + std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); + + const auto tEntry = std::chrono::steady_clock::now(); + + /* Serve from the event-maintained cache; no per-call NMClient. An interface + * absent from the cache (not yet reported by NM) is simply omitted; an empty + * list is a valid result (ground reality), not an error. */ + for (int i = 0; i < 2; ++i) + { + const std::string& ifname = (i == 0) ? ethname : wifiname; + GnomeNetworkManagerEvents::InterfaceStateInfo info; + if (!GnomeNetworkManagerEvents::getInterfaceStateCache(ifname, info)) + continue; + + Exchange::INetworkManager::InterfaceDetails interface{}; + interface.type = (i == 0) ? INTERFACE_TYPE_ETHERNET : INTERFACE_TYPE_WIFI; + interface.name = ifname; + interface.mac = info.mac; + interface.enabled = GnomeNetworkManagerEvents::isInterfaceStateEnabled(info.state); + interface.connected = GnomeNetworkManagerEvents::isInterfaceStateConnected(info.state); + + interfaceList.push_back(interface); + } + + using Implementation = RPC::IteratorType; + interfacesItr = Core::Service::Create(interfaceList); + if(interfacesItr == nullptr) { + return Core::ERROR_GENERAL; + } + rc = Core::ERROR_NONE; + + NMLOG_DEBUG("[PERF] GetAvailableInterfaces (impl) total %lld us", + static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - tEntry).count())); + return rc; + } +#if 0 + /* @brief Get the active Interface used for external world communication */ + uint32_t NetworkManagerImplementation::GetPrimaryInterface (string& interface /* @out */) + { + uint32_t rc = Core::ERROR_RPC_CALL_FAILED; + GError *error = NULL; + NMActiveConnection *activeConn = NULL; + NMRemoteConnection *remoteConn = NULL; + + if(client == nullptr) + { + NMLOG_WARNING("client connection null:"); + return Core::ERROR_GENERAL; + } + + if(!m_wlanEnabled.load() && !m_ethEnabled.load()) + { + NMLOG_INFO("Both iface disabled state, returning no primary interface"); + interface.clear(); + m_defaultInterface = interface; + return Core::ERROR_NONE; + } + + activeConn = nm_client_get_primary_connection(client); + if (activeConn != NULL) + { + remoteConn = nm_active_connection_get_connection(activeConn); + if(remoteConn != NULL) + { + const char *ifacePtr = nm_connection_get_interface_name(NM_CONNECTION(remoteConn)); + if(ifacePtr != NULL) + { + interface = ifacePtr; + if(interface != nmUtils::wlanIface() && interface != nmUtils::ethIface()) + { + NMLOG_WARNING("primary interface is not Ethernet or WiFi"); + interface.clear(); + } + else + { + NMLOG_DEBUG("primary interface is %s", interface.c_str()); + rc = Core::ERROR_NONE; + } + } + else + NMLOG_WARNING("interface name is missing form connection"); + } + else + NMLOG_WARNING("primary connection but remote connection error"); + } + else + NMLOG_WARNING("No primary connection"); + + if(rc != Core::ERROR_NONE) + { + // if no active connection or libnm error, choose the default interface based on the connection state + NMDeviceState ethState = ifaceState(client, nmUtils::ethIface()); + /* if ethernet is connected but not completely activate then ethernet is taken as primary else wifi */ + if((ethState > NM_DEVICE_STATE_DISCONNECTED && ethState < NM_DEVICE_STATE_DEACTIVATING) || m_ethConnected.load()) + interface = nmUtils::ethIface(); + else + interface = nmUtils::wlanIface(); // default is wifi + rc = Core::ERROR_NONE; + // TODO: if no proimary connection, should we return empty string? RDK V return empty string + } + + m_defaultInterface = interface; + return rc; + } +#endif + + uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool enabled /* @in */) + { + + if(m_nmContext == nullptr) + { + NMLOG_WARNING("NMContext is null"); + return Core::ERROR_RPC_CALL_FAILED; + } + + if(interface.empty() || (interface != nmUtils::wlanIface() && interface != nmUtils::ethIface())) + { + NMLOG_ERROR("interface: %s; not valied", interface.c_str()!=nullptr? interface.c_str():"empty"); + return Core::ERROR_GENERAL; + } + + // For ethernet enable: run BOOT_MIGRATION cleanup first, then setInterfaceState + if(enabled && interface == nmUtils::ethIface()) + { + // Check boot type and delete all ethernet NM connections if BOOT_MIGRATION + { + const char* bootFile = "/tmp/bootType"; + std::ifstream file(bootFile); + + if(file.is_open()) + { + std::string line, bootTypeValue; + while(std::getline(file, line)) + { + const std::string key = "BOOT_TYPE="; + auto pos = line.find(key); + if(pos != std::string::npos) + { + bootTypeValue = line.substr(pos + key.size()); + break; + } + } + + if(bootTypeValue == "BOOT_MIGRATION") + { + NMLOG_INFO("BOOT_MIGRATION detected, deleting all wired NM connections"); + + NMClient *client = createProxyClient(m_nmContext); + if (client != nullptr) + { + // Bring down the ethernet interface before wiping its connections + // so NM doesn't immediately re-activate them during deletion. + NMDevice *ethDev = nm_client_get_device_by_iface(client, interface.c_str()); + if(ethDev) + { + GError *discError = nullptr; + if(!nm_device_disconnect(ethDev, nullptr, &discError)) + { + NMLOG_WARNING("Failed to disconnect %s before migration cleanup: %s", + interface.c_str(), + discError ? discError->message : "unknown error"); + if(discError) g_error_free(discError); + } + } + + const GPtrArray *connections = nm_client_get_connections(client); + if(connections && connections->len > 0) + { + /* Snapshot the list before iterating: nm_client_get_connections() + * returns an internal array that can be mutated as connections + * are removed, so we must not iterate it while deleting. */ + GPtrArray *snapshot = g_ptr_array_new_full(connections->len, g_object_unref); + for(guint i = 0; i < connections->len; ++i) + { + NMRemoteConnection *conn = NM_REMOTE_CONNECTION(connections->pdata[i]); + if(!conn) continue; + NMSettingConnection *sCon = nm_connection_get_setting_connection(NM_CONNECTION(conn)); + if(!sCon) continue; + const char *connType = nm_setting_connection_get_connection_type(sCon); + if(g_strcmp0(connType, NM_SETTING_WIRED_SETTING_NAME) != 0) + { + NMLOG_DEBUG("Skipping non-wired connection type: %s", connType ? connType : "null"); + continue; + } + g_ptr_array_add(snapshot, g_object_ref(conn)); + } + + for(guint i = 0; i < snapshot->len; ++i) + { + NMRemoteConnection *conn = NM_REMOTE_CONNECTION(snapshot->pdata[i]); + GError *error = nullptr; + if(!nm_remote_connection_delete(conn, nullptr, &error)) + { + const char *connId = nm_connection_get_id(NM_CONNECTION(conn)); + NMLOG_ERROR("Failed to delete connection %s: %s", + connId ? connId : "", + error ? error->message : "unknown error"); + if(error) g_error_free(error); + } + } + g_ptr_array_unref(snapshot); + } + deleteProxyClient(client); + } + } + } + } + + NMLOG_INFO("Adding minimal ethernet connection profile ..."); + wifi->addMinimalEthernetConnection(nmUtils::ethIface()); + + if(!wifi->setInterfaceState(interface, enabled)) + { + NMLOG_ERROR("interface state change failed"); + return Core::ERROR_GENERAL; + } + + NMLOG_INFO("interface %s state: %s", interface.c_str(), enabled ? "enabled" : "disabled"); + if(_instance != NULL) + _instance->m_ethEnabled.store(enabled); + + sleep(1); // wait for 1 sec to change the device state + NMLOG_INFO("Activating connection 'Wired connection 1' ..."); + // default wired connection name is 'Wired connection 1' + wifi->activateKnownConnection(nmUtils::ethIface(), "Wired connection 1"); + } + else + { + if(!wifi->setInterfaceState(interface, enabled)) + { + NMLOG_ERROR("interface state change failed"); + return Core::ERROR_GENERAL; + } + + NMLOG_INFO("interface %s state: %s", interface.c_str(), enabled ? "enabled" : "disabled"); + // update the interface global cache state + if(interface == nmUtils::wlanIface() && _instance != NULL) + _instance->m_wlanEnabled.store(enabled); + else if(interface == nmUtils::ethIface() && _instance != NULL) + _instance->m_ethEnabled.store(enabled); + + if(enabled && interface == nmUtils::wlanIface() && _instance != NULL) + { + sleep(1); // wait for 1 sec to change the device state + const string lastConnectedSSID = getLastConnectedSSID(); + NMLOG_INFO("Activating connection '%s' ...", lastConnectedSSID.c_str()); + wifi->activateKnownConnection(nmUtils::wlanIface(), lastConnectedSSID); + } + } + + return Core::ERROR_NONE; + } + + uint32_t NetworkManagerImplementation::GetInterfaceState(const string& interface/* @in */, bool& isEnabled /* @out */) + { + isEnabled = false; + std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); + + const auto tEntry = std::chrono::steady_clock::now(); + + if(interface.empty() || (wifiname != interface && ethname != interface)) + { + NMLOG_ERROR("interface: %s; not valied", interface.c_str()!=nullptr? interface.c_str():"empty"); + return Core::ERROR_GENERAL; + } + + /* Serve from the event-maintained cache; no per-call NMClient. */ + GnomeNetworkManagerEvents::InterfaceStateInfo info; + if (!GnomeNetworkManagerEvents::getInterfaceStateCache(interface, info)) + { + NMLOG_WARNING("%s : state not known yet", interface.c_str()); + return Core::ERROR_GENERAL; + } + + isEnabled = GnomeNetworkManagerEvents::isInterfaceStateEnabled(info.state); + NMLOG_INFO("%s : %s", interface.c_str(), isEnabled?"enabled":"disabled"); + NMLOG_DEBUG("[PERF] GetInterfaceState (impl) total %lld us", + static_cast(std::chrono::duration_cast(std::chrono::steady_clock::now() - tEntry).count())); + return Core::ERROR_NONE; + } + + /* @brief Get IP Address Of the Interface */ + uint32_t NetworkManagerImplementation::GetIPSettings(string& interface /* @inout */, const string &ipversion /* @in */, IPAddress& result /* @out */) + { + std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); + + if(interface.empty()) + { + if(Core::ERROR_NONE != GetPrimaryInterface(interface)) + { + NMLOG_WARNING("default interface get failed"); + return Core::ERROR_NONE; + } + + if(interface.empty()) + { + NMLOG_WARNING("default interface is empty, no active interface"); + return Core::ERROR_GENERAL; + } + } + else if(wifiname != interface && ethname != interface) + { + NMLOG_ERROR("interface: %s; not valid", interface.c_str()); + return Core::ERROR_GENERAL; + } + + string ipversionStr = ipversion.empty() ? "IPv4" : ipversion; + std::string family = nmUtils::caseInsensitiveCompare(ipversionStr, "IPv6") ? "IPv6" : "IPv4"; + + result = IPAddress{}; + + // Serve from event-driven cache + if (lookupIpCache(interface, family, result)) + { + NMLOG_DEBUG("%s %s address from cache", interface.c_str(), family.c_str()); + } + else + { + NMLOG_DEBUG("no %s address on %s", family.c_str(), interface.c_str()); + } + result.ipversion = family; + + return Core::ERROR_NONE; + } + + /* @brief Set IP Address Of the Interface */ + uint32_t NetworkManagerImplementation::SetIPSettings(const string& interface /* @in */, const IPAddress& address /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if (("IPv4" != address.ipversion) && ("IPv6" != address.ipversion)) + { + return Core::ERROR_BAD_REQUEST; + } + if(wifi->setIpSettings(interface, address)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) + { + uint32_t rc = Core::ERROR_RPC_CALL_FAILED; + + std::vector filteredSsids; + std::vector filteredFrequencies; + + if(ssids) + { + string tmpssidlist{}; + while (ssids->Next(tmpssidlist) == true) + { + if (!tmpssidlist.empty()) + { + filteredSsids.push_back(tmpssidlist); + NMLOG_DEBUG("%s added to SSID filtering", tmpssidlist.c_str()); + } + else + { + NMLOG_DEBUG("Empty SSID encountered in input list; skipping."); + } + } + } + + if (frequencies) + { + string frequency{}; + while (frequencies->Next(frequency) == true) + { + if (!frequency.empty()) + { + Core::JSON::EnumType parsedFrequency; + parsedFrequency.FromString(frequency); + const string normalizedFrequency = parsedFrequency.Data(); + if ((!normalizedFrequency.empty()) && (normalizedFrequency == frequency)) + { + filteredFrequencies.push_back(normalizedFrequency); + NMLOG_DEBUG("Frequency %s added to scan filtering", normalizedFrequency.c_str()); + } + else + { + NMLOG_ERROR("Invalid frequency value: %s", frequency.c_str()); + return Core::ERROR_BAD_REQUEST; + } + } + else + { + NMLOG_DEBUG("Empty frequency encountered in input list; skipping."); + } + } + } + + m_filterVectorsLock.Lock(); + // Replace existing stored filters only after successful parsing/validation. + m_filterSsidslist.clear(); + m_filterFrequencies.clear(); + m_filterSsidslist = filteredSsids; + m_filterFrequencies = filteredFrequencies; + m_filterVectorsLock.Unlock(); + + nmEvent->setwifiScanOptions(true); + if(wifi->wifiScanRequest(filteredSsids)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::StopWiFiScan(void) + { + uint32_t rc = Core::ERROR_NONE; + // TODO explore wpa_supplicant stop + nmEvent->setwifiScanOptions(false); // This will stop periodic posting of onAvailableSSID event + NMLOG_INFO ("StopWiFiScan is success"); + return rc; + } + + uint32_t NetworkManagerImplementation::GetKnownSSIDs(IStringIterator*& ssids /* @out */) + { + uint32_t rc = Core::ERROR_RPC_CALL_FAILED; + // TODO Fix the RPC waring [Process.cpp:78](Dispatch)<1>: We still have living object [1] + std::list ssidList; + if(wifi->getKnownSSIDs(ssidList)) + { + if (!ssidList.empty()) + { + ssids = Core::Service::Create(ssidList); + if(ssids == nullptr) { + return Core::ERROR_GENERAL; + } + rc = Core::ERROR_NONE; + } + else + { + NMLOG_INFO("known ssids not found !"); + rc = Core::ERROR_GENERAL; + } + } + + return rc; + } + + uint32_t NetworkManagerImplementation::AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + NMLOG_WARNING("ssid security %d", ssid.security); + if(wifi->addToKnownSSIDs(ssid)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::ConnectToKnownSSID(const string& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->connectToKnownSSID(ssid)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::RemoveKnownSSID(const string& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->removeKnownSSID(ssid)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::WiFiConnect(const WiFiConnectTo& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + + if(ssid.ssid.empty()) + { + NMLOG_WARNING("ssid is empty activating last connected ssid !"); + if(_instance != NULL && wifi->activateKnownConnection(nmUtils::wlanIface(), getLastConnectedSSID())) + { + rc = Core::ERROR_NONE; + } + else + { + NMLOG_ERROR("activating last connected ssid failed"); + } + return rc; + } + + if(ssid.ssid.size() > 32) + { + NMLOG_WARNING("SSID is invalid"); + return rc; + } + + if(!ssid.bssid.empty() && !nmUtils::isValidBSSID(ssid.bssid)) + { + return Core::ERROR_GENERAL; + } + + if(wifi->wifiConnect(ssid)) + rc = Core::ERROR_NONE; + + return rc; + } + + uint32_t NetworkManagerImplementation::WiFiDisconnect(void) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->wifiDisconnect()) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::EthernetDeactivate(void) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->ethernetDeactivate()) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::ReacquireDHCPLease(const string& iface) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->reacquireDhcpLease(iface)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) + { + uint32_t rc = Core::ERROR_RPC_CALL_FAILED; + if(wifi->wifiConnectedSSIDInfo(ssidInfo)) + rc = Core::ERROR_NONE; + return rc; + } + +#if 0 + uint32_t NetworkManagerImplementation::GetWiFiSignalQuality(string& ssid /* @out */, string& strength /* @out */, WiFiSignalQuality& quality /* @out */) + { + float rssi = 0.0f; + float noise = 0.0f; + float floatSignalStrength = 0.0f; + WiFiSSIDInfo ssidInfo; + if(wifi->wifiConnectedSSIDInfo(ssidInfo)) + { + ssid = ssidInfo.ssid; + if (!ssidInfo.strength.empty()) + rssi = std::stof(ssidInfo.strength.c_str()); + if (!ssidInfo.noise.empty()) + noise = std::stof(ssidInfo.noise.c_str()); + floatSignalStrength = (rssi - noise); + if (floatSignalStrength < 0) + floatSignalStrength = 0.0; + + strengthOut = static_cast(floatSignalStrength); + NMLOG_INFO ("WiFiSignalQuality in dB = %u",strengthOut); + + if (strengthOut == 0) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED; + signalStrength = "0"; + } + else if (strengthOut > 0 && strengthOut < NM_WIFI_SNR_THRESHOLD_FAIR) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_WEAK; + } + else if (strengthOut > NM_WIFI_SNR_THRESHOLD_FAIR && strengthOut < NM_WIFI_SNR_THRESHOLD_GOOD) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_FAIR; + } + else if (strengthOut > NM_WIFI_SNR_THRESHOLD_GOOD && strengthOut < NM_WIFI_SNR_THRESHOLD_EXCELLENT) + { + quality = WiFiSignalQuality::WIFI_SIGNAL_GOOD; + } + else + { + quality = WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT; + } + + signalStrength = std::to_string(strengthOut); + + NMLOG_INFO ("GetWiFiSignalQuality success"); + + rc = Core::ERROR_NONE; + } + return rc; + } +#endif + + uint32_t NetworkManagerImplementation::GetWifiState(WiFiState &state) + { + uint32_t rc = Core::ERROR_GENERAL; + if(wifi->getWifiState(state)) + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) + { + uint32_t rc = Core::ERROR_NONE; + if(method == WIFI_WPS_SERIALIZED_PIN || method == WIFI_WPS_PIN) + { + NMLOG_ERROR("WPS PIN method is not supported as of now"); + return Core::ERROR_RPC_CALL_FAILED; + } + + if(!wifi->wifiScanRequest()) + { + NMLOG_WARNING("scanning request failed; trying to connect wps"); + } + + if(wifi->startWPS()) + NMLOG_INFO ("start WPS success"); + else { + rc = Core::ERROR_GENERAL; + NMLOG_ERROR("start WPS failed"); + } + + return rc; + } + + uint32_t NetworkManagerImplementation::StopWPS(void) + { + uint32_t rc = Core::ERROR_NONE; + if(wifi->stopWPS()) + NMLOG_INFO ("cancelWPS success"); + else + rc = Core::ERROR_RPC_CALL_FAILED; + return rc; + } + + } +} diff --git a/plugin/gnome/NetworkManagerGnomeUtils.cpp b/plugin/gnome/NetworkManagerGnomeUtils.cpp new file mode 100644 index 000000000..fa2b28690 --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeUtils.cpp @@ -0,0 +1,476 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Module.h" + +#include "NetworkManagerLogger.h" +#include "NetworkManagerGnomeUtils.h" +#include "NetworkManagerImplementation.h" +#include "INetworkManager.h" + +namespace WPEFramework +{ + namespace Plugin + { + static std::string m_ethifname = "eth0"; + static std::string m_wlanifname = "wlan0"; + static std::string m_deviceHostname = "rdk-device"; // default hostname can be empty if not set in /etc/device.properties + + const char* nmUtils::wlanIface() {return m_wlanifname.c_str();} + const char* nmUtils::ethIface() {return m_ethifname.c_str();} + const char* nmUtils::deviceHostname() {return m_deviceHostname.c_str();} + + uint8_t nmUtils::wifiSecurityModeFromAp(const std::string& ssid, guint32 flags, guint32 wpaFlags, guint32 rsnFlags, bool doPrint) + { + uint8_t security = Exchange::INetworkManager::WIFI_SECURITY_NONE; + if(!doPrint) + NMLOG_DEBUG("ap [%s] security str %s", ssid.c_str(), nmUtils::getSecurityModeString(flags, wpaFlags, rsnFlags).c_str()); + else + NMLOG_INFO("ap [%s] security str %s", ssid.c_str(), nmUtils::getSecurityModeString(flags, wpaFlags, rsnFlags).c_str()); + + if ((flags != NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) && (rsnFlags == NM_802_11_AP_SEC_NONE)) // Open network + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_NONE; + else if((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) && (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) // WPA2/WPA3 Transition + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; + else if (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE) // Pure WPA3 (SAE only): WPA3-Personal + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE; + else if (wpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X || rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) // WPA2/WPA3 Enterprise: EAP present in either WPA or RSN + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_EAP; + else + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; // WPA2-PSK + + return security; + } + + // Function to convert percentage (0-100) to dBm string + int nmUtils::convertPercentageToSignalStrength(int percentage) { + + if (percentage <= 0 || percentage > 100) { + return 0; + } + + /* + * -30 dBm to -50 dBm: Excellent signal strength. + * -50 dBm to -60 dBm: Very good signal strength. + * -60 dBm to -70 dBm: Good signal strength; acceptable for basic internet browsing. + * -70 dBm to -80 dBm: Weak signal; performance may degrade, slower speeds, and possible dropouts. + * -80 dBm to -90 dBm: Very poor signal; likely unusable or highly unreliable. + * Below -90 dBm: Disconnected or too weak to establish a stable connection. + */ + + // dBm range: -30 dBm (strong) to -90 dBm (weak) + const int max_dBm = -30; + const int min_dBm = -90; + int dBm_value = max_dBm + ((min_dBm - max_dBm) * (100 - percentage)) / 100; + return dBm_value; + } + + std::string nmUtils::getSecurityModeString(guint32 flag, guint32 wpaFlags, guint32 rsnFlags) + { + std::string securityStr = "[AP type: "; + if (flag == NM_802_11_AP_FLAGS_NONE) + securityStr += "NONE "; + else + { + if ((flag & NM_802_11_AP_FLAGS_PRIVACY) != 0) + securityStr += "PRIVACY "; + if ((flag & NM_802_11_AP_FLAGS_WPS) != 0) + securityStr += "WPS "; + if ((flag & NM_802_11_AP_FLAGS_WPS_PBC) != 0) + securityStr += "WPS_PBC "; + if ((flag & NM_802_11_AP_FLAGS_WPS_PIN) != 0) + securityStr += "WPS_PIN "; + } + securityStr += "] "; + + if (!(flag & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags != NM_802_11_AP_SEC_NONE) && (rsnFlags != NM_802_11_AP_SEC_NONE)) + securityStr += ("Encrypted: "); + + if ((flag & NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) + && (rsnFlags == NM_802_11_AP_SEC_NONE)) + securityStr += ("WEP "); + if (wpaFlags != NM_802_11_AP_SEC_NONE) + securityStr += ("WPA "); + if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + securityStr += ("WPA2 "); + } + if (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE) { + securityStr += ("WPA3 "); + } + if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM)) { + securityStr += ("OWE "); + } + if ((wpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + || (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X)) { + securityStr += ("802.1X "); + } + + if (securityStr.empty()) + { + securityStr = "None"; + return securityStr; + } + + uint32_t flags[2] = { wpaFlags, rsnFlags }; + securityStr += "[WPA: "; + + for (int i = 0; i < 2; ++i) + { + if (flags[i] & NM_802_11_AP_SEC_PAIR_WEP40) + securityStr += "pair_wep40 "; + if (flags[i] & NM_802_11_AP_SEC_PAIR_WEP104) + securityStr += "pair_wep104 "; + if (flags[i] & NM_802_11_AP_SEC_PAIR_TKIP) + securityStr += "pair_tkip "; + if (flags[i] & NM_802_11_AP_SEC_PAIR_CCMP) + securityStr += "pair_ccmp "; + if (flags[i] & NM_802_11_AP_SEC_GROUP_WEP40) + securityStr += "group_wep40 "; + if (flags[i] & NM_802_11_AP_SEC_GROUP_WEP104) + securityStr += "group_wep104 "; + if (flags[i] & NM_802_11_AP_SEC_GROUP_TKIP) + securityStr += "group_tkip "; + if (flags[i] & NM_802_11_AP_SEC_GROUP_CCMP) + securityStr += "group_ccmp "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_PSK) + securityStr += "psk "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_802_1X) + securityStr += "802.1X "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_SAE) + securityStr += "sae "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_OWE) + securityStr += "owe "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_OWE_TM) + securityStr += "owe_transition_mode "; + if (flags[i] & NM_802_11_AP_SEC_KEY_MGMT_EAP_SUITE_B_192) + securityStr += "wpa-eap-suite-b-192 "; + + if (i == 0) { + securityStr += "] [RSN: "; + } + } + securityStr +="] "; + return securityStr; + } + + double nmUtils::wifiFrequencyFromAp(guint32 apFreq) + { + double freq; + if (apFreq >= 2400 && apFreq < 5000) + freq = 2.4; + else if (apFreq >= 5000 && apFreq < 6000) + freq = 5; + else if (apFreq >= 6000) + freq = 6; + else + freq = 0; + + return freq; + } + + bool nmUtils::isValidBSSID(const std::string& bssid) + { + // Regular expression to match valid BSSID formats (e.g., "00:11:22:33:44:55") + static const std::regex bssidRegex("^([0-9A-Fa-f]{2}[:]){5}([0-9A-Fa-f]{2})$"); + if (!std::regex_match(bssid, bssidRegex)) + { + NMLOG_ERROR("Invalid BSSID format Expected format is XX:XX:XX:XX:XX:XX where X is a hexadecimal digit."); + return false; + } + + return true; + } + + bool nmUtils::caseInsensitiveCompare(const std::string& str1, const std::string& str2) + { + std::string upperStr1 = str1; + std::string upperStr2 = str2; + + // Convert both strings to uppercase + std::transform(upperStr1.begin(), upperStr1.end(), upperStr1.begin(), ::toupper); + std::transform(upperStr2.begin(), upperStr2.end(), upperStr2.begin(), ::toupper); + + return upperStr1 == upperStr2; + } + + bool nmUtils::getDeviceProperties() + { + std::string line; + std::string wifiIfname; + std::string ethIfname; // cached interface name + std::string deviceHostname; + + std::ifstream file("/etc/device.properties"); + if (!file.is_open()) { + NMLOG_FATAL("/etc/device.properties opening file Error"); + return false; + } + + while (std::getline(file, line)) + { + if (line.empty()) { + continue; + } + if (line.find("ETHERNET_INTERFACE=") != std::string::npos) { + ethIfname = line.substr(line.find('=') + 1); + ethIfname.erase(ethIfname.find_last_not_of("\r\n\t") + 1); + ethIfname.erase(0, ethIfname.find_first_not_of("\r\n\t")); + if(ethIfname.empty()) + { + NMLOG_WARNING("ETHERNET_INTERFACE is empty in /etc/device.properties"); + ethIfname = "eth0_missing"; // means device doesnot have ethernet interface invalid name + } + } + + if (line.find("WIFI_INTERFACE=") != std::string::npos) { + wifiIfname = line.substr(line.find('=') + 1); + wifiIfname.erase(wifiIfname.find_last_not_of("\r\n\t") + 1); + wifiIfname.erase(0, wifiIfname.find_first_not_of("\r\n\t")); + if(wifiIfname.empty()) + { + NMLOG_WARNING("WIFI_INTERFACE is empty in /etc/device.properties"); + wifiIfname = "wlan0_missing"; // means device doesnot have wifi interface + } + } + + if (line.find("DEFAULT_HOSTNAME=") != std::string::npos) { + deviceHostname = line.substr(line.find('=') + 1); + deviceHostname.erase(deviceHostname.find_last_not_of("\r\n\t") + 1); + deviceHostname.erase(0, deviceHostname.find_first_not_of("\r\n\t")); + if(deviceHostname.empty()) + { + NMLOG_WARNING("DEFAULT_HOSTNAME is empty in /etc/device.properties"); + deviceHostname = ""; // set empty default hostname + } + } + } + file.close(); + + m_wlanifname = wifiIfname; + m_ethifname = ethIfname; + m_deviceHostname = deviceHostname; + NMLOG_INFO("/etc/device.properties eth: %s, wlan: %s, default hostname: %s", m_ethifname.c_str(), m_wlanifname.c_str(), m_deviceHostname.c_str()); + return true; + } + + bool nmUtils::setNetworkManagerlogLevelToTrace() + { + /* set networkmanager daemon log level based on current plugin log level */ + const char* command = "nmcli general logging level TRACE domains PLATFORM,DEVICE,WIFI,ETHER,DNS,DHCP4,DHCP6,DEVICE,SETTINGS,DISPATCH"; + + // Execute the command using popen + FILE *pipe = popen(command, "r"); + + if (pipe == NULL) { + NMLOG_ERROR("popen failed %s ", command); + return false; + } + + // Close the pipe and retrieve the command's exit status + int status = pclose(pipe); + if (status == -1) { + perror("pclose failed"); + return false; + } + + // Extract the exit status from the status code + int exitCode = WEXITSTATUS(status); + if (exitCode == 0) + NMLOG_INFO("NetworkManager daemon log level changed to trace"); + else + NMLOG_INFO(" '%s' failed with exit code %d.", command, exitCode); + + return exitCode == 0; + } + + void nmUtils::setMarkerFile(const char* filename, bool unmark) + { + char fqn[64]; + snprintf(fqn, sizeof(fqn), "%s", filename); + if (unmark) + { + if (remove(fqn) != 0) { + NMLOG_ERROR("Failed to remove marker file"); + } + } + else + { + FILE *fp = fopen(fqn, "w"); + if (fp != NULL) { + fclose(fp); + } else { + NMLOG_ERROR("Failed to open marker file %s", fqn); + } + } + } + + + + bool nmUtils::isInterfaceEnabled(const std::string& interface) + { + std::string markerFile; + if (interface == nmUtils::ethIface()) { + markerFile = EthernetDisableMarker; + } + else if (interface == nmUtils::wlanIface()) { + markerFile = WiFiDisableMarker; + } + else { + NMLOG_ERROR("Invalid interface name: %s", interface.c_str()); + return false; + } + + bool isAllowed = (access(markerFile.c_str(), F_OK) != 0); + NMLOG_DEBUG("isInterfaceEnabled %s: %s", interface.c_str(), isAllowed ? "true" : "false"); + return isAllowed; + } + + bool nmUtils::writePersistentHostname(const std::string& hostname) + { + if (hostname.empty()) { + NMLOG_ERROR("Cannot write empty hostname to persistent storage"); + return false; + } + + std::ofstream file(HostnameFile); + if (!file.is_open()) { + NMLOG_ERROR("Failed to open %s for writing", HostnameFile); + return false; + } + + file << hostname; + bool success = !file.fail(); + file.close(); + + if (success) + NMLOG_DEBUG("Successfully wrote hostname '%s' to %s",hostname.c_str(), HostnameFile); + else + NMLOG_ERROR("Error writing hostname to %s", HostnameFile); + + return success; + } + + bool nmUtils::readPersistentHostname(std::string& hostname) + { + std::ifstream file(HostnameFile); + if (!file.is_open()) { + NMLOG_DEBUG("Could not open %s - file may not exist yet", HostnameFile); + hostname = ""; + return false; + } + + std::string line; + if (std::getline(file, line)) { + // Remove any whitespace, newlines, etc. + line.erase(line.find_last_not_of("\r\n\t") + 1); + line.erase(0, line.find_first_not_of("\r\n\t")); + hostname = line; + file.close(); + + NMLOG_INFO("Read persistent hostname: '%s'", hostname.c_str()); + return true; + } + + NMLOG_WARNING("Persistent hostname file exists but is empty"); + hostname = ""; + file.close(); + return false; + } + + std::string nmUtils::resolveGatewayMac(const std::string& gatewayIp) + { + std::string mac = ""; + std::string arpFile = "/proc/net/arp"; + std::ifstream file(arpFile); + std::string line; + + if (!file.is_open()) { + NMLOG_ERROR("Failed to open %s", arpFile.c_str()); + return mac; + } + + // Skip header + std::getline(file, line); + + while (std::getline(file, line)) { + std::istringstream iss(line); + std::string ip, hwType, flags, hwAddr; + + if (iss >> ip >> hwType >> flags >> hwAddr) { + if (ip == gatewayIp && hwAddr != "00:00:00:00:00:00") { + mac = hwAddr; + NMLOG_INFO("Resolved gateway IP %s to MAC %s", gatewayIp.c_str(), mac.c_str()); + break; + } + } + } + + if (mac.empty()) { + NMLOG_WARNING("Could not resolve gateway IP %s to MAC address", gatewayIp.c_str()); + } + + return mac; + } + + std::string nmUtils::getGatewayMacAddress(NMDevice* device) + { + std::string gatewayMac = ""; + + if (!device) { + NMLOG_ERROR("device is NULL"); + return gatewayMac; + } + + if (!NM_IS_DEVICE(device)) { + NMLOG_ERROR("device is not an NMDevice"); + return gatewayMac; + } + + NMIPConfig *ip4Config = nm_device_get_ip4_config(device); + if (!ip4Config) { + NMLOG_WARNING("No IPv4 configuration found"); + return gatewayMac; + } + + const char *ifname = nm_device_get_iface(device); + const char *gateway = nm_ip_config_get_gateway(ip4Config); + if (!gateway) { + NMLOG_WARNING("No gateway found for %s", ifname ? ifname : "unknown"); + return gatewayMac; + } + + NMLOG_DEBUG("Found gateway IP for %s: %s", ifname ? ifname : "unknown", gateway); + gatewayMac = resolveGatewayMac(gateway); + return gatewayMac; + } + + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeUtils.h b/plugin/gnome/NetworkManagerGnomeUtils.h new file mode 100644 index 000000000..e336ddc35 --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeUtils.h @@ -0,0 +1,58 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include +#include +#include "Module.h" + +namespace WPEFramework +{ + namespace Plugin + { + constexpr const char* EthernetDisableMarker = "/opt/persistent/ethernet.interface.disable"; + constexpr const char* WiFiDisableMarker = "/opt/persistent/wifi.interface.disable"; + constexpr const char* HostnameFile = "/opt/persistent/nm.plugin.hostname"; + class nmUtils + { + + public: + static bool getDeviceProperties(); + static const char* wlanIface(); + static const char* ethIface(); + static const char* deviceHostname(); + static int convertPercentageToSignalStrength(int percentage); + static bool caseInsensitiveCompare(const std::string& str1, const std::string& str2); + static uint8_t wifiSecurityModeFromAp(const std::string& ssid, guint32 flags, guint32 wpaFlags, guint32 rsnFlags, bool doPrint = true); + static double wifiFrequencyFromAp(guint32 apFreq); + static std::string getSecurityModeString(guint32 flags, guint32 wpaFlags, guint32 rsnFlags); + static bool setNetworkManagerlogLevelToTrace(); + static void setMarkerFile(const char* filename, bool unmark = false); + static bool isInterfaceEnabled(const std::string& interface); + static bool writePersistentHostname(const std::string& hostname); + static bool readPersistentHostname(std::string& hostname); + static std::string resolveGatewayMac(const std::string& gatewayIp); + static std::string getGatewayMacAddress(NMDevice* device); + static bool isValidBSSID(const std::string& bssid); + }; + } +} diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.cpp b/plugin/gnome/NetworkManagerGnomeWIFI.cpp new file mode 100644 index 000000000..148e54ef2 --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeWIFI.cpp @@ -0,0 +1,2746 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include + +#include +#include +#include +#include "NetworkManagerLogger.h" +#include "INetworkManager.h" +#include "NetworkManagerGnomeWIFI.h" +#include "NetworkManagerGnomeUtils.h" +#include "NetworkManagerImplementation.h" +#ifdef ENABLE_MIGRATION_MFRMGR_SUPPORT +#include "NetworkManagerGnomeMfrMgr.h" +#endif + +using namespace std; +namespace WPEFramework +{ + + namespace Plugin + { + extern NetworkManagerImplementation* _instance; + static std::atomic wpsProcessRun = {false}; + + wifiManager::wifiManager() : m_client(nullptr), m_loop(nullptr), m_createNewConnection(false), m_objectPath(nullptr), m_wifidevice(nullptr), m_source(nullptr), m_cancellable(nullptr){ + NMLOG_INFO("wifiManager"); + m_nmContext = g_main_context_new(); + // g_main_context_push_thread_default(m_nmContext); + // Do NOT push m_nmContext here. Pushing here permanently locks ownership + // to the constructor thread (owner_count stays at 1, never released). + // All callers — must push/pop around + // each createClientNewConnection()/deleteClientConnection() pair instead. + m_loop = g_main_loop_new(m_nmContext, FALSE); + } + + bool wifiManager::createClientNewConnection() + { + GError *error = NULL; + + // Serialize concurrent wifi operations from different threads + m_opMutex.lock(); + + g_main_context_push_thread_default(m_nmContext); + + m_client = nm_client_new(NULL, &error); + if (!m_client || !m_loop) { + if (error) { + NMLOG_ERROR("Could not connect to NetworkManager: %s.", error->message); + g_error_free(error); + } + g_clear_object(&m_client); + m_client = nullptr; + g_main_context_pop_thread_default(m_nmContext); + m_opMutex.unlock(); + return false; + } + + // Create new cancellable for this client session + { + std::lock_guard lock(m_cancellableMutex); + m_cancellable = g_cancellable_new(); + } + return true; + } + + void wifiManager::deleteClientConnection() + { + // Cancel all pending async operations before destroying client + { + std::lock_guard lock(m_cancellableMutex); + if(m_cancellable) { + NMLOG_DEBUG("Cancelling pending async operations"); + g_cancellable_cancel(m_cancellable); + g_clear_object(&m_cancellable); + m_cancellable = nullptr; + } + } + + if(m_client) { + GMainContext *context = g_main_context_ref(nm_client_get_main_context(m_client)); + GObject *contextBusyWatcher = nm_client_get_context_busy_watcher(m_client); + g_object_add_weak_pointer(contextBusyWatcher,(gpointer *) &contextBusyWatcher); + g_clear_object(&m_client); + while (contextBusyWatcher) + { + g_main_context_iteration(context, TRUE); + } + g_main_context_unref(context); + m_client = nullptr; + } + + if(m_objectPath) + { + NMLOG_DEBUG("Freeing object path"); + g_free(m_objectPath); + m_objectPath = nullptr; + } + + // Pop the context pushed in createClientNewConnection() + if (m_nmContext) + g_main_context_pop_thread_default(m_nmContext); + // Release operation lock acquired in createClientNewConnection() + m_opMutex.unlock(); + } + + bool wifiManager::quit(NMDevice *wifiNMDevice) + { + if(!g_main_loop_is_running(m_loop)) { + NMLOG_ERROR("g_main_loop_is not running"); + return false; + } + + g_main_loop_quit(m_loop); + return false; + } + + static gboolean gmainLoopTimoutCB(gpointer user_data) + { + wifiManager *_wifiManager = (static_cast(user_data)); + NMLOG_WARNING("GmainLoop ERROR_TIMEDOUT"); + _wifiManager->m_isSuccess = false; + g_main_loop_quit(_wifiManager->m_loop); + return true; + } + + bool wifiManager::wait(GMainLoop *loop, int timeOutMs) + { + NMLOG_DEBUG("wait started ..."); + if(g_main_loop_is_running(loop)) { + NMLOG_WARNING("g_main_loop_is running"); + return false; + } + m_source = g_timeout_source_new(timeOutMs); // 10000ms interval + g_source_set_callback(m_source, (GSourceFunc)gmainLoopTimoutCB, this, NULL); + g_source_attach(m_source, g_main_loop_get_context(loop)); + g_main_loop_run(loop); + if(m_source != nullptr) { + if(g_source_is_destroyed(m_source)) { + NMLOG_WARNING("Source has been destroyed"); + } + else { + g_source_destroy(m_source); + } + g_source_unref(m_source); + m_source = nullptr; + } + NMLOG_DEBUG("wait exited ..."); + return true; + } + + NMDevice* wifiManager::getWifiDevice() + { + NMDevice *wifiDevice = NULL; + + GPtrArray *devices = const_cast(nm_client_get_devices(m_client)); + if (devices == NULL) { + NMLOG_ERROR("Failed to get device list."); + return wifiDevice; + } + + for (guint j = 0; j < devices->len; j++) + { + NMDevice *device = NM_DEVICE(devices->pdata[j]); + const char* interface = nm_device_get_iface(device); + if(interface == nullptr) + continue; + std::string iface = interface; + if (iface == nmUtils::wlanIface()) + { + wifiDevice = device; + //NMLOG_DEBUG("Wireless Device found ifce : %s !", nm_device_get_iface (wifiDevice)); + break; + } + } + + if (wifiDevice == NULL || !NM_IS_DEVICE_WIFI(wifiDevice)) + { + NMLOG_ERROR("Wireless Device not found !"); + return NULL; + } + + NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; + deviceState = nm_device_get_state(wifiDevice); + switch (deviceState) + { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + case NM_DEVICE_STATE_UNAVAILABLE: + NMLOG_WARNING("wifi device state is not vallied; state: (%d)", deviceState); + return NULL; + break; + default: + break; + } + return wifiDevice; + } + + static void getApInfo(NMAccessPoint *AccessPoint, Exchange::INetworkManager::WiFiSSIDInfo &wifiInfo, bool doPrint = true) + { + guint32 flags, wpaFlags, rsnFlags, freq, bitrate; + guint8 strength; + gint16 noise; + GBytes *ssid = NULL; + const char *hwaddr = NULL; + NM80211Mode mode; + /* Get AP properties */ + flags = nm_access_point_get_flags(AccessPoint); + wpaFlags = nm_access_point_get_wpa_flags(AccessPoint); + rsnFlags = nm_access_point_get_rsn_flags(AccessPoint); + ssid = nm_access_point_get_ssid(AccessPoint); + hwaddr = nm_access_point_get_bssid(AccessPoint); + freq = nm_access_point_get_frequency(AccessPoint); + mode = nm_access_point_get_mode(AccessPoint); + bitrate = nm_access_point_get_max_bitrate(AccessPoint); + strength = nm_access_point_get_strength(AccessPoint); + noise = 0; /* ToDo: Returning as 0 as of now. Need to fetch actual noise value */ + + /* Convert to strings */ + if (ssid) + { + char* ssidStr = nm_utils_ssid_to_utf8((const guint8*)g_bytes_get_data(ssid, NULL), g_bytes_get_size(ssid)); + if(ssidStr != nullptr) + { + wifiInfo.ssid = ssidStr; + free(ssidStr); + } + else + { + NMLOG_ERROR("Invalid ssid length Error"); + wifiInfo.ssid.clear(); + return; + } + } + else + { + /* hidden ssid */ + wifiInfo.ssid.clear(); + NMLOG_DEBUG("hidden ssid found bssid: %s", hwaddr); + } + + wifiInfo.bssid = (hwaddr != nullptr) ? hwaddr : "-----"; + wifiInfo.frequency = ((double)freq/1000); + + wifiInfo.rate = bitrate; + wifiInfo.noise = noise; + NMLOG_DEBUG("bitrate : %d kbit/s", wifiInfo.rate); + //TODO signal strength to dBm + wifiInfo.strength = nmUtils::convertPercentageToSignalStrength(strength); + wifiInfo.security = static_cast(nmUtils::wifiSecurityModeFromAp(wifiInfo.ssid, flags, wpaFlags, rsnFlags, doPrint)); + if(doPrint) + { + NMLOG_INFO("ssid: %s, frequency: %f, strength: %d, security: %u", wifiInfo.ssid.c_str(), wifiInfo.frequency, wifiInfo.strength, wifiInfo.security); + NMLOG_DEBUG("Mode: %s", mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc": mode == NM_802_11_MODE_INFRA ? "Infrastructure": "Unknown"); + } + else + { + NMLOG_DEBUG("ssid: %s, frequency: %f, strength: %d, security: %u", wifiInfo.ssid.c_str(), wifiInfo.frequency, wifiInfo.strength, wifiInfo.security); + NMLOG_DEBUG("Mode: %s", mode == NM_802_11_MODE_ADHOC ? "Ad-Hoc": mode == NM_802_11_MODE_INFRA ? "Infrastructure": "Unknown"); + } + } + + bool static getConnectedAPInfo(NMDevice *device, Exchange::INetworkManager::WiFiSSIDInfo &wifiInfo) + { + NMDeviceState deviceState = nm_device_get_state(device); + if (deviceState == NM_DEVICE_STATE_ACTIVATED) + { + NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(device)); + if(activeAP == nullptr) + return false; + getApInfo(activeAP, wifiInfo, false); + NMLOG_INFO("connected ssid: %s", wifiInfo.ssid.c_str()); + return true; + } + return false; + } + + bool wifiManager::getWifiState(Exchange::INetworkManager::WiFiState& state) + { + if(!createClientNewConnection()) + return false; + + NMDevice *wifiDevice = getWifiDevice(); + if(wifiDevice == NULL) { + NMLOG_WARNING("wifi state disabled !"); + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISABLED; + deleteClientConnection(); + return true; + } + NMDeviceState deviceState = nm_device_get_state(wifiDevice); + // Todo check NMDeviceStateReason for more information + switch(deviceState) + { + case NM_DEVICE_STATE_ACTIVATED: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED; + break; + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_PAIRING; + break; + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTING; + break; + case NM_DEVICE_STATE_DEACTIVATING: + case NM_DEVICE_STATE_DISCONNECTED: + case NM_DEVICE_STATE_UNAVAILABLE: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED; + break; + default: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISABLED; + } + + NMLOG_INFO("wifi state (%d) mapped state (%d) ", (int)deviceState, (int)state); + deleteClientConnection(); + return true; + } + + bool wifiManager::wifiConnectedSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo) + { + if(!createClientNewConnection()) + return false; + + NMDevice* wifiDevice = getWifiDevice(); + if(wifiDevice == NULL) { + NMLOG_FATAL("NMDeviceWifi * NULL !"); + deleteClientConnection(); + return false; + } + + NMDeviceState deviceState = nm_device_get_state(wifiDevice); + if(deviceState >= NM_DEVICE_STATE_IP_CONFIG) + { + NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(wifiDevice)); + if(activeAP == NULL) { + NMLOG_ERROR("NMAccessPoint = NULL !"); + deleteClientConnection(); + return false; + } + NMLOG_DEBUG("active access point found !"); + getApInfo(activeAP, ssidinfo, false); + deleteClientConnection(); + return true; + } + else + NMLOG_WARNING("no active access point!; wifi device state: (%d)", deviceState); + + deleteClientConnection(); + return true; + } + + static void disconnectCb(GObject *object, GAsyncResult *result, gpointer user_data) + { + NMDevice *device = NM_DEVICE(object); + GError *error = NULL; + wifiManager *_wifiManager = static_cast(user_data); + + NMLOG_DEBUG("Disconnecting... "); + _wifiManager->m_isSuccess = true; + if (!nm_device_disconnect_finish(device, result, &error)) + { + NMLOG_ERROR("device '%s' disconnect failed !", nm_device_get_iface(device)); + if(error != NULL) + { + if(g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + NMLOG_DEBUG("Disconnect operation was cancelled"); + } + else + { + NMLOG_ERROR("Device path '%s': %s", nm_object_get_path(NM_OBJECT(device)), error->message); + } + g_error_free(error); + } + _wifiManager->m_isSuccess = false; + } + _wifiManager->quit(device); + } + + bool wifiManager::wifiDisconnect() + { + NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; + if(!createClientNewConnection()) + return false; + + NMDevice *wifiNMDevice = getWifiDevice(); + if(wifiNMDevice == NULL) { + NMLOG_WARNING("wifi state is unmanaged !"); + deleteClientConnection(); + return true; + } + + NMLOG_DEBUG("wifi device current state is %d !", deviceState); + deviceState = nm_device_get_state(wifiNMDevice); + if (deviceState <= NM_DEVICE_STATE_DISCONNECTED || deviceState == NM_DEVICE_STATE_FAILED || deviceState == NM_DEVICE_STATE_DEACTIVATING) + { + NMLOG_WARNING("wifi already disconnected !"); + deleteClientConnection(); + return true; + } + + nm_device_disconnect_async(wifiNMDevice, m_cancellable, disconnectCb, this); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + static void ethernetDeactivateCb(GObject *object, GAsyncResult *result, gpointer user_data) + { + NMClient *client = NM_CLIENT(object); + GError *error = NULL; + wifiManager *_wifiManager = static_cast(user_data); + + NMLOG_DEBUG("ethernet connection deactivating..."); + _wifiManager->m_isSuccess = true; + if (!nm_client_deactivate_connection_finish(client, result, &error)) + { + NMLOG_ERROR("ethernet connection deactivate failed !"); + if(error != NULL) + { + if(g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + NMLOG_DEBUG("Deactivate operation was cancelled"); + } + else + { + NMLOG_ERROR("Deactivate error: %s", error->message); + } + g_error_free(error); + } + _wifiManager->m_isSuccess = false; + } + _wifiManager->quit(NULL); + } + + bool wifiManager::ethernetDeactivate() + { + NMDeviceState deviceState = NM_DEVICE_STATE_UNKNOWN; + if(!createClientNewConnection()) + return false; + + NMDevice *ethDevice = nm_client_get_device_by_iface(m_client, nmUtils::ethIface()); + if(ethDevice == NULL) { + NMLOG_WARNING("ethernet device not found !"); + deleteClientConnection(); + return false; + } + + deviceState = nm_device_get_state(ethDevice); + NMLOG_DEBUG("ethernet device current state is %d !", deviceState); + if (deviceState <= NM_DEVICE_STATE_DISCONNECTED || deviceState == NM_DEVICE_STATE_FAILED || deviceState == NM_DEVICE_STATE_DEACTIVATING) + { + NMLOG_WARNING("ethernet already disconnected !"); + deleteClientConnection(); + return true; + } + + NMActiveConnection *activeConn = nm_device_get_active_connection(ethDevice); + if(activeConn == NULL) { + NMLOG_WARNING("ethernet has no active connection, nothing to deactivate !"); + deleteClientConnection(); + return true; + } + + nm_client_deactivate_connection_async(m_client, activeConn, m_cancellable, ethernetDeactivateCb, this); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + static void appliedConnCb(GObject *src, GAsyncResult *res, gpointer user_data) + { + wifiManager *_wifiManager = static_cast(user_data); + GError *error = NULL; + guint64 versionId = 0; + NMConnection *conn = nm_device_get_applied_connection_finish( + NM_DEVICE(src), res, &versionId, &error); + if (error) { + NMLOG_ERROR("reacquireDhcpLease: get_applied_connection failed: %s", error->message); + g_error_free(error); + _wifiManager->m_appliedConn = nullptr; + _wifiManager->m_isSuccess = false; + } else { + _wifiManager->m_appliedConn = conn; + _wifiManager->m_versionId = versionId; + _wifiManager->m_isSuccess = true; + } + if (_wifiManager->m_loop) + g_main_loop_quit(_wifiManager->m_loop); + } + + static void reappliedCb(GObject *src, GAsyncResult *res, gpointer user_data) + { + wifiManager *_wifiManager = static_cast(user_data); + GError *error = NULL; + nm_device_reapply_finish(NM_DEVICE(src), res, &error); + if (error) { + NMLOG_ERROR("reacquireDhcpLease: reapply failed: %s", error->message); + g_error_free(error); + _wifiManager->m_isSuccess = false; + } else { + _wifiManager->m_isSuccess = true; + } + if (_wifiManager->m_loop) + g_main_loop_quit(_wifiManager->m_loop); + NMLOG_DEBUG("reacquireDhcpLease: reapply completed for '%s'", nm_device_get_iface(NM_DEVICE(src))); + } + + bool wifiManager::reacquireDhcpLease(const std::string& iface) + { + /* No direct libnm API to trigger a DHCP renew, hence by toggling ipv4.auto-route-ext-gw on the + * APPLIED connection (not the stored profile) and calling reapply(). + */ + if(!createClientNewConnection()) + return false; + + NMDevice *device = nm_client_get_device_by_iface(m_client, iface.c_str()); + if (device == NULL) { + NMLOG_ERROR("reacquireDhcpLease: device '%s' not found", iface.c_str()); + deleteClientConnection(); + return false; + } + + /* Round 1: fetch what NM actually has applied in memory */ + m_isSuccess = false; + m_appliedConn = nullptr; + nm_device_get_applied_connection_async(device, 0, m_cancellable, appliedConnCb, this); + wait(m_loop); + + if (!m_isSuccess || m_appliedConn == nullptr) { + NMLOG_ERROR("reacquireDhcpLease: could not get applied connection for '%s'", iface.c_str()); + deleteClientConnection(); + return false; + } + + NMSettingIPConfig *s_ip4 = NM_SETTING_IP_CONFIG( + nm_connection_get_setting(m_appliedConn, NM_TYPE_SETTING_IP4_CONFIG)); + if (s_ip4 == NULL) { + NMLOG_ERROR("reacquireDhcpLease: no IPv4 settings on '%s'", iface.c_str()); + g_object_unref(m_appliedConn); + m_appliedConn = nullptr; + deleteClientConnection(); + return false; + } + + NMTernary currentVal = NM_TERNARY_DEFAULT; + g_object_get(s_ip4, NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, ¤tVal, NULL); + NMTernary newVal = (currentVal == NM_TERNARY_DEFAULT) ? NM_TERNARY_TRUE : NM_TERNARY_DEFAULT; + NMLOG_DEBUG("reacquireDhcpLease: '%s' auto-route-ext-gw %d -> %d (in-memory only)", + iface.c_str(), static_cast(currentVal), static_cast(newVal)); + g_object_set(s_ip4, NM_SETTING_IP_CONFIG_AUTO_ROUTE_EXT_GW, newVal, NULL); + + /* Round 2: reapply with version_id for race safety — no disk write */ + m_isSuccess = false; + nm_device_reapply_async(device, m_appliedConn, m_versionId, 0, m_cancellable, reappliedCb, this); + wait(m_loop); + + if (!m_isSuccess) { + NMLOG_ERROR("reacquireDhcpLease: reapply failed for '%s'", iface.c_str()); + } else { + NMLOG_INFO("reacquireDhcpLease: reapply successful on '%s'", iface.c_str()); + } + + g_object_unref(m_appliedConn); + m_appliedConn = nullptr; + deleteClientConnection(); + return m_isSuccess; + } + + static NMAccessPoint* findMatchingSSID(const GPtrArray* ApList, Exchange::INetworkManager::WiFiConnectTo& ssidInfo) + { + NMAccessPoint *AccessPoint = nullptr; + if(ssidInfo.ssid.empty()) + return nullptr; + + for (guint i = 0; i < ApList->len; i++) + { + std::string ssidstr{}; + bool ssidMatch = false; + NMAccessPoint *ap = static_cast(g_ptr_array_index(ApList, i)); + GBytes *ssidGBytes = nm_access_point_get_ssid(ap); + if(ssidGBytes == nullptr) + { + NMLOG_DEBUG("hidden ssid found, bssid: %s", nm_access_point_get_bssid(ap)); + continue; + } + + char* ssidUtf8 = nm_utils_ssid_to_utf8((const guint8*)g_bytes_get_data(ssidGBytes, NULL), g_bytes_get_size(ssidGBytes)); + if(ssidUtf8 == nullptr) + { + NMLOG_WARNING("Invalid ssid length Error"); + continue; + } + + ssidstr = ssidUtf8; + free(ssidUtf8); + + if(ssidInfo.ssid == ssidstr) + { + NMLOG_DEBUG("SSID matched: %s", ssidstr.c_str()); + ssidMatch = true; + } + else + { + // NMLOG_DEBUG("SSID did not match: expected %s, got %s", ssidInfo.ssid.c_str(), ssidstr.c_str()); + ssidMatch = false; + continue; + } + + if(!ssidInfo.bssid.empty()) + { + const char* bssid = nm_access_point_get_bssid(ap); + if(bssid == nullptr) + { + NMLOG_WARNING("BSSID is NULL for AP, skipping"); + ssidMatch = false; + continue; + } + + std::string bssidStr = bssid; + if(strcasecmp(ssidInfo.bssid.c_str(), bssidStr.c_str()) == 0) + { + NMLOG_DEBUG("BSSID matched: %s", bssidStr.c_str()); + ssidMatch = true; + } + else + { + ssidMatch = false; + NMLOG_DEBUG("SSID matched but BSSID did not match: expected %s, got %s", ssidInfo.bssid.c_str(), bssidStr.c_str()); + continue; + } + } + + if(ssidMatch) + { + NMLOG_INFO("Matching AP found : %s", ssidstr.c_str()); + AccessPoint = ap; + break; + } + } + + return AccessPoint; + } + + static void wifiConnectCb(GObject *client, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = (static_cast(user_data)); + NMActiveConnection *activeConnection = NULL; + + if (_wifiManager->m_createNewConnection) { + NMLOG_DEBUG("nm_client_add_and_activate_connection_finish"); + activeConnection = nm_client_add_and_activate_connection_finish(NM_CLIENT(_wifiManager->m_client), result, &error); + } + else { + NMLOG_DEBUG("nm_client_activate_connection_finish "); + activeConnection = nm_client_activate_connection_finish(NM_CLIENT(_wifiManager->m_client), result, &error); + } + + // Check if operation was cancelled - this is expected during cleanup + if (error) { + _wifiManager->m_isSuccess = false; + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + NMLOG_DEBUG("Connection operation was cancelled"); + } + else { + if (_wifiManager->m_createNewConnection) { + NMLOG_ERROR("Failed to add/activate new connection: %s", error->message); + } else { + NMLOG_ERROR("Failed to activate connection: %s", error->message); + } + } + g_error_free(error); + } + else { + _wifiManager->m_isSuccess = true; + } + + if(activeConnection) + g_object_unref(activeConnection); + g_main_loop_quit(_wifiManager->m_loop); + } + + static void wifiConnectTempCb(GObject *client, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = (static_cast(user_data)); + NMRemoteConnection *remoteConnection = NULL; + + remoteConnection = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error); + + if (error) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + NMLOG_DEBUG("Add temporary connection was cancelled"); + } + else { + NMLOG_ERROR("Failed to add temporary connection: %s", error->message); + } + _wifiManager->m_isSuccess = false; + g_error_free(error); + g_main_loop_quit(_wifiManager->m_loop); + return; + } + + if (remoteConnection) { + NMLOG_DEBUG("Temporary connection added, now activating..."); + + // Check if client is still valid before activating + if (_wifiManager->m_client == NULL) { + NMLOG_WARNING("Client was destroyed, cannot activate connection"); + _wifiManager->m_isSuccess = false; + g_object_unref(remoteConnection); + g_main_loop_quit(_wifiManager->m_loop); + return; + } + + // Now activate the temporary connection + nm_client_activate_connection_async(_wifiManager->m_client, + NM_CONNECTION(remoteConnection), + _wifiManager->m_wifidevice, + _wifiManager->m_objectPath, + _wifiManager->m_cancellable, + wifiConnectCb, + _wifiManager); + g_object_unref(remoteConnection); + } else { + NMLOG_ERROR("Failed to add temporary connection - no connection returned"); + _wifiManager->m_isSuccess = false; + g_main_loop_quit(_wifiManager->m_loop); + } + } + + static void wifiConnectionUpdate(GObject *rmObject, GAsyncResult *res, gpointer user_data) + { + NMRemoteConnection *remote_con = NM_REMOTE_CONNECTION(rmObject); + wifiManager *_wifiManager = (static_cast(user_data)); + GVariant *ret = NULL; + GError *error = NULL; + + ret = nm_remote_connection_update2_finish(remote_con, res, &error); + + if (!ret || error) { + if(error) { + NMLOG_ERROR("Error: %s.", error->message); + g_error_free(error); + } + _wifiManager->m_isSuccess = false; + _wifiManager->quit(NULL); + return; + } + else + g_variant_unref(ret); + + // Check if client is still valid before activating + if (_wifiManager->m_client == NULL) { + NMLOG_WARNING("Client was destroyed, cannot activate connection"); + _wifiManager->m_isSuccess = false; + _wifiManager->quit(NULL); + return; + } + + _wifiManager->m_createNewConnection = false; // no need to create new connection + nm_client_activate_connection_async( + _wifiManager->m_client, NM_CONNECTION(remote_con), _wifiManager->m_wifidevice, _wifiManager->m_objectPath, _wifiManager->m_cancellable, wifiConnectCb, _wifiManager); + } + + static NMConnection* createMinimalEthernetConnection(const std::string& iface) + { + NMConnection *connection = nm_simple_connection_new(); + + // Connection settings with autoconnect enabled + NMSettingConnection *sConnection = (NMSettingConnection *)nm_setting_connection_new(); + std::string connId = "Wired connection 1"; + char *uuidRawEth = nm_utils_uuid_generate(); + if (!uuidRawEth) + { + NMLOG_ERROR("Failed to generate UUID for ethernet connection"); + return nullptr; + } + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_ID, connId.c_str(), NULL); + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_UUID, uuidRawEth, NULL); + g_free(uuidRawEth); + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_TYPE, "802-3-ethernet", NULL); + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_INTERFACE_NAME, iface.c_str(), NULL); + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_AUTOCONNECT, TRUE, NULL); // Enable autoconnect + nm_connection_add_setting(connection, NM_SETTING(sConnection)); + + // Wired ethernet settings + NMSettingWired *sWired = (NMSettingWired *)nm_setting_wired_new(); + nm_connection_add_setting(connection, NM_SETTING(sWired)); + + // Get hostname for DHCP + std::string hostname; + if(!nmUtils::readPersistentHostname(hostname)) + { + hostname = nmUtils::deviceHostname(); + NMLOG_DEBUG("No persistent hostname found, using device hostname"); + } + + if(hostname.empty()) + NMLOG_WARNING("dhcp hostname: "); + else + NMLOG_INFO("dhcp hostname: %s", hostname.c_str()); + + // IPv4 settings with DHCP + NMSettingIP4Config *sIpv4 = (NMSettingIP4Config *)nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(sIpv4), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + if(!hostname.empty()) { + g_object_set(G_OBJECT(sIpv4), NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), NULL); + g_object_set(G_OBJECT(sIpv4), NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, NULL); + } + nm_connection_add_setting(connection, NM_SETTING(sIpv4)); + + // IPv6 settings with DHCP + NMSettingIP6Config *sIpv6 = (NMSettingIP6Config *)nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(sIpv6), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + if(!hostname.empty()) { + g_object_set(G_OBJECT(sIpv6), NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), NULL); + g_object_set(G_OBJECT(sIpv6), NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, NULL); + } + nm_connection_add_setting(connection, NM_SETTING(sIpv6)); + + NMLOG_DEBUG("Created minimal ethernet connection with autoconnect=true"); + return connection; + } + + static void addMinimalEthernetConnectionCb(GObject *client, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = static_cast(user_data); + NMRemoteConnection *remoteConn = nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error); + if (error) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + NMLOG_DEBUG("addMinimalEthernetConnection operation was cancelled"); + g_error_free(error); + if (remoteConn) + g_object_unref(remoteConn); + if (_wifiManager->m_loop && g_main_loop_is_running(_wifiManager->m_loop)) { + g_main_loop_quit(_wifiManager->m_loop); + } + return; // do not alter m_isSuccess on cancellation + } + NMLOG_ERROR("addMinimalEthernetConnection error: %s", error->message); + _wifiManager->m_isSuccess = false; + g_error_free(error); + } + else if (!remoteConn) { + NMLOG_ERROR("addMinimalEthernetConnection failed"); + _wifiManager->m_isSuccess = false; + } + else { + NMLOG_INFO("addMinimalEthernetConnection success"); + _wifiManager->m_isSuccess = true; + g_object_unref(remoteConn); + } + g_main_loop_quit(_wifiManager->m_loop); + } + + bool wifiManager::addMinimalEthernetConnection(std::string iface) + { + if (!createClientNewConnection()) + return false; + + NMConnection *ethConn = createMinimalEthernetConnection(iface); + if (ethConn == NULL) + { + NMLOG_ERROR("Failed to create minimal ethernet connection"); + deleteClientConnection(); + return false; + } + + GVariant *connSettings = nm_connection_to_dbus(ethConn, NM_CONNECTION_SERIALIZE_ALL); + g_object_unref(ethConn); + + m_isSuccess = false; + nm_client_add_connection2(m_client, + connSettings, + NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK, + NULL, TRUE, m_cancellable, + addMinimalEthernetConnectionCb, this); + g_variant_unref(connSettings); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + static bool connectionBuilder(const Exchange::INetworkManager::WiFiConnectTo& ssidinfo, NMConnection *m_connection, bool iswpsAP = false) + { + if(ssidinfo.ssid.empty() || ssidinfo.ssid.length() > 32) + { + NMLOG_WARNING("ssid name is missing or invalid"); + return false; + } + /* Build up the 'connection' Setting */ + NMSettingConnection *sConnection = (NMSettingConnection *) nm_setting_connection_new(); + char *uuidRaw = nm_utils_uuid_generate(); + if (!uuidRaw) + { + NMLOG_ERROR("Failed to generate UUID for WiFi connection"); + return false; + } + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_UUID, uuidRaw, NULL); // uuid + g_free(uuidRaw); + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_ID, ssidinfo.ssid.c_str(), NULL); // connection id = ssid + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_INTERFACE_NAME, "wlan0", NULL); // interface name + g_object_set(G_OBJECT(sConnection), NM_SETTING_CONNECTION_TYPE, "802-11-wireless", NULL); // type 802.11wireless + nm_connection_add_setting(m_connection, NM_SETTING(sConnection)); + + /* Build up the '802-11-wireless-security' settings */ + NMSettingWireless *sWireless = NULL; + sWireless = (NMSettingWireless *)nm_setting_wireless_new(); + nm_connection_add_setting(m_connection, NM_SETTING(sWireless)); + GBytes *ssid = g_bytes_new(ssidinfo.ssid.c_str(), strlen(ssidinfo.ssid.c_str())); + g_object_set(G_OBJECT(sWireless), NM_SETTING_WIRELESS_SSID, ssid, NULL); // ssid in Gbyte + g_object_set(G_OBJECT(sWireless), NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, NULL); // infra mode + if(!iswpsAP) // wps never be a hidden AP, it will be always visible + g_object_set(G_OBJECT(sWireless), NM_SETTING_WIRELESS_HIDDEN, true, NULL); // hidden = true + if(ssid) + g_bytes_unref(ssid); + + if(!ssidinfo.bssid.empty()) + { + NMLOG_INFO("bssid: %s", ssidinfo.bssid.c_str()); + g_object_set(sWireless, NM_SETTING_WIRELESS_BSSID, ssidinfo.bssid.c_str(), NULL); + } + + if(ssidinfo.frequency != Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_ALL) + { + if(ssidinfo.frequency == Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_2_4_GHZ) + { + g_object_set(sWireless, NM_SETTING_WIRELESS_BAND, "bg", NULL); + NMLOG_INFO("frequency: %s", "bg - 2.4GHz"); + } + else if(ssidinfo.frequency == Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_5_GHZ) + { + g_object_set(sWireless, NM_SETTING_WIRELESS_BAND, "a", NULL); + NMLOG_INFO("frequency: %s", "a - 5GHz"); + } + else if(ssidinfo.frequency == Exchange::INetworkManager::WIFIFrequency::WIFI_FREQUENCY_6_GHZ) + { + // TODO: 6GHz band support - not supported by current NetworkManager version 1.47.7 + NMLOG_WARNING("6GHz frequency band is not supported by the current NetworkManager version"); + return false; + } + else + { + NMLOG_WARNING("invalid frequency value: %d", ssidinfo.frequency); + return false; + } + } + + NMSettingWirelessSecurity *sSecurity = NULL; + switch(ssidinfo.security) + { + case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK: + case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE: + { + sSecurity = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(m_connection, NM_SETTING(sSecurity)); + if(Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE == ssidinfo.security) + { + NMLOG_INFO("key-mgmt: %s", "sae"); + g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"sae", NULL); + } + else + { + NMLOG_INFO("key-mgmt: %s", "wpa-psk"); + g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"wpa-psk", NULL); + } + + /* if ap is not a wps network */ + if(!iswpsAP) + { + if(ssidinfo.passphrase.empty() || ssidinfo.passphrase.length() < 8) + { + NMLOG_WARNING("password legth should be >= 8"); + return false; + } + g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_PSK, ssidinfo.passphrase.c_str(), NULL); + } + break; + } + case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_EAP: + { + NMSetting8021x *s8021X = NULL; + GError *error = NULL; + + NMLOG_INFO("key-mgmt: %s", "802.1X"); + NMLOG_DEBUG("802.1x Identity : %s", ssidinfo.eap_identity.c_str()); + NMLOG_DEBUG("802.1x CA cert path : %s", ssidinfo.ca_cert.c_str()); + NMLOG_DEBUG("802.1x Client cert path : %s", ssidinfo.client_cert.c_str()); + NMLOG_DEBUG("802.1x Private key path : %s", ssidinfo.private_key.c_str()); + NMLOG_DEBUG("802.1x Private key psswd : %s", ssidinfo.private_key_passwd.c_str()); + + s8021X = (NMSetting8021x *) nm_setting_802_1x_new(); + nm_connection_add_setting(m_connection, NM_SETTING(s8021X)); + g_object_set(s8021X, NM_SETTING_802_1X_IDENTITY, ssidinfo.eap_identity.c_str(), NULL); + nm_setting_802_1x_add_eap_method(s8021X, "tls"); + if(!ssidinfo.ca_cert.empty() && !nm_setting_802_1x_set_ca_cert(s8021X, + ssidinfo.ca_cert.c_str(), + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error)) + { + NMLOG_ERROR("ca certificate add failed"); + if(error != NULL) + { + NMLOG_ERROR("Error message %s", error->message); + g_error_free(error); + } + return false; + } + + if(!ssidinfo.client_cert.empty() && !nm_setting_802_1x_set_client_cert(s8021X, + ssidinfo.client_cert.c_str(), + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error)) + { + NMLOG_ERROR("client certificate add failed"); + if(error != NULL) + { + NMLOG_ERROR("Error message %s", error->message); + g_error_free(error); + } + return false; + } + + if(!ssidinfo.private_key.empty() && !nm_setting_802_1x_set_private_key(s8021X, + ssidinfo.private_key.c_str(), + ssidinfo.private_key_passwd.c_str(), + NM_SETTING_802_1X_CK_SCHEME_PATH, + NULL, + &error)) + { + NMLOG_ERROR("client private key failed"); + if(error != NULL) + { + NMLOG_ERROR("Error message %s", error->message); + g_error_free(error); + } + return false; + } + + sSecurity = (NMSettingWirelessSecurity *) nm_setting_wireless_security_new(); + nm_connection_add_setting(m_connection, NM_SETTING(sSecurity)); + g_object_set(G_OBJECT(sSecurity), NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,"wpa-eap", NULL); + break; + } + case Exchange::INetworkManager::WIFI_SECURITY_NONE: + { + NMLOG_WARNING("open wifi network configuration key-mgmt: none"); + break; + } + default: + { + NMLOG_ERROR("wifi securtity type not supported %d", ssidinfo.security); + return false; + } + } + + std::string hostname; + if(!nmUtils::readPersistentHostname(hostname)) + { + hostname = nmUtils::deviceHostname(); + NMLOG_DEBUG("No persistent hostname found, using device hostname"); + } + + if(hostname.empty()) + NMLOG_WARNING("dhcp hostname: "); + else + NMLOG_INFO("dhcp hostname: %s", hostname.c_str()); + + /* Build up the 'IPv4' Setting */ + NMSettingIP4Config *sIpv4Conf = (NMSettingIP4Config *) nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(sIpv4Conf), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); // autoconf = true + if(!hostname.empty()) { + g_object_set(G_OBJECT(sIpv4Conf), NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), NULL); + g_object_set(G_OBJECT(sIpv4Conf), NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, NULL); // hostname send enabled + } + nm_connection_add_setting(m_connection, NM_SETTING(sIpv4Conf)); + + /* Build up the 'IPv6' Setting */ + NMSettingIP6Config *sIpv6Conf = (NMSettingIP6Config *) nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(sIpv6Conf), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); // autoconf = true + if(!hostname.empty()) { + g_object_set(G_OBJECT(sIpv6Conf), NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, hostname.c_str(), NULL); + g_object_set(G_OBJECT(sIpv6Conf), NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME, TRUE, NULL); // hostname send enabled + } + nm_connection_add_setting(m_connection, NM_SETTING(sIpv6Conf)); + return true; + } + + + bool wifiManager::connectToKnownSSID(const std::string& ssid) + { + const GPtrArray *allnmConn = NULL; + const char* specificObjPath = "/"; + NMConnection *knownConnection = NULL; + bool ret = false; + + if(!createClientNewConnection()) + return ret; + + m_wifidevice = getWifiDevice(); + if(m_wifidevice == NULL) + { + deleteClientConnection(); + return false; + } + + allnmConn = nm_client_get_connections(m_client); + if(allnmConn == NULL || allnmConn->len == 0) + { + NMLOG_ERROR("No connections found !"); + deleteClientConnection(); + return ret; + } + + for (guint i = 0; i < allnmConn->len; i++) + { + NMConnection *conn = static_cast(g_ptr_array_index(allnmConn, i)); + if(conn == NULL) + continue; + + const char *connId = nm_connection_get_id(NM_CONNECTION(conn)); + if (connId == NULL) { + NMLOG_WARNING("connection id is NULL"); + continue; + } + + const char *connTyp = nm_connection_get_connection_type(NM_CONNECTION(conn)); + if (connTyp == NULL) { + NMLOG_WARNING("connection type is NULL"); + continue; + } + + std::string connTypStr = connTyp; + if(connTypStr != "802-11-wireless") + { + NMLOG_DEBUG("%s not a wifi connection", connId); + continue; + } + + if(ssid == connId) + { + knownConnection = g_object_ref(conn); + NMLOG_DEBUG("connection '%s' exists !", ssid.c_str()); + break; + } + } + + if(knownConnection != NULL) + { + NMLOG_INFO("activating known wifi '%s' connection", ssid.c_str()); + m_isSuccess = false; + m_createNewConnection = false; // no need to create new connection + nm_client_activate_connection_async(m_client, NM_CONNECTION(knownConnection), m_wifidevice, specificObjPath, m_cancellable, wifiConnectCb, this); + wait(m_loop); + g_object_unref(knownConnection); + ret = m_isSuccess; + } + else + { + NMLOG_WARNING("'%s' connection not found", ssid.c_str()); + ret = false; + } + + deleteClientConnection(); + return ret; + } + + bool wifiManager::activateKnownConnection(std::string iface, std::string knowConnectionID) + { + const GPtrArray *devConnections = NULL; + NMConnection *knownConnection = NULL; + NMConnection *firstConnection = NULL; + const char* specificObjPath = "/"; + + if(!createClientNewConnection()) + return false; + + NMDevice *nmDevice = nm_client_get_device_by_iface(m_client, iface.c_str());; + if(nmDevice == NULL) { + NMLOG_WARNING("nm_client_get_device_by_iface failed !"); + deleteClientConnection(); + return false; + } + + nm_device_set_autoconnect(nmDevice, true); // set autoconnect true + + devConnections = nm_client_get_connections(m_client); + if(devConnections == NULL || devConnections->len == 0) + { + if(iface == nmUtils::ethIface()) + { + NMLOG_INFO("Creating and activating ethernet connection with autoconnect=true"); + NMConnection *ethConn = createMinimalEthernetConnection(iface); + if(ethConn == NULL) + { + NMLOG_ERROR("Failed to create ethernet connection"); + deleteClientConnection(); + return false; + } + m_isSuccess = false; + m_createNewConnection = true; + nm_client_add_and_activate_connection_async(m_client, ethConn, nmDevice, NULL, m_cancellable, wifiConnectCb, this); + g_object_unref(ethConn); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + else + { + NMLOG_ERROR("No connections found !"); + deleteClientConnection(); + return false; + } + } + + for (guint i = 0; i < devConnections->len; i++) + { + NMConnection *connection = static_cast(g_ptr_array_index(devConnections, i)); + if(connection == NULL) + continue; + + const char *connId = nm_connection_get_id(NM_CONNECTION(connection)); + if (connId == NULL) { + NMLOG_WARNING("connection id is NULL"); + continue; + } + + const char *connTyp = nm_connection_get_connection_type(NM_CONNECTION(connection)); + if (connTyp == NULL) { + NMLOG_WARNING("connection type is NULL"); + continue; + } + + std::string connTypStr = connTyp; + NMLOG_DEBUG("connection id: %s, type: %s", connId != NULL ? connId : "NULL", connTypStr.c_str()); + // Store first connection only if it matches the interface type + if(iface == nmUtils::wlanIface() && connTypStr == "802-11-wireless") { + NMLOG_INFO("wifi conn found : %s", connId); + // if no known ssid given then use first wifi connection from list + // it usefule when bootup there will be no known ssid + if(firstConnection == NULL) + firstConnection = connection; + } + else if(iface == nmUtils::ethIface() && connTypStr == "802-3-ethernet") { + NMLOG_INFO("ethernet conn found : %s", connId); + if(firstConnection == NULL) + firstConnection = connection; + } + + if (connId != NULL && strcmp(connId, knowConnectionID.c_str()) == 0) + { + knownConnection = g_object_ref(connection); + NMLOG_DEBUG("connection '%s' exists !", knowConnectionID.c_str()); + if (knownConnection == NULL) + { + NMLOG_ERROR("knownConnection == NULL smothing went worng"); + deleteClientConnection(); + return false; + } + break; + } + } + + // if no connection found then try activate first connection form connection list + if(knownConnection == NULL && firstConnection != NULL) + { + NMLOG_INFO("No known connection found, using first wifi connection"); + knownConnection = g_object_ref(firstConnection); + knowConnectionID = nm_connection_get_id(NM_CONNECTION(knownConnection)); + } + + m_isSuccess = false; + if (knownConnection != NULL && NM_IS_REMOTE_CONNECTION(knownConnection)) + { + NMLOG_INFO("activating known wifi '%s' connection", knowConnectionID.c_str()); + m_createNewConnection = false; // no need to create new connection + nm_client_activate_connection_async(m_client, NM_CONNECTION(knownConnection), nmDevice, specificObjPath, m_cancellable, wifiConnectCb, this); + wait(m_loop); + } + else + { + NMLOG_WARNING("'%s' connection not found", knowConnectionID.empty() ? iface.c_str() : knowConnectionID.c_str()); + // For ethernet, create minimal connection and activate + if(iface == nmUtils::ethIface()) + { + NMLOG_INFO("Creating and activating ethernet connection with autoconnect=true"); + NMConnection *ethConn = createMinimalEthernetConnection(iface); + if(ethConn == NULL) + { + NMLOG_ERROR("Failed to create ethernet connection"); + m_isSuccess = false; + } + else + { + m_createNewConnection = true; + nm_client_add_and_activate_connection_async(m_client, ethConn, nmDevice, NULL, m_cancellable, wifiConnectCb, this); + g_object_unref(ethConn); + wait(m_loop); + } + } + else + NMLOG_ERROR("'%s' connection not found !", knowConnectionID.c_str()); + } + + if(knownConnection != NULL) + g_object_unref(knownConnection); + + deleteClientConnection(); + return m_isSuccess; + } + + bool wifiManager::wifiConnect(const Exchange::INetworkManager::WiFiConnectTo &ssidInfoParam) + { + NMAccessPoint *AccessPoint = NULL; + const GPtrArray* ApList = NULL; + NMConnection *m_connection = NULL; + const GPtrArray *availableConnections = NULL; + + Exchange::INetworkManager::WiFiSSIDInfo apinfo; + Exchange::INetworkManager::WiFiSSIDInfo connectedApInfo; + + NMLOG_DEBUG("wifi connect ssid: %s, bssid: %s, frequency: %d, security %d, persist %d", ssidInfoParam.ssid.c_str(), ssidInfoParam.bssid.c_str(), ssidInfoParam.frequency, ssidInfoParam.security, ssidInfoParam.persist); + + Exchange::INetworkManager::WiFiConnectTo ssidInfo = ssidInfoParam; + m_isSuccess = false; + if(!createClientNewConnection()) + return false; + + m_wifidevice = getWifiDevice(); + if(m_wifidevice == NULL) + { + deleteClientConnection(); + return false; + } + + if(getConnectedAPInfo(m_wifidevice, connectedApInfo)) + { + if(ssidInfo.ssid == connectedApInfo.ssid) + { + if(ssidInfo.bssid.empty()) + { + NMLOG_INFO("'%s' Already connected !", connectedApInfo.ssid.c_str()); + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, connectedApInfo.ssid); + deleteClientConnection(); + return true; + } + else if (strcasecmp(ssidInfo.bssid.c_str(), connectedApInfo.bssid.c_str()) == 0) + { + NMLOG_INFO("Already connected to the requested SSID '%s' with matching BSSID", ssidInfo.ssid.c_str()); + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, connectedApInfo.ssid); + deleteClientConnection(); + return true; + } + else + { + NMLOG_INFO("SSID '%s' matches but BSSID mismatch (connected: %s, requested: %s), reconnecting...", + ssidInfo.ssid.c_str(), connectedApInfo.bssid.c_str(), ssidInfo.bssid.c_str()); + } + } + else + NMLOG_DEBUG("wifi already connected with %s AP", connectedApInfo.ssid.c_str()); + } + + ApList = nm_device_wifi_get_access_points(NM_DEVICE_WIFI(m_wifidevice)); + if(ApList == NULL) + { + NMLOG_ERROR("Aplist Error !"); + deleteClientConnection(); + return false; + } + + AccessPoint = findMatchingSSID(ApList, ssidInfo); + if(AccessPoint == NULL) + { + NMLOG_WARNING("SSID '%s' not found !", ssidInfo.ssid.c_str()); + // if(_instance != nullptr) + // _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND); + /* ssid not found in scan list so add to known ssid it will do a scanning and connect */ + if(ssidInfo.persist) + { + deleteClientConnection(); + if(addToKnownSSIDs(ssidInfo)) + { + NMLOG_DEBUG("Adding to known ssid '%s' ", ssidInfo.ssid.c_str()); + deleteClientConnection(); + return activateKnownConnection(nmUtils::wlanIface(), ssidInfo.ssid); + } + else + { + deleteClientConnection(); + return false; + } + } + } + else + { + getApInfo(AccessPoint, apinfo); + ssidInfo.security = apinfo.security; + } + + availableConnections = nm_device_get_available_connections(m_wifidevice); + if(availableConnections != NULL) + { + for (guint i = 0; i < availableConnections->len; i++) + { + NMConnection *connection = static_cast(g_ptr_array_index(availableConnections, i)); + const char *connId = nm_connection_get_id(NM_CONNECTION(connection)); + if (connId != NULL && strcmp(connId, ssidInfo.ssid.c_str()) == 0) + { + if (nm_access_point_connection_valid(AccessPoint, NM_CONNECTION(connection))) { + m_connection = g_object_ref(connection); + NMLOG_DEBUG("connection '%s' exists !", ssidInfo.ssid.c_str()); + if (m_connection == NULL) + { + NMLOG_ERROR("m_connection == NULL smothing went worng"); + deleteClientConnection(); + return false; + } + break; + } + else + { + if (NM_IS_REMOTE_CONNECTION(connection)) + { + /* + * libnm reuses the existing connection if new settings match the AP properties; + * remove the old one because now only one connection per SSID is supported. + */ + GError *error = NULL; + NMLOG_WARNING(" '%s' connection existing but properties mismatch; deleting...", ssidInfo.ssid.c_str()); + nm_remote_connection_delete (NM_REMOTE_CONNECTION(connection), NULL, &error); + if (error) { + NMLOG_ERROR("deleting connection failed %s", error->message); + g_error_free(error); + } + else + connection = NULL; + } + } + } + } + } + + if (m_connection != NULL && NM_IS_REMOTE_CONNECTION(m_connection)) + { + const char *apPath = nm_object_get_path(NM_OBJECT(AccessPoint)); + if (!apPath) + { + NMLOG_WARNING("AccessPoint object path is NULL"); + apPath = "/"; + } + + if(m_objectPath) + { + NMLOG_WARNING("Freeing object path"); + g_free(m_objectPath); + m_objectPath = NULL; + } + + m_objectPath = g_strdup(apPath); + + if(!connectionBuilder(ssidInfo, m_connection)) { + NMLOG_ERROR("connection builder failed"); + g_object_unref(m_connection); + m_connection = NULL; + deleteClientConnection(); + return false; + } + if (ssidInfo.persist) + { + NMLOG_INFO("updating connection '%s'", ssidInfo.ssid.c_str()); + // Save to persistent storage and activate + GVariant *connSettings = nm_connection_to_dbus(m_connection, NM_CONNECTION_SERIALIZE_ALL); + nm_remote_connection_update2(NM_REMOTE_CONNECTION(m_connection), + connSettings, + NM_SETTINGS_UPDATE2_FLAG_BLOCK_AUTOCONNECT, // block auto connect becuse manualy activate + NULL, + NULL, + wifiConnectionUpdate, + this); + g_variant_unref(connSettings); + } + else + { + // Don't persist changes, just activate existing connection + // Note: Any changes made by connectionBuilder will be temporary for this session only + NMLOG_INFO("activating existing connection without persisting changes '%s'", ssidInfo.ssid.c_str()); + m_createNewConnection = false; + nm_client_activate_connection_async(m_client, NM_CONNECTION(m_connection), m_wifidevice, m_objectPath, m_cancellable, wifiConnectCb, this); + } + g_object_unref(m_connection); + m_connection = NULL; + } + else + { + NMLOG_INFO("creating new connection '%s' persist=%d", ssidInfo.ssid.c_str(), ssidInfo.persist); + m_connection = nm_simple_connection_new(); + + const char *apPath = nm_object_get_path(NM_OBJECT(AccessPoint)); + if (!apPath) + { + NMLOG_WARNING("AccessPoint object path is NULL"); + apPath = "/"; + } + + if(m_objectPath) + { + NMLOG_WARNING("Freeing object path"); + g_free(m_objectPath); + m_objectPath = NULL; + } + + m_objectPath = g_strdup(apPath); + NMLOG_DEBUG("Setting object path to '%s'", m_objectPath); + if(!connectionBuilder(ssidInfo, m_connection)) + { + NMLOG_ERROR("connection builder failed"); + if(m_connection) + g_object_unref(m_connection); + deleteClientConnection(); + return false; + } + if (ssidInfo.persist) + { + m_createNewConnection = true; + nm_client_add_and_activate_connection_async(m_client, m_connection, m_wifidevice, m_objectPath, m_cancellable, wifiConnectCb, this); + } + else + { + // Create temporary connection - add to memory only, do not save to disk + m_createNewConnection = false; + GVariant *connSettings = nm_connection_to_dbus(m_connection, NM_CONNECTION_SERIALIZE_ALL); + // Use nm_client_add_connection2 without NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK + // This creates an in-memory only connection that won't persist + nm_client_add_connection2(m_client, + connSettings, + NM_SETTINGS_ADD_CONNECTION2_FLAG_IN_MEMORY, + NULL, + TRUE, + m_cancellable, + wifiConnectTempCb, + this); + g_variant_unref(connSettings); + } + if(m_connection) + g_object_unref(m_connection); + } + + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + static void addToKnownSSIDsUpdateCb(GObject *rmObject, GAsyncResult *res, gpointer user_data) + { + NMRemoteConnection *remote_con = NM_REMOTE_CONNECTION(rmObject); + wifiManager *_wifiManager = (static_cast(user_data)); + GVariant *ret = NULL; + GError *error = NULL; + + ret = nm_remote_connection_update2_finish(remote_con, res, &error); + + if (!ret) { + if(error) { + NMLOG_ERROR("Error: %s.", error->message); + g_error_free(error); + } + _wifiManager->m_isSuccess = false; + NMLOG_ERROR("AddToKnownSSIDs failed"); + } + else + { + _wifiManager->m_isSuccess = true; + NMLOG_INFO("AddToKnownSSIDs success"); + } + + if(ret) + g_variant_unref(ret); + + _wifiManager->quit(NULL); + } + + static void addToKnownSSIDsCb(GObject *client, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = (static_cast(user_data)); + if (!nm_client_add_connection2_finish(NM_CLIENT(client), result, NULL, &error)) { + NMLOG_ERROR("AddToKnownSSIDs Failed"); + _wifiManager->m_isSuccess = false; + } + else + { + NMLOG_INFO("AddToKnownSSIDs success"); + _wifiManager->m_isSuccess = true; + } + + if(error) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + NMLOG_DEBUG("Add connection operation was cancelled"); + } else { + NMLOG_ERROR("AddToKnownSSIDs Failed: %s", error->message); + } + _wifiManager->m_isSuccess = false; + g_error_free(error); + } + g_main_loop_quit(_wifiManager->m_loop); + } + + bool wifiManager::addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo &ssidinfo) + { + m_isSuccess = false; + NMConnection *m_connection = NULL; + + if(!createClientNewConnection()) + return false; + + NMDevice *device = getWifiDevice(); + if(device == NULL) + { + deleteClientConnection(); + return false; + } + + const GPtrArray *availableConnections = nm_device_get_available_connections(device); + if(availableConnections != NULL) + { + for (guint i = 0; i < availableConnections->len; i++) + { + NMConnection *connection = static_cast(g_ptr_array_index(availableConnections, i)); + const char *connId = nm_connection_get_id(NM_CONNECTION(connection)); + if (connId != NULL && strcmp(connId, ssidinfo.ssid.c_str()) == 0) + { + m_connection = g_object_ref(connection); + } + } + } + else + NMLOG_WARNING("No known available connections found !"); + + if (m_connection && NM_IS_REMOTE_CONNECTION(m_connection)) + { + if(!connectionBuilder(ssidinfo, m_connection)) + { + NMLOG_ERROR("connection builder failed"); + g_object_unref(m_connection); + m_connection = NULL; + deleteClientConnection(); + return false; + } + NMLOG_DEBUG("update exsisting connection '%s' ", ssidinfo.ssid.c_str()); + GVariant *connSettings = nm_connection_to_dbus(m_connection, NM_CONNECTION_SERIALIZE_ALL); + nm_remote_connection_update2(NM_REMOTE_CONNECTION(m_connection), + connSettings, + NM_SETTINGS_UPDATE2_FLAG_TO_DISK, + NULL, + NULL, + addToKnownSSIDsUpdateCb, + this); + g_variant_unref(connSettings); + g_object_unref(m_connection); + m_connection = NULL; + } + else + { + NMLOG_DEBUG("creating new connection '%s' ", ssidinfo.ssid.c_str()); + m_connection = nm_simple_connection_new(); + if(!connectionBuilder(ssidinfo, m_connection)) + { + NMLOG_ERROR("connection builder failed"); + g_object_unref(m_connection); + m_connection = NULL; + deleteClientConnection(); + return false; + } + m_createNewConnection = true; + GVariant *connSettings = nm_connection_to_dbus(m_connection, NM_CONNECTION_SERIALIZE_ALL); + nm_client_add_connection2(m_client, + connSettings, + NM_SETTINGS_ADD_CONNECTION2_FLAG_TO_DISK, + NULL, TRUE, m_cancellable, + addToKnownSSIDsCb, this); + g_variant_unref(connSettings); + g_object_unref(m_connection); + m_connection = NULL; + } + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + bool wifiManager::removeKnownSSID(const string& ssid) + { + NMConnection *m_connection = NULL; + bool ssidSpecified = false; + bool connectionFound = false; + + if(!createClientNewConnection()) + return false; + + if(!ssid.empty()) + ssidSpecified = true; + else + NMLOG_WARNING("ssid is not specified, Deleting all availble wifi connection !"); + + const GPtrArray *allconnections = nm_client_get_connections(m_client); + if(allconnections == NULL) + { + NMLOG_ERROR("nm connections list null "); + deleteClientConnection(); + return false; + } + for (guint i = 0; i < allconnections->len; i++) + { + GError *error = NULL; + NMConnection *connection = static_cast(g_ptr_array_index(allconnections, i)); + if(connection == NULL) + { + NMLOG_WARNING("ssid connection null !"); + continue; + } + if (!NM_IS_SETTING_WIRELESS(nm_connection_get_setting_wireless(connection))) + continue; // if not wireless connection skipt + const char *connId = nm_connection_get_id(NM_CONNECTION(connection)); + if(connId == NULL) + { + NMLOG_WARNING("ssid connection id null !"); + continue; + } + + NMLOG_DEBUG("wireless connection '%s'", connId); + if (ssidSpecified && strcmp(connId, ssid.c_str()) != 0) + continue; + + m_connection = g_object_ref(connection); + if (NM_IS_REMOTE_CONNECTION(connection)) + { + nm_remote_connection_delete(NM_REMOTE_CONNECTION(connection), NULL, &error); + if(error) { + NMLOG_ERROR("deleting connection failed %s", error->message); + g_error_free(error); + if(m_connection) + { + g_object_unref(m_connection); + m_connection = NULL; + } + break; // error break the loop and return false + } + else + NMLOG_INFO("delete '%s' connection ...", connId); + connectionFound = true; + } + + if(m_connection) + { + g_object_unref(m_connection); + m_connection = NULL; + } + } + + if(!connectionFound) + { + if(ssidSpecified) + NMLOG_WARNING("'%s' no such connection profile", ssid.c_str()); + else + NMLOG_WARNING("No wifi connection profiles found !!"); + } + + deleteClientConnection(); +#ifdef ENABLE_MIGRATION_MFRMGR_SUPPORT + NetworkManagerMfrManager* mfrManager = NetworkManagerMfrManager::getInstance(); + if (mfrManager != nullptr) + { + mfrManager->clearWiFiSettingsFromMfr(); + } +#endif + // ssid is specified and connection is not found return false + // all other case return true, even if no wificonnection is found + return((ssidSpecified && !connectionFound)? false : true); + } + + bool wifiManager::getKnownSSIDs(std::list& ssids) + { + std::string ssidPrint{}; + + if(!createClientNewConnection()) + return false; + + const GPtrArray *connections = nm_client_get_connections(m_client); + if(connections == nullptr) + { + NMLOG_ERROR("nm connections list null "); + deleteClientConnection(); + return false; + } + for (guint i = 0; i < connections->len; i++) + { + NMConnection *connection = NM_CONNECTION(connections->pdata[i]); + if(NM_IS_SETTING_WIRELESS(nm_connection_get_setting_wireless(connection))) + { + GBytes *ssidBytes = nm_setting_wireless_get_ssid(nm_connection_get_setting_wireless(connection)); + if (ssidBytes) + { + char* ssidStr = nm_utils_ssid_to_utf8((const guint8*)g_bytes_get_data(ssidBytes, NULL), g_bytes_get_size(ssidBytes)); + if(ssidStr != nullptr) + { + ssids.push_back(string(ssidStr)); + ssidPrint += ssidStr; + ssidPrint += ", "; + free(ssidStr); + } + else + { + NMLOG_ERROR("Invalid ssid length Error"); + continue; + } + } + else + { + /* hidden ssid */ + NMLOG_WARNING("wifi connection list have hidden ssid also !"); + } + } + } + if (!ssids.empty()) + { + NMLOG_INFO("known wifi connections are %s", ssidPrint.c_str()); + deleteClientConnection(); + return true; + } + + deleteClientConnection(); + return false; + } + + static void wifiScanCb(GObject *object, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = (static_cast(user_data)); + if(nm_device_wifi_request_scan_finish(NM_DEVICE_WIFI(object), result, &error)) { + _wifiManager->m_isSuccess = true; + NMLOG_DEBUG("wifi scanning request success .."); + } + else + { + NMLOG_ERROR("Scanning Failed"); + _wifiManager->m_isSuccess = false; + } + if (error) { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { + NMLOG_DEBUG("Scan operation was cancelled"); + } else { + NMLOG_ERROR("Scanning Failed Error: %s.", error->message); + } + g_error_free(error); + } + + g_main_loop_quit(_wifiManager->m_loop); + } + + bool wifiManager::wifiScanRequest(const std::vector& ssidsToFilter) + { + if(!createClientNewConnection()) + return false; + NMDeviceWifi *wifiDevice = NM_DEVICE_WIFI(getWifiDevice()); + if(wifiDevice == NULL) { + NMLOG_FATAL("NMDeviceWifi * NULL !"); + deleteClientConnection(); + return false; + } + m_isSuccess = false; + if(!ssidsToFilter.empty()) + { + NMLOG_INFO("Starting wifi scanning for %d SSIDs:",static_cast(ssidsToFilter.size())); + GVariantBuilder nm_variant, nm_array_variant; + GVariant *options; + g_variant_builder_init(&nm_variant, G_VARIANT_TYPE_VARDICT); + g_variant_builder_init(&nm_array_variant, G_VARIANT_TYPE("aay")); + for (const auto& ssid : ssidsToFilter) { + g_variant_builder_add(&nm_array_variant, "@ay", + g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (const guint8 *) ssid.c_str(), ssid.length(), 1) + ); + } + g_variant_builder_add(&nm_variant, "{sv}", "ssids", g_variant_builder_end(&nm_array_variant)); + options = g_variant_builder_end(&nm_variant); + nm_device_wifi_request_scan_options_async(wifiDevice, options, m_cancellable, wifiScanCb, this); + g_variant_unref(options); // Unreference the GVariant after passing it to the async function + } + else { + NMLOG_INFO("Starting normal wifi scanning .."); + nm_device_wifi_request_scan_async(wifiDevice, m_cancellable, wifiScanCb, this); + } + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + bool wifiManager::isWifiScannedRecently(int timelimitInSec) + { + if (!createClientNewConnection()) + return false; + + NMDeviceWifi *wifiDevice = NM_DEVICE_WIFI(getWifiDevice()); + if (wifiDevice == NULL) { + NMLOG_ERROR("Invalid Wi-Fi device."); + deleteClientConnection(); + return false; + } + + gint64 last_scan_time = nm_device_wifi_get_last_scan(wifiDevice); + if (last_scan_time <= 0) { + NMLOG_INFO("No scan has been performed yet"); + deleteClientConnection(); + return false; + } + + gint64 current_time_in_msec = nm_utils_get_timestamp_msec(); + gint64 time_difference_in_seconds = (current_time_in_msec - last_scan_time) / 1000; + if (time_difference_in_seconds <= timelimitInSec) { + deleteClientConnection(); + return true; + } + deleteClientConnection(); + return false; + } + + static void wpsWifiConnectCb(GObject *client, GAsyncResult *result, gpointer user_data) + { + GError *error = NULL; + GMainLoop *loop = static_cast(user_data); + NMActiveConnection *activeConnection = nm_client_add_and_activate_connection_finish(NM_CLIENT(client), result, &error); + + if (error) { + NMLOG_ERROR("Failed to add/activate new connection: %s", error->message); + g_error_free(error); + } + else + NMLOG_INFO("WPS connection added/activated successfully"); + if(activeConnection) + g_object_unref(activeConnection); + g_main_loop_quit(loop); + } + + static gboolean wpsGmainLoopTimoutCB(gpointer user_data) + { + GMainLoop *loop = static_cast(user_data); + NMLOG_WARNING("GmainLoop ERROR_TIMEDOUT"); + g_main_loop_quit(loop); + return true; + } + + static bool removeSameNmConnection(NMDevice* wifidevice, std::string ssid) + { + const GPtrArray *deviceConns = NULL; + + deviceConns = nm_device_get_available_connections(wifidevice); + if(deviceConns != NULL) + { + for (guint i = 0; i < deviceConns->len; i++) + { + NMConnection* connection = static_cast(g_ptr_array_index(deviceConns, i)); + if(connection == NULL) + continue; + const char* connId = nm_connection_get_id(NM_CONNECTION(connection)); + if (connId != NULL && strcmp(connId, ssid.c_str()) == 0) + { + GError* error = NULL; + NMLOG_WARNING(" '%s' wps connection existing deleting...", ssid.c_str()); + nm_remote_connection_delete (NM_REMOTE_CONNECTION(connection), NULL, &error); + if(error) { + NMLOG_ERROR("deleting connection failed %s", error->message); + g_error_free(error); + return false; + } + } + } + } + + return true; + } + + static bool findWpsPbcSSID(const GPtrArray* ApList, Exchange::INetworkManager::WiFiSSIDInfo& wpsApInfo, const char** apObjPath) + { + for (guint i = 0; i < ApList->len; i++) + { + NMAccessPoint *Ap = static_cast(g_ptr_array_index(ApList, i)); + guint32 flags = nm_access_point_get_flags(Ap); + if (flags & NM_802_11_AP_FLAGS_WPS_PBC) + { + getApInfo(Ap, wpsApInfo, true); + *apObjPath = nm_object_get_path(NM_OBJECT(Ap)); + if(*apObjPath == NULL) + return false; + NMLOG_INFO("WPS PBC AP found! bssid: %s, Ap Path: %s", wpsApInfo.bssid.c_str(), *apObjPath); + return true; + } + } + + NMLOG_INFO("WPS PBC Not found !!!"); + return false; + } + + void wifiManager::wpsProcess() + { + wpsProcessRun = true; + GError *error = NULL; + const GPtrArray* ApList = NULL; + std::string wpsApSsid{}; + GMainContext *wpsContext = NULL; + const char *wpsApPath = NULL; + GMainLoop *loop = NULL; + NMClient* client = NULL; + Exchange::INetworkManager::WiFiConnectTo wifiConnectInfo{}; + Exchange::INetworkManager::WiFiSSIDInfo wpsApInfo{}; + + bool wpsComplete= false; + bool wpsActionTriggerd = false; + + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTING, ""); + + for(int retry = 0; retry < WPS_RETRY_COUNT; retry++) + { + sleep(WPS_RETRY_WAIT_IN_MS); + if(wpsProcessRun.load() == false) // stop wps process if reuested + { + NMLOG_INFO("stop wps process reuested"); + break; + } + + wpsContext = g_main_context_new(); + if(wpsContext == NULL) + { + NMLOG_ERROR("wpsContext create failed !!"); + break; + } + + if(!g_main_context_acquire(wpsContext)) + { + NMLOG_ERROR("acquire wpsContext failed !!"); + g_main_context_unref(wpsContext); + wpsContext = NULL; + break; + } + + g_main_context_push_thread_default(wpsContext); + + client = nm_client_new(NULL, &error); + if (!client) + { + if(error != NULL) { + NMLOG_ERROR("Could not connect to NetworkManager: %s.", error->message); + g_error_free(error); + error = NULL; + } + else + NMLOG_ERROR("NetworkManager client create failed"); + g_main_context_pop_thread_default(wpsContext); + g_main_context_release(wpsContext); + g_main_context_unref(wpsContext); + wpsContext = NULL; + break; + } + + NMDevice* wifidevice = nm_client_get_device_by_iface(client, nmUtils::wlanIface()); + if(wifidevice == NULL) + { + NMLOG_ERROR("Failed to get device list."); + g_main_context_pop_thread_default(wpsContext); + g_main_context_release(wpsContext); + g_main_context_unref(wpsContext); + wpsContext = NULL; + break; + } + + if(wpsActionTriggerd) + { + /* if wps action started we need to check the status of wifi device and post event based on that */ + NMDeviceState state = nm_device_get_state(wifidevice); + if(state <= NM_DEVICE_STATE_DISCONNECTED) + { + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, wifiConnectInfo.ssid); + // TODO post correct error code insted of WIFI_STATE_SSID_NOT_FOUND + // sedning WIFI_STATE_SSID_NOT_FOUND to avoid UI stuck issue + break; + } + else if(state > NM_DEVICE_STATE_NEED_AUTH) + { + NMLOG_INFO("WPS process completed successfully"); + wpsComplete = true; + // TODO stop Secret agenet ? + break; + } + + NMLOG_INFO("WPS process not completed yet, state: %d", state); + if(retry >= WPS_RETRY_COUNT - 1) // 3 times retry to check the status + { + // wifi state stuck in betwen disconnected and connected + NMLOG_ERROR("WPS process failed"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, wifiConnectInfo.ssid); + // TODO post correct error code insted of WIFI_STATE_SSID_NOT_FOUND + // sedning WIFI_STATE_SSID_NOT_FOUND to avoid UI stuck issue + } + } + + ApList = nm_device_wifi_get_access_points(NM_DEVICE_WIFI(wifidevice)); + if(ApList == NULL) + { + NMLOG_ERROR("Aplist Error !"); + g_main_context_pop_thread_default(wpsContext); + g_main_context_release(wpsContext); + g_main_context_unref(wpsContext); + wpsContext = NULL; + break; + } + + if(!wpsActionTriggerd && findWpsPbcSSID(ApList, wpsApInfo, &wpsApPath) && wpsApPath != NULL) + { + Exchange::INetworkManager::WiFiSSIDInfo activeApInfo{}; + NMAccessPoint *activeAP = nm_device_wifi_get_active_access_point(NM_DEVICE_WIFI(wifidevice)); + if(activeAP != NULL) + { + NMLOG_WARNING("active access point found during wps operation !"); + getApInfo(activeAP, activeApInfo); + if(activeApInfo.ssid == wpsApInfo.ssid) + { + NMLOG_INFO("WPS process stoped connected to same '%s' wps ap ", wpsApInfo.ssid.c_str()); + wpsComplete = true; + //TODO Post SSID connected event ? + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, wpsApInfo.ssid); + + } + else + { + NMDeviceState state = nm_device_get_state(wifidevice); + if(state > NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_INFO("stopping the ongoing Wi-Fi connection"); + // some other ssid connected or connecting; wps need a disconnected wifi state + nm_device_disconnect(wifidevice, NULL, &error); + if (error) { + NMLOG_ERROR("disconnect connection failed %s", error->message); + g_error_free(error); + } + sleep(3); // wait for 3 sec to complete disconnect process + } + } + } + + if(wpsComplete) + { + break; + } + + wifiConnectInfo.ssid = wpsApInfo.ssid; + wifiConnectInfo.security = wpsApInfo.security; + NMConnection* connection = NULL; + /* if same connection name exsist we remove and add new one */ + removeSameNmConnection(wifidevice, wifiConnectInfo.ssid); + NMLOG_DEBUG("creating new connection '%s' ", wifiConnectInfo.ssid.c_str()); + connection = nm_simple_connection_new(); + if(!connectionBuilder(wifiConnectInfo, connection, true)) + { + NMLOG_ERROR("wps connection builder failed"); + break; + } + + loop = g_main_loop_new(wpsContext, FALSE); + if(loop == NULL) + { + NMLOG_ERROR("g_main_loop_new failed"); + break; + } + + nm_client_add_and_activate_connection_async(client, connection, wifidevice, wpsApPath, NULL, wpsWifiConnectCb, loop); + GSource *source = g_timeout_source_new(10000); // 10000ms interval + if(source != nullptr) { + g_source_set_callback(source, (GSourceFunc)wpsGmainLoopTimoutCB, loop, NULL); + g_source_attach(source, wpsContext); + g_main_loop_run(loop); + if(g_source_is_destroyed(source)) { + NMLOG_WARNING("Source has been destroyed"); + } + else { + g_source_destroy(source); + } + g_source_unref(source); + } + if(loop != NULL) { + g_main_loop_unref(loop); + loop = NULL; + } + if(connection != NULL) { + g_object_unref(connection); + connection = NULL; + } + + wpsActionTriggerd = true; + retry = WPS_RETRY_COUNT - 3; // expecting wps process will be completed in 30 sec(3 10sec retry) (Ex: retry = 10-3) + } + else if(!wpsActionTriggerd) + { + /* if wps action not triggerd do a scanning request */ + nm_device_wifi_request_scan(NM_DEVICE_WIFI(wifidevice), NULL, &error); + if(error) { + NMLOG_WARNING("WiFi scan request failed: %s", error->message); + g_error_free(error); + error = NULL; + } + } + + g_main_context_pop_thread_default(wpsContext); + g_main_context_release(wpsContext); + g_main_context_unref(wpsContext); + wpsContext = NULL; + if(client != NULL) { + g_object_unref(client); + client = NULL; + } + + if(wpsComplete || wpsProcessRun.load() == false) // stop wps process if user requested + break; + } + + if(wpsApPath == NULL) + { + NMLOG_WARNING("WPS AP not found"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, ""); + } + else if(!wpsComplete) + { + NMLOG_INFO("WPS process Error"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED, wifiConnectInfo.ssid); + } + + if(wpsContext != NULL) + { + g_main_context_pop_thread_default(wpsContext); + g_main_context_release(wpsContext); + g_main_context_unref(wpsContext); + } + + if(client != NULL) { + g_object_unref(client); + client = NULL; + } + + // wps success ! wifi connect event will be send by wifi event monitor + m_secretAgent.UnregisterAgent(); + NMLOG_INFO("WPS process thread exist"); + wpsProcessRun = false; + } + + bool wifiManager::startWPS() + { + NMLOG_DEBUG("Start WPS %s", __FUNCTION__); + if(wpsProcessRun.load()) + { + NMLOG_WARNING("wps process WPS already running"); + return true; + } + + m_secretAgent.RegisterAgent(); + std::thread wpsthread(&wifiManager::wpsProcess, this); + wpsthread.detach(); + return true; + } + + bool wifiManager::stopWPS() { + NMLOG_DEBUG("Stop WPS %s", __FUNCTION__); + wpsProcessRun = false; + return m_secretAgent.UnregisterAgent(); + } + + static void deviceManagedCb(GObject *object, GAsyncResult *result, gpointer user_data) + { + wifiManager *_wifiManager = static_cast(user_data); + GError *error = nullptr; + + if (!nm_client_dbus_set_property_finish(NM_CLIENT(object), result, &error)) + { + NMLOG_ERROR("Failed to set Managed property"); + if (error != nullptr) + { + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + NMLOG_DEBUG("Device Manage operation was cancelled !"); + } + else + { + NMLOG_ERROR("Failed Error msg: %s", error->message); + } + g_error_free(error); + _wifiManager->m_isSuccess = false; + } + } + else + { + NMLOG_DEBUG("Successfully set Managed property."); + _wifiManager->m_isSuccess = true; + } + + _wifiManager->quit(nullptr); + } + + bool wifiManager::setInterfaceState(std::string interface, bool enabled) + { + m_isSuccess = false; + NMDevice *device = nullptr; + + if(interface.empty() || (interface != nmUtils::ethIface() && interface != nmUtils::wlanIface())) + { + NMLOG_ERROR("Invalid interface name: %s", interface.c_str()); + return false; + } + + if (!createClientNewConnection()) + return false; + + GPtrArray *devices = const_cast(nm_client_get_devices(m_client)); + if (devices == nullptr) { + NMLOG_ERROR("Failed to get device list."); + deleteClientConnection(); + return m_isSuccess; + } + + for (guint j = 0; j < devices->len; j++) { + device = NM_DEVICE(devices->pdata[j]); + const char *ifaceStr = nm_device_get_iface(device); + if (ifaceStr == nullptr) + continue; + if (interface == ifaceStr) { + // NMLOG_DEBUG("Device found: %s", interface.c_str()); + break; + } else { + device = nullptr; + } + } + + if (device == nullptr) + { + NMLOG_ERROR("Device not found: %s", interface.c_str()); + deleteClientConnection(); + return false; + } + + NMDeviceState deviceState = nm_device_get_state(device); + + if (enabled) { + NMLOG_DEBUG("Enabling interface..."); + if (deviceState >= NM_DEVICE_STATE_UNAVAILABLE) // already enabled + { + deleteClientConnection(); + return true; + } + } else { + NMLOG_DEBUG("Disabling interface..."); + if (deviceState < NM_DEVICE_STATE_UNAVAILABLE) // already disabled + { + deleteClientConnection(); + return true; + } + else if (deviceState > NM_DEVICE_STATE_DISCONNECTED) { + NMLOG_DEBUG("Disconnecting device..."); + // Disconnect the device before setting it to unmanaged. + // This ensures that NetworkManager cleanly removes any IP addresses, routes, + // and DNS configuration associated with the interface. Setting an interface + // to unmanaged without disconnecting first may leave residual configuration + // that can cause networking issues. + nm_device_disconnect_async(device, nullptr, disconnectCb, this); + wait(m_loop); + + // Identify the correct context + GMainContext *device_context = g_main_loop_get_context(m_loop); + int retry = 24; // 12 seconds + NMDeviceState oldDevState = NM_DEVICE_STATE_UNKNOWN; + while (retry-- > 0) { + // If there are multiple messages backed up, process a bounded number + // of pending iterations so this path cannot stall indefinitely if the + // context keeps receiving new work. + while (g_main_context_iteration(device_context, FALSE)); + + // Fetch the updated state + deviceState = nm_device_get_state(device); + if(oldDevState != deviceState) { + oldDevState = deviceState; + NMLOG_WARNING("Device state: %d Retry: %d", deviceState, retry); + } + if (deviceState <= NM_DEVICE_STATE_DISCONNECTED) { + break; + } + g_usleep(500 * 1000); // give some time to NM to process the request + } + } + } + + if(deviceState > NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_WARNING("Device not fully disconnected (state: %d), setting to unmanaged state", deviceState); + } + // Set the "Managed" property to enable/disable the device + m_isSuccess = false; + const char *objectPath = nm_object_get_path(NM_OBJECT(device)); + GVariant *value = g_variant_new_boolean(enabled); + + nm_client_dbus_set_property( m_client, objectPath, NM_DBUS_INTERFACE_DEVICE,"Managed", + value, -1, nullptr, deviceManagedCb, this); + wait(m_loop); + deleteClientConnection(); + + if(m_isSuccess) + { + // set persistent marker file for specific interface + if(interface == nmUtils::ethIface()) + nmUtils::setMarkerFile(EthernetDisableMarker, enabled); + else // wifi + nmUtils::setMarkerFile(WiFiDisableMarker, enabled); + } + + return m_isSuccess; + } + + static void onActivateComplete(GObject *source_object, GAsyncResult *res, gpointer user_data) + { + GError *error = NULL; + wifiManager *_wifiManager = static_cast(user_data); + NMLOG_DEBUG("activate connection completeing..."); + // Check if the operation was successful + if (!nm_client_activate_connection_finish(NM_CLIENT(source_object), res, &error)) { + NMLOG_ERROR("Activating connection failed"); + if(error != NULL) + { + NMLOG_ERROR("Activating connection failed: %s", error->message); + g_error_free(error); + } + _wifiManager->m_isSuccess = false; + } else { + NMLOG_DEBUG("Activating connection successful"); + _wifiManager->m_isSuccess = true; + } + _wifiManager->quit(nullptr); + } + + bool static checkAutoConnectEnabledInIPv4Conn(NMConnection *connection) + { + if(connection == NULL) + return false; + NMSettingIPConfig *ipConfig = nm_connection_get_setting_ip4_config(connection); + if(ipConfig) + { + const char* ipConfMethod = nm_setting_ip_config_get_method (ipConfig); + if(ipConfMethod != NULL && g_strcmp0(ipConfMethod, "auto") == 0) + return true; + else + NMLOG_WARNING("ip configuration: %s", ipConfMethod != NULL? ipConfMethod: "null"); + } + + return false; + } + + bool wifiManager::setIpSettings(const string interface, const Exchange::INetworkManager::IPAddress &address) + { + m_isSuccess = false; + NMConnection *connection = NULL; + NMRemoteConnection *remoteConn = NULL; + NMActiveConnection* activeConnection = NULL; + NMSetting *setting = NULL; + NMDevice *device = NULL; + const char *specObject = NULL; + + if(interface.empty() || (interface != nmUtils::ethIface() && interface != nmUtils::wlanIface())) + { + NMLOG_ERROR("Invalid interface name: %s", interface.c_str()); + return false; + } + + if (!createClientNewConnection()) + return false; + + device = nm_client_get_device_by_iface(m_client, interface.c_str()); + if(device == NULL) + { + deleteClientConnection(); + return false; + } + + if(interface == nmUtils::ethIface()) + { + NMSettingConnection *settings = NULL; + + const GPtrArray *connections = nm_device_get_available_connections(device); + if (connections == NULL || connections->len == 0) + { + NMLOG_WARNING("no connections availble to edit "); + deleteClientConnection(); + return true; + } + + for (guint i = 0; i < connections->len; i++) + { + NMConnection *tmpConn = NM_CONNECTION(connections->pdata[i]); + if(tmpConn == nullptr) + continue; + settings = nm_connection_get_setting_connection(tmpConn); + if(settings == NULL) + continue; + if (g_strcmp0(nm_setting_connection_get_interface_name(settings), interface.c_str()) == 0) { + connection = tmpConn; + + if (NM_IS_REMOTE_CONNECTION(connection)) { + remoteConn = NM_REMOTE_CONNECTION(connection); + } else { + NMLOG_ERROR("The connection is not a remote connection."); + deleteClientConnection(); + return false; /* connection and remoteconnection should match */ + } + + NMLOG_DEBUG("ethernet connection found "); + break; + } + } + } + else if(interface == nmUtils::wlanIface()) + { + /* for wifi we need active connection to modify becuse of multiple wifi connection */ + activeConnection = nm_device_get_active_connection(device); + if(activeConnection == NULL) + { + NMLOG_WARNING("no active connection for wifi"); + deleteClientConnection(); + return false; + } + remoteConn = nm_active_connection_get_connection(activeConnection); + if(remoteConn == NULL) { + NMLOG_WARNING("no remote connection for wifi"); + deleteClientConnection(); + return false; + } + connection = NM_CONNECTION(remoteConn); + } + + if(connection == nullptr) + { + NMLOG_WARNING("not a single connection availble for %s", interface.c_str()); + deleteClientConnection(); + return true; + } + + if(address.autoconfig) + { + NMSettingIP4Config *sIp4 = nullptr; + NMSettingIP6Config *sIp6 = nullptr; + if(address.ipversion.empty() || nmUtils::caseInsensitiveCompare("IPv4", address.ipversion)) + { + if(checkAutoConnectEnabledInIPv4Conn(connection)) // already auto connect true connection + { + NMLOG_INFO("Setting IPv4 auto connect enabled"); + deleteClientConnection(); + return true; // no need to modify + } + sIp4 = (NMSettingIP4Config *)nm_setting_ip4_config_new(); + g_object_set(G_OBJECT(sIp4), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting(connection, NM_SETTING(sIp4)); + } + else + { + sIp6 = (NMSettingIP6Config *)nm_setting_ip6_config_new(); + g_object_set(G_OBJECT(sIp6), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO, NULL); + nm_connection_add_setting(connection, NM_SETTING(sIp6)); + } + } + else + { + if (nmUtils::caseInsensitiveCompare("IPv4", address.ipversion)) + { + NMSettingIPConfig *ip4Config = nm_connection_get_setting_ip4_config(connection); + if (ip4Config == nullptr) + { + ip4Config = (NMSettingIPConfig *)nm_setting_ip4_config_new(); + } + NMIPAddress *ipAddress; + setting = nm_connection_get_setting_by_name(connection, "ipv4"); + ipAddress = nm_ip_address_new(AF_INET, address.ipaddress.c_str(), address.prefix, NULL); + nm_setting_ip_config_clear_addresses(ip4Config); + nm_setting_ip_config_add_address(NM_SETTING_IP_CONFIG(setting), ipAddress); + nm_setting_ip_config_clear_dns(ip4Config); + if(!address.primarydns.empty()) + nm_setting_ip_config_add_dns(ip4Config, address.primarydns.c_str()); + if(!address.secondarydns.empty()) + nm_setting_ip_config_add_dns(ip4Config, address.secondarydns.c_str()); + + g_object_set(G_OBJECT(ip4Config),NM_SETTING_IP_CONFIG_GATEWAY, address.gateway.c_str(), NULL); + g_object_set(G_OBJECT(ip4Config),NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE, NULL); + g_object_set(G_OBJECT(ip4Config), NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL); + } + else + { + //FIXME : Add IPv6 support here + NMLOG_WARNING("Setting IPv6 is not supported at this point in time. This is just a place holder"); + } + } + + // TODO fix depricated api + GError *error = NULL; + + if (!nm_remote_connection_commit_changes(remoteConn, FALSE, NULL, &error)) { + if (error) { + NMLOG_ERROR("Failed to commit changes to the remote connection: %s", error->message); + g_error_free(error); // Free the GError object after handling it + } else { + NMLOG_ERROR("Failed to commit changes to the remote connection (unknown error)."); + } + deleteClientConnection(); + return false; + } + + if (activeConnection != NULL) { + specObject = nm_object_get_path(NM_OBJECT(activeConnection)); + GError *deactivate_error = NULL; + // TODO fix depricated api + if (!nm_client_deactivate_connection(m_client, activeConnection, NULL, &deactivate_error)) { + NMLOG_ERROR("Failed to deactivate connection: %s", deactivate_error->message); + g_clear_error(&deactivate_error); + deleteClientConnection(); + return false; + } + } + + nm_client_activate_connection_async(m_client, connection, device, specObject, NULL, onActivateComplete, this); + wait(m_loop); + deleteClientConnection(); + return m_isSuccess; + } + + bool wifiManager::setPrimaryInterface(const string interface) + { + uint32_t rc = Core::ERROR_RPC_CALL_FAILED; + GError *error = NULL; + std::string otherInterface; + std::string wifiname = nmUtils::wlanIface(), ethname = nmUtils::ethIface(); + const char *specObject = NULL; + if (!createClientNewConnection()) return false; + if(interface.empty() || (wifiname != interface && ethname != interface)) + { NMLOG_FATAL("interface is not valied %s", interface.c_str()!=nullptr? interface.c_str():"empty"); + deleteClientConnection(); + return false; } + otherInterface = (interface == wifiname)?ethname:wifiname; + + // Retrieve the active connections by interface name + const GPtrArray* activeConnections = nm_client_get_active_connections(m_client); + if (!activeConnections) { + NMLOG_ERROR("Failed to retrieve active connections"); + deleteClientConnection(); + return false; + } + for (guint i = 0; i < activeConnections->len; i++) + { + gpointer elem = g_ptr_array_index(activeConnections, i); + if (!elem || !NM_IS_ACTIVE_CONNECTION(elem)) { + NMLOG_WARNING("Invalid or null active connection at index %u", i); + continue; + } + NMActiveConnection *activeConnection = NM_ACTIVE_CONNECTION(elem); + const GPtrArray* activeDevices = nm_active_connection_get_devices(activeConnection); + for (guint j = 0; j < activeDevices->len; j++) + { + NMDevice* device = NM_DEVICE(g_ptr_array_index(activeDevices, j)); + const char *iface = nm_device_get_iface(device); + if (g_strcmp0(iface, interface.c_str()) == 0 || g_strcmp0(iface, otherInterface.c_str()) == 0) { + NMRemoteConnection* connection = NM_REMOTE_CONNECTION(nm_active_connection_get_connection(activeConnection)); + NMSettingIPConfig *s_ip4 = nm_connection_get_setting_ip4_config(NM_CONNECTION(connection)); + NMSettingIPConfig *s_ip6 = nm_connection_get_setting_ip6_config(NM_CONNECTION(connection)); + if (!s_ip4 || !s_ip6) + { + NMLOG_ERROR("Failed to retrieve IP settings for connection on interface: %s\n", iface); + continue; + } + + int metric_value = (g_strcmp0(iface, interface.c_str()) == 0) ? ROUTE_METRIC_PRIORITY_HIGH:ROUTE_METRIC_PRIORITY_LOW; + g_object_set(G_OBJECT(s_ip4),NM_SETTING_IP_CONFIG_ROUTE_METRIC, metric_value, NULL); + g_object_set(G_OBJECT(s_ip6),NM_SETTING_IP_CONFIG_ROUTE_METRIC, metric_value, NULL); + // TODO fix depricated api + if (!nm_remote_connection_commit_changes(connection, FALSE, NULL, &error)) + { + if (error) { + NMLOG_ERROR("Failed to commit changes to the remote connection: %s", error->message); + g_error_free(error); // Free the GError object after handling it + } + else { + NMLOG_ERROR("Failed to commit changes to the remote connection (unknown error)."); } + deleteClientConnection(); + return false; + } + if (activeConnection != NULL) + { + specObject = nm_object_get_path(NM_OBJECT(activeConnection)); + GError *deactivate_error = NULL; + // TODO fix depricated api + if (!nm_client_deactivate_connection(m_client, activeConnection, NULL, &deactivate_error)) + { + NMLOG_ERROR("Failed to deactivate connection: %s", deactivate_error->message); + g_clear_error(&deactivate_error); + deleteClientConnection(); + return false; + } + } + nm_client_activate_connection_async(m_client, NM_CONNECTION(connection), device, specObject, NULL, onActivateComplete, this); + } + } + } + wait(m_loop); + deleteClientConnection(); + return rc; + } + } // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/gnome/NetworkManagerGnomeWIFI.h b/plugin/gnome/NetworkManagerGnomeWIFI.h new file mode 100644 index 000000000..cfc741fb2 --- /dev/null +++ b/plugin/gnome/NetworkManagerGnomeWIFI.h @@ -0,0 +1,119 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include +#include +#include "NetworkManagerLogger.h" +#include "INetworkManager.h" +#include "NetworkManagerSecretAgent.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#define WPS_RETRY_WAIT_IN_MS 10 // 10 sec +#define WPS_RETRY_COUNT 10 +#define WPS_PROCESS_WAIT_IN_MS 5 + +namespace WPEFramework +{ + namespace Plugin + { + class wifiManager + { + public: + static wifiManager* getInstance() + { + static wifiManager instance; + return &instance; + } + + bool getWifiState(Exchange::INetworkManager::WiFiState& state); + bool wifiDisconnect(); + bool ethernetDeactivate(); + bool reacquireDhcpLease(const std::string& iface); + bool activateKnownConnection(std::string iface, std::string knowConnectionID=""); + bool wifiConnectedSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo); + bool wifiConnect(const Exchange::INetworkManager::WiFiConnectTo &ssidInfo); + bool wifiScanRequest(const std::vector& ssidsToFilter = {}); + bool isWifiScannedRecently(int timelimitInSec = 5); // default 5 sec as shotest scanning interval + bool getKnownSSIDs(std::list& ssids); + bool addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo &ssidinfo); + bool removeKnownSSID(const string& ssid); + bool connectToKnownSSID(const std::string& ssid); + bool quit(NMDevice *wifiNMDevice); + bool wait(GMainLoop *loop, int timeOutMs = 10000); // default maximium set as 10 sec + bool startWPS(); + bool stopWPS(); + bool setInterfaceState(std::string interface, bool enabled); + bool setIpSettings(const string interface, const Exchange::INetworkManager::IPAddress &address); + bool setPrimaryInterface(const string interface); + bool addMinimalEthernetConnection(std::string iface); + private: + NMDevice *getWifiDevice(); + + private: + wifiManager(); + ~wifiManager() { + NMLOG_INFO("~wifiManager"); + if(m_client != NULL) { + deleteClientConnection(); // handles pop + } + if (m_nmContext) { + // Do NOT pop here — deleteClientConnection already popped + g_main_context_unref(m_nmContext); + m_nmContext = NULL; + } + if(m_loop != NULL) { + g_main_loop_unref(m_loop); + m_loop = NULL; + } + } + + void wpsProcess(); + wifiManager(wifiManager const&) = delete; + void operator=(wifiManager const&) = delete; + + bool createClientNewConnection(); + void deleteClientConnection(); + + public: + NMClient *m_client; + GMainLoop *m_loop; + gboolean m_createNewConnection; + GMainContext *m_nmContext = nullptr; + char* m_objectPath = nullptr; + NMDevice *m_wifidevice; + GSource *m_source; + GCancellable *m_cancellable; + std::mutex m_cancellableMutex; + std::mutex m_opMutex; // serializes concurrent wifi operations from different threads + bool m_isSuccess = false; + NMConnection *m_appliedConn = nullptr; + guint64 m_versionId = 0; + SecretAgent m_secretAgent; + }; + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp new file mode 100644 index 000000000..1ee31c41b --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp @@ -0,0 +1,3377 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include "NetworkManagerLogger.h" +#include "NetworkManagerGdbusClient.h" +#include "NetworkManagerGdbusUtils.h" +#include "../NetworkManagerGnomeUtils.h" + +namespace WPEFramework +{ + namespace Plugin + { + extern NetworkManagerImplementation* _instance; + NetworkManagerClient::NetworkManagerClient() { + NMLOG_INFO("NetworkManagerClient"); + m_wpsProcessRun = false; + m_wpsActionTriggered = false; + } + + NetworkManagerClient::~NetworkManagerClient() { + NMLOG_INFO("~NetworkManagerClient"); + } + + bool updateRouteMetric(DbusMgr& m_dbus, const std::string& connectionPath, gint64 route_metric, const gchar* interface, const std::string& activeConnectionPath) + { + GError *error = nullptr; + deviceInfo devInfo{}; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface, devInfo)) + return false; + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath.c_str()); + + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating connection settings proxy"); + return false; + } + + GVariant *connectionSettings = g_dbus_proxy_call_sync( + settingsProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (connectionSettings == nullptr) { + NMLOG_ERROR("Error retrieving connection settings: %s", error->message); + g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *iterator; + GVariant *settingsDict; + const gchar *settingsKey; + const gchar *existingId = nullptr; + const gchar *existingType = nullptr; + const gchar *existingIPv4Method = nullptr; + const gchar *existingIPv6Method = nullptr; + const gchar *existingInterfaceName = nullptr; + + const gchar *existingKeyMgmt = nullptr; + const gchar *existingPSK = nullptr; + const gchar *existingSSID = nullptr; + GVariant *existingIPv4Settings = nullptr; + GVariant *existingIPv6Settings = nullptr; + + g_variant_get(connectionSettings, "(a{sa{sv}})", &iterator); + while (g_variant_iter_loop(iterator, "{&s@a{sv}}", &settingsKey, &settingsDict)) { + GVariantIter settingsIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&settingsIter, settingsDict); + while (g_variant_iter_loop(&settingsIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "id") == 0) { + existingId = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Connection ID: %s\n", existingId); + } else if (g_strcmp0(key, "type") == 0) { + existingType = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Connection Type: %s\n", existingType); + } else if (g_strcmp0(key, "interface-name") == 0) { + existingInterfaceName = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Interface Name: %s\n", existingInterfaceName); + } else if (g_strcmp0(key, "method") == 0) { + if(g_strcmp0(settingsKey, "ipv4") == 0) + { + existingIPv4Method = g_variant_get_string(value, NULL); + NMLOG_DEBUG("IPV4 Method: %s\n", existingIPv4Method); + } + else if(g_strcmp0(settingsKey, "ipv6") == 0) + { + existingIPv6Method = g_variant_get_string(value, NULL); + NMLOG_DEBUG("IPV6 Method: %s\n", existingIPv6Method); + } + } else if (g_strcmp0(key, "ssid") == 0) { + gsize size; + const guint8 *ssid = (const guint8 *) g_variant_get_fixed_array(value, &size, sizeof(guint8)); + // Convert the ssid to a null-terminated string + existingSSID = g_strndup((const gchar *)ssid, size); + } else if (g_strcmp0(key, "key-mgmt") == 0) { + existingKeyMgmt = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Key Management: %s\n", existingKeyMgmt); + } else if (g_strcmp0(key, "psk") == 0) { + existingPSK = g_variant_get_string(value, NULL); + NMLOG_DEBUG("psk: %s\n", existingPSK); + } else if (g_strcmp0(settingsKey, "ipv4") == 0) { + existingIPv4Settings = g_variant_ref(settingsDict); + } else if (g_strcmp0(settingsKey, "ipv6") == 0) { + existingIPv6Settings = g_variant_ref(settingsDict); + } + + } + } + g_variant_iter_free(iterator); + + GVariantBuilder connectionBuilder; + + GVariantBuilder wifiBuilder; + GVariantBuilder settingsBuilder; + GVariantBuilder wifiSecurityBuilder; + g_variant_builder_init(&settingsBuilder, G_VARIANT_TYPE("a{sa{sv}}")); + // Define the 'connection' dictionary with connection details + g_variant_builder_init(&connectionBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&connectionBuilder, "{sv}", "id", g_variant_new_string(existingId)); + g_variant_builder_add(&connectionBuilder, "{sv}", "type", g_variant_new_string(existingType)); + g_variant_builder_add(&connectionBuilder, "{sv}", "interface-name", g_variant_new_string(existingInterfaceName)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "connection", &connectionBuilder); + + if (g_strcmp0(interface, GnomeUtils::getWifiIfname()) == 0) { + // Define the '802-11-wireless' dictionary with Wi-Fi specific details + g_variant_builder_init(&wifiBuilder, G_VARIANT_TYPE("a{sv}")); + GVariantBuilder ssidBuilder; + g_variant_builder_init(&ssidBuilder, G_VARIANT_TYPE("ay")); + while (*existingSSID) { + g_variant_builder_add(&ssidBuilder, "y", *(existingSSID++)); + } + g_variant_builder_add(&wifiBuilder, "{sv}", "ssid", g_variant_builder_end(&ssidBuilder)); + g_variant_builder_add(&wifiBuilder, "{sv}", "mode", g_variant_new_string("infrastructure")); // Set Wi-Fi mode + g_variant_builder_add(&wifiBuilder, "{sv}", "security", g_variant_new_string("802-11-wireless-security")); // Security type + + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless", &wifiBuilder); + + // Define the '802-11-wireless-security' dictionary with security details + g_variant_builder_init(&wifiSecurityBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&wifiSecurityBuilder, "{sv}", "key-mgmt", g_variant_new_string(existingKeyMgmt)); // Key management + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless-security", &wifiSecurityBuilder); + } + + + GVariantBuilder ipv4Builder; + g_variant_builder_init(&ipv4Builder, G_VARIANT_TYPE("a{sv}")); + GVariantBuilder ipv6Builder; + g_variant_builder_init(&ipv6Builder, G_VARIANT_TYPE("a{sv}")); + + GnomeUtils::addGvariantToBuilder(existingIPv4Settings, &ipv4Builder, true); + GnomeUtils::addGvariantToBuilder(existingIPv6Settings, &ipv6Builder, true); + /*g_variant_builder_add(&ipv4Builder, "{sv}", "method", g_variant_new_string(existingIPv4Method)); + g_variant_builder_add(&ipv6Builder, "{sv}", "method", g_variant_new_string(existingIPv6Method));*/ + g_variant_builder_add(&ipv4Builder, "{sv}", "route-metric", g_variant_new_int64(route_metric)); + g_variant_builder_add(&ipv6Builder, "{sv}", "route-metric", g_variant_new_int64(route_metric)); + + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv4", &ipv4Builder); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv6", &ipv6Builder); + + g_dbus_proxy_call_sync( + settingsProxy, + "Update", + g_variant_new("(a{sa{sv}})", &settingsBuilder), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error) { + NMLOG_ERROR("Error updating connection settings: %s", error->message); + g_error_free(error); + } else { + NMLOG_DEBUG("Successfully updated IPv4 settings for %s interface", interface); + } + if(!GnomeUtils::activateConnection(m_dbus, connectionPath, devInfo.path)) + { + NMLOG_INFO("activateConnection not successful"); + return false; + } + else + NMLOG_INFO("activateConnection successful"); + + g_variant_unref(connectionSettings); + g_object_unref(settingsProxy); + return true; + } + + bool updateIPSettings(DbusMgr& m_dbus, const std::string& connectionPath, const Exchange::INetworkManager::IPAddress& address, const std::string& interface) + { + GError *error = nullptr; + deviceInfo devInfo{}; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + return false; + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath.c_str()); + + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating connection settings proxy"); + return false; + } + + GVariant *connectionSettings = g_dbus_proxy_call_sync( + settingsProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (connectionSettings == nullptr) { + NMLOG_ERROR("Error retrieving connection settings: %s", error->message); + g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *iterator; + GVariant *settingsDict; + const gchar *settingsKey; + const gchar *existingId = nullptr; + const gchar *existingType = nullptr; + const gchar *existingInterfaceName = nullptr; + + const gchar *existingKeyMgmt = nullptr; + const gchar *existingPSK = nullptr; + const gchar *existingSSID = nullptr; + GVariant *existingIPv4Settings = nullptr; + GVariant *existingIPv6Settings = nullptr; + + + g_variant_get(connectionSettings, "(a{sa{sv}})", &iterator); + while (g_variant_iter_loop(iterator, "{&s@a{sv}}", &settingsKey, &settingsDict)) { + GVariantIter settingsIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&settingsIter, settingsDict); + while (g_variant_iter_loop(&settingsIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "id") == 0) { + existingId = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Connection ID: %s\n", existingId); + } else if (g_strcmp0(key, "type") == 0) { + existingType = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Connection Type: %s\n", existingType); + } else if (g_strcmp0(key, "interface-name") == 0) { + existingInterfaceName = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Interface Name: %s\n", existingInterfaceName); + } else if (g_strcmp0(key, "ssid") == 0) { + gsize size; + const guint8 *ssid = (const guint8 *) g_variant_get_fixed_array(value, &size, sizeof(guint8)); + // Convert the ssid to a null-terminated string + existingSSID = g_strndup((const gchar *)ssid, size); + } else if (g_strcmp0(key, "key-mgmt") == 0) { + existingKeyMgmt = g_variant_get_string(value, NULL); + NMLOG_DEBUG("Key Management: %s\n", existingKeyMgmt); + } else if (g_strcmp0(key, "psk") == 0) { + existingPSK = g_variant_get_string(value, NULL); + NMLOG_DEBUG("psk: %s\n", existingPSK); + } else if (g_strcmp0(settingsKey, "ipv4") == 0) { + existingIPv4Settings = g_variant_ref(settingsDict); + } else if (g_strcmp0(settingsKey, "ipv6") == 0) { + existingIPv6Settings = g_variant_ref(settingsDict); + } + } + } + g_variant_iter_free(iterator); + + GVariantBuilder connectionBuilder; + + GVariantBuilder wifiBuilder; + GVariantBuilder settingsBuilder; + GVariantBuilder wifiSecurityBuilder; + GVariantBuilder addressesBuilder; + GVariantBuilder addressEntryBuilder; + GVariantBuilder dnsBuilder; + g_variant_builder_init(&settingsBuilder, G_VARIANT_TYPE("a{sa{sv}}")); + // Define the 'connection' dictionary with connection details + g_variant_builder_init(&connectionBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&connectionBuilder, "{sv}", "id", g_variant_new_string(existingId)); + g_variant_builder_add(&connectionBuilder, "{sv}", "type", g_variant_new_string(existingType)); + g_variant_builder_add(&connectionBuilder, "{sv}", "interface-name", g_variant_new_string(existingInterfaceName)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "connection", &connectionBuilder); + + if (g_strcmp0(interface.c_str(), GnomeUtils::getWifiIfname()) == 0) { + // Define the '802-11-wireless' dictionary with Wi-Fi specific details + g_variant_builder_init(&wifiBuilder, G_VARIANT_TYPE("a{sv}")); + GVariantBuilder ssidBuilder; + g_variant_builder_init(&ssidBuilder, G_VARIANT_TYPE("ay")); + while (*existingSSID) { + g_variant_builder_add(&ssidBuilder, "y", *(existingSSID++)); + } + g_variant_builder_add(&wifiBuilder, "{sv}", "ssid", g_variant_builder_end(&ssidBuilder)); + g_variant_builder_add(&wifiBuilder, "{sv}", "mode", g_variant_new_string("infrastructure")); // Set Wi-Fi mode + g_variant_builder_add(&wifiBuilder, "{sv}", "security", g_variant_new_string("802-11-wireless-security")); // Security type + + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless", &wifiBuilder); + + // Define the '802-11-wireless-security' dictionary with security details + g_variant_builder_init(&wifiSecurityBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&wifiSecurityBuilder, "{sv}", "key-mgmt", g_variant_new_string(existingKeyMgmt)); // Key management + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless-security", &wifiSecurityBuilder); + } + + GVariantBuilder ipv4Builder; + GVariantBuilder ipv6Builder; + + g_variant_builder_init(&ipv4Builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_init(&ipv6Builder, G_VARIANT_TYPE("a{sv}")); + if (g_strcmp0(address.ipversion.c_str(), "IPv4") == 0) + { + g_variant_builder_add(&ipv4Builder, "{sv}", "method", g_variant_new_string(address.autoconfig ? "auto" : "manual")); + if(!address.autoconfig) + { + // addresses + g_variant_builder_init(&addressesBuilder, G_VARIANT_TYPE("aau")); + + g_variant_builder_init(&addressEntryBuilder, G_VARIANT_TYPE("au")); + + g_variant_builder_add(&addressEntryBuilder, "u", GnomeUtils::ip4StrToNBO(address.ipaddress)); + g_variant_builder_add(&addressEntryBuilder, "u", address.prefix); + g_variant_builder_add(&addressEntryBuilder, "u", GnomeUtils::ip4StrToNBO(address.gateway)); + g_variant_builder_add_value(&addressesBuilder, g_variant_builder_end(&addressEntryBuilder)); + + g_variant_builder_add(&ipv4Builder, "{sv}", "addresses", g_variant_builder_end(&addressesBuilder)); + + // dns + g_variant_builder_init(&dnsBuilder, G_VARIANT_TYPE("au")); + g_variant_builder_add(&dnsBuilder, "u", GnomeUtils::ip4StrToNBO(address.primarydns)); + g_variant_builder_add(&dnsBuilder, "u", GnomeUtils::ip4StrToNBO(address.secondarydns)); + + g_variant_builder_add(&ipv4Builder, "{sv}", "dns", g_variant_builder_end(&dnsBuilder)); + // Add gateway + g_variant_builder_add(&ipv4Builder, "{sv}", "gateway", g_variant_new_string(address.gateway.c_str())); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv4", &ipv4Builder); + } + if (existingIPv6Settings) { + GnomeUtils::addGvariantToBuilder(existingIPv6Settings, &ipv6Builder, false); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv6", &ipv6Builder); + g_variant_unref(existingIPv6Settings); + } + } + else if (g_strcmp0(address.ipversion.c_str(), "IPv6") == 0) + { + g_variant_builder_add(&ipv6Builder, "{sv}", "method", g_variant_new_string(address.autoconfig ? "auto" : "manual")); + if(!address.autoconfig) + { + // addresses + g_variant_builder_init(&addressesBuilder, G_VARIANT_TYPE("a(ayuay)")); + + g_variant_builder_init(&addressEntryBuilder, G_VARIANT_TYPE("(ayuay)")); + std::array ip6 = GnomeUtils::ip6StrToNBO(address.ipaddress); + std::array gateway6 = GnomeUtils::ip6StrToNBO(address.gateway); + g_variant_builder_add_value(&addressEntryBuilder, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, ip6.data(), ip6.size(), sizeof(guint8))); + g_variant_builder_add(&addressEntryBuilder, "u", address.prefix); + g_variant_builder_add_value(&addressEntryBuilder, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, gateway6.data(), gateway6.size(), sizeof(guint8))); + g_variant_builder_add_value(&addressesBuilder, g_variant_builder_end(&addressEntryBuilder)); + g_variant_builder_add(&ipv6Builder, "{sv}", "addresses", g_variant_builder_end(&addressesBuilder)); + + //DNS + g_variant_builder_init(&dnsBuilder, G_VARIANT_TYPE("aay")); + std::array primaryDns6 = GnomeUtils::ip6StrToNBO(address.primarydns); + std::array secondaryDns6 = GnomeUtils::ip6StrToNBO(address.secondarydns); + g_variant_builder_add_value(&dnsBuilder, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, primaryDns6.data(), primaryDns6.size(), sizeof(guint8))); + g_variant_builder_add_value(&dnsBuilder, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, secondaryDns6.data(), secondaryDns6.size(), sizeof(guint8))); + g_variant_builder_add(&ipv6Builder, "{sv}", "dns", g_variant_builder_end(&dnsBuilder)); + // Add gateway + g_variant_builder_add(&ipv6Builder, "{sv}", "gateway", g_variant_new_string(address.gateway.c_str())); + + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv6", &ipv6Builder); + } + if (existingIPv4Settings) { + GnomeUtils::addGvariantToBuilder(existingIPv4Settings, &ipv4Builder, false); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv4", &ipv4Builder); + g_variant_unref(existingIPv4Settings); + } + } + + g_dbus_proxy_call_sync( + settingsProxy, + "Update", + g_variant_new("(a{sa{sv}})", &settingsBuilder), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error) { + NMLOG_ERROR("Error updating connection settings: %s", error->message); + g_error_free(error); + } else { + NMLOG_DEBUG("Successfully updated IPv4 settings for %s interface", interface.c_str()); + } + if(!GnomeUtils::activateConnection(m_dbus, connectionPath, devInfo.path)) + { + NMLOG_INFO("activateConnection not successful"); + return false; + } + else + NMLOG_INFO("activateConnection successful"); + + g_variant_unref(connectionSettings); + g_object_unref(settingsProxy); + return true; + } + + bool updateHostnameSettings(DbusMgr& m_dbus, const std::string& connectionPath, const std::string& hostname, const std::string& interface) + { + GError *error = nullptr; + deviceInfo devInfo{}; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + return false; + + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath.c_str()); + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating connection settings proxy"); + return false; + } + + GVariant *connectionSettings = g_dbus_proxy_call_sync( + settingsProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (connectionSettings == nullptr) { + NMLOG_ERROR("Error retrieving connection settings: %s", error->message); + g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *iterator; + GVariant *settingsDict; + const gchar *settingsKey; + const gchar *existingId = nullptr; + const gchar *existingType = nullptr; + const gchar *existingInterfaceName = nullptr; + const gchar *existingKeyMgmt = nullptr; + const gchar *existingSSID = nullptr; + GVariant *existingIPv4Settings = nullptr; + GVariant *existingIPv6Settings = nullptr; + + g_variant_get(connectionSettings, "(a{sa{sv}})", &iterator); + while (g_variant_iter_loop(iterator, "{&s@a{sv}}", &settingsKey, &settingsDict)) { + GVariantIter settingsIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&settingsIter, settingsDict); + while (g_variant_iter_loop(&settingsIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "id") == 0) { + existingId = g_variant_get_string(value, NULL); + } else if (g_strcmp0(key, "type") == 0) { + existingType = g_variant_get_string(value, NULL); + } else if (g_strcmp0(key, "interface-name") == 0) { + existingInterfaceName = g_variant_get_string(value, NULL); + } else if (g_strcmp0(key, "ssid") == 0) { + gsize size; + const guint8 *ssid = (const guint8 *) g_variant_get_fixed_array(value, &size, sizeof(guint8)); + existingSSID = g_strndup((const gchar *)ssid, size); + } else if (g_strcmp0(key, "key-mgmt") == 0) { + existingKeyMgmt = g_variant_get_string(value, NULL); + } else if (g_strcmp0(settingsKey, "ipv4") == 0) { + existingIPv4Settings = g_variant_ref(settingsDict); + } else if (g_strcmp0(settingsKey, "ipv6") == 0) { + existingIPv6Settings = g_variant_ref(settingsDict); + } + } + } + g_variant_iter_free(iterator); + + GVariantBuilder connectionBuilder; + GVariantBuilder wifiBuilder; + GVariantBuilder settingsBuilder; + GVariantBuilder wifiSecurityBuilder; + g_variant_builder_init(&settingsBuilder, G_VARIANT_TYPE("a{sa{sv}}")); + + // Define the 'connection' dictionary with connection details + g_variant_builder_init(&connectionBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&connectionBuilder, "{sv}", "id", g_variant_new_string(existingId)); + g_variant_builder_add(&connectionBuilder, "{sv}", "type", g_variant_new_string(existingType)); + g_variant_builder_add(&connectionBuilder, "{sv}", "interface-name", g_variant_new_string(existingInterfaceName)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "connection", &connectionBuilder); + + if (g_strcmp0(interface.c_str(), GnomeUtils::getWifiIfname()) == 0) { + // Define the '802-11-wireless' dictionary with Wi-Fi specific details + g_variant_builder_init(&wifiBuilder, G_VARIANT_TYPE("a{sv}")); + GVariantBuilder ssidBuilder; + g_variant_builder_init(&ssidBuilder, G_VARIANT_TYPE("ay")); + while (*existingSSID) { + g_variant_builder_add(&ssidBuilder, "y", *(existingSSID++)); + } + g_variant_builder_add(&wifiBuilder, "{sv}", "ssid", g_variant_builder_end(&ssidBuilder)); + g_variant_builder_add(&wifiBuilder, "{sv}", "mode", g_variant_new_string("infrastructure")); + g_variant_builder_add(&wifiBuilder, "{sv}", "security", g_variant_new_string("802-11-wireless-security")); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless", &wifiBuilder); + + // Define the '802-11-wireless-security' dictionary with security details + g_variant_builder_init(&wifiSecurityBuilder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&wifiSecurityBuilder, "{sv}", "key-mgmt", g_variant_new_string(existingKeyMgmt)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "802-11-wireless-security", &wifiSecurityBuilder); + } + + GVariantBuilder ipv4Builder; + g_variant_builder_init(&ipv4Builder, G_VARIANT_TYPE("a{sv}")); + GVariantBuilder ipv6Builder; + g_variant_builder_init(&ipv6Builder, G_VARIANT_TYPE("a{sv}")); + + // Add existing IPv4 settings and set hostname + if (existingIPv4Settings) + { + GVariantIter iter; + const gchar *key; + GVariant *value; + g_variant_iter_init(&iter, existingIPv4Settings); + while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) { + // Skip existing hostname properties to avoid duplicates + if (g_strcmp0(key, "dhcp-hostname") != 0 && g_strcmp0(key, "dhcp-send-hostname") != 0) { + g_variant_builder_add(&ipv4Builder, "{sv}", key, value); + } + } + } + // Now add the new hostname settings + g_variant_builder_add(&ipv4Builder, "{sv}", "dhcp-hostname", g_variant_new_string(hostname.c_str())); + g_variant_builder_add(&ipv4Builder, "{sv}", "dhcp-send-hostname", g_variant_new_boolean(TRUE)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv4", &ipv4Builder); + + // Add existing IPv6 settings, excluding hostname-related properties + if (existingIPv6Settings) { + GVariantIter iter; + const gchar *key; + GVariant *value; + g_variant_iter_init(&iter, existingIPv6Settings); + while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) { + // Skip existing hostname properties to avoid duplicates + if (g_strcmp0(key, "dhcp-hostname") != 0 && g_strcmp0(key, "dhcp-send-hostname") != 0) { + g_variant_builder_add(&ipv6Builder, "{sv}", key, value); + } + } + } + // Now add the new hostname settings + g_variant_builder_add(&ipv6Builder, "{sv}", "dhcp-hostname", g_variant_new_string(hostname.c_str())); + g_variant_builder_add(&ipv6Builder, "{sv}", "dhcp-send-hostname", g_variant_new_boolean(TRUE)); + g_variant_builder_add(&settingsBuilder, "{sa{sv}}", "ipv6", &ipv6Builder); + + g_dbus_proxy_call_sync( + settingsProxy, + "Update", + g_variant_new("(a{sa{sv}})", &settingsBuilder), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error) { + NMLOG_ERROR("Error updating connection settings with hostname: %s", error->message); + g_error_free(error); + g_variant_unref(connectionSettings); + g_object_unref(settingsProxy); + return false; + } + + NMLOG_DEBUG("Successfully updated hostname settings for %s interface to: %s", interface.c_str(), hostname.c_str()); + + // Activate connection to apply changes + if(!GnomeUtils::activateConnection(m_dbus, connectionPath, devInfo.path)) + { + NMLOG_INFO("activateConnection not successful"); + } + else + NMLOG_INFO("activateConnection successful"); + + if (existingIPv4Settings) g_variant_unref(existingIPv4Settings); + if (existingIPv6Settings) g_variant_unref(existingIPv6Settings); + g_variant_unref(connectionSettings); + g_object_unref(settingsProxy); + return true; + } + + bool NetworkManagerClient::getAvailableInterfaces(std::vector& interfacesList) + { + deviceInfo ethDevInfo{}; + deviceInfo wifiDevInfo{}; + Exchange::INetworkManager::InterfaceDetails ethInterface; + Exchange::INetworkManager::InterfaceDetails wifiInterface; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getEthIfname(), ethDevInfo)) + return false; + ethInterface.type = Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_ETHERNET; + ethInterface.name = ethDevInfo.interface; + ethInterface.mac = ethDevInfo.MAC; + ethInterface.enabled = ethDevInfo.managed?true:false; + ethInterface.connected = ethDevInfo.state == NM_DEVICE_STATE_ACTIVATED?true:false; + interfacesList.push_back(ethInterface); + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), wifiDevInfo)) + return false; + wifiInterface.type = Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_WIFI; + wifiInterface.name = wifiDevInfo.interface; + wifiInterface.mac = wifiDevInfo.MAC; + wifiInterface.enabled = wifiDevInfo.managed?true:false; + wifiInterface.connected = wifiDevInfo.state == NM_DEVICE_STATE_ACTIVATED?true:false; + interfacesList.push_back(wifiInterface); + + return true; + } + + bool NetworkManagerClient::setPrimaryInterface(const std::string& interface) + { + GError *error = nullptr; + GDBusProxy *nmProxy = nullptr; + std::string connectionPath; + nmProxy = m_dbus.getNetworkManagerProxy(); + if(nmProxy == nullptr) + return false; + GVariant *activeConnections = g_dbus_proxy_get_cached_property(nmProxy, "ActiveConnections"); + if (activeConnections == nullptr) { + NMLOG_ERROR("Error retrieving active connections"); + g_object_unref(nmProxy); + return false; + } + + GVariantIter iter; + g_variant_iter_init(&iter, activeConnections); + gchar *activeConnectionPath = nullptr; + gint64 routeMetric; + while (g_variant_iter_loop(&iter, "o", &activeConnectionPath)) { + GDBusProxy *activeConnectionProxy = m_dbus.getNetworkManagerActiveConnProxy(activeConnectionPath); + + if (activeConnectionProxy == nullptr) { + NMLOG_ERROR("Error creating active connection proxy"); + continue; + } + + GVariant *devicesVar = g_dbus_proxy_get_cached_property(activeConnectionProxy, "Devices"); + if (devicesVar == nullptr) { + NMLOG_ERROR("Error retrieving devices property"); + g_object_unref(activeConnectionProxy); + continue; + } + + GVariantIter devicesIter; + g_variant_iter_init(&devicesIter, devicesVar); + gchar *devicePath = nullptr; + + while (g_variant_iter_loop(&devicesIter, "o", &devicePath)) { + GDBusProxy *deviceProxy = m_dbus.getNetworkManagerDeviceProxy(devicePath); + + if (deviceProxy == nullptr) { + NMLOG_ERROR("Error creating device proxy: %s", error->message); + g_error_free(error); + continue; + } + + GVariant *ifaceProperty = g_dbus_proxy_get_cached_property(deviceProxy, "Interface"); + if (ifaceProperty) { + const gchar *iface = g_variant_get_string(ifaceProperty, nullptr); + GVariant *connectionProperty = g_dbus_proxy_get_cached_property(activeConnectionProxy, "Connection"); + if (connectionProperty) { + connectionPath = g_variant_get_string(connectionProperty, nullptr); + g_variant_unref(connectionProperty); + } else { + NMLOG_ERROR("Error retrieving connection property"); + } + if (!g_strcmp0(iface, GnomeUtils::getWifiIfname()) || !g_strcmp0(iface, GnomeUtils::getEthIfname())) { + if(interface == iface) + routeMetric = ROUTE_METRIC_PRIORITY_HIGH; + else + routeMetric = ROUTE_METRIC_PRIORITY_LOW; + if(!updateRouteMetric(m_dbus, connectionPath, routeMetric, iface, activeConnectionPath)) + { + NMLOG_ERROR("Error: Failed to update route metric for Interface %s", iface); + return false; + } + } + g_variant_unref(ifaceProperty); + g_object_unref(deviceProxy); + break; + } + g_variant_unref(ifaceProperty); + } + + g_variant_unref(devicesVar); + g_object_unref(activeConnectionProxy); + } + + g_variant_unref(activeConnections); + g_object_unref(nmProxy); + + return true; + } + + bool NetworkManagerClient::getDefaultInterface(std::string& interface) + { + deviceInfo devInfo{}; + std::string wifiname = GnomeUtils::getWifiIfname(), ethname = GnomeUtils::getEthIfname(); + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, ethname.c_str(), devInfo)) + return false; + if(devInfo.state > NM_DEVICE_STATE_DISCONNECTED && devInfo.state < NM_DEVICE_STATE_DEACTIVATING) + interface = ethname; + else + interface = wifiname; // default is wifi + return true; + } + + bool NetworkManagerClient::getPrimaryInterface(std::string& interface) + { + GError* error = nullptr; + std::string primaryConnectionPath; + GDBusProxy *nmProxy = nullptr; + + nmProxy = m_dbus.getNetworkManagerPropertyProxy("/org/freedesktop/NetworkManager"); + if(nmProxy == nullptr) + return false; + + GVariant* result = g_dbus_proxy_call_sync( + nmProxy, + "Get", + g_variant_new("(ss)", "org.freedesktop.NetworkManager", "PrimaryConnection"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error) { + NMLOG_ERROR("Error: Getting primary connection path %s",error->message); + g_error_free(error); + return false; + } + + if (result != NULL) { + GVariant* value; + g_variant_get(result, "(v)", &value); + if (g_variant_is_of_type(value, G_VARIANT_TYPE_OBJECT_PATH)) { + primaryConnectionPath = g_variant_get_string(value, nullptr); + } else { + NMLOG_ERROR("Expected object path but got different type"); + } + g_variant_unref(value); + g_variant_unref(result); + } + + GDBusProxy* deviceProxy = m_dbus.getNetworkManagerPropertyProxy(primaryConnectionPath.c_str()); + if(deviceProxy == NULL) + return false; + + std::string defaultDevicePath; + + result = g_dbus_proxy_call_sync( + deviceProxy, + "Get", + g_variant_new("(ss)", "org.freedesktop.NetworkManager.Connection.Active", "Devices"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error) { + NMLOG_ERROR("Error: Getting default device path %s", error->message); + g_error_free(error); + return false; + } + + if (result != nullptr) { + GVariant* value; + g_variant_get(result, "(v)", &value); + if (g_variant_is_of_type(value, G_VARIANT_TYPE_ARRAY)) { + GVariantIter* iter; + const gchar* devicePath; + g_variant_get(value, "ao", &iter); + + if (g_variant_iter_loop(iter, "o", &devicePath)) { + defaultDevicePath = devicePath; + } + g_variant_iter_free(iter); + } else { + NMLOG_ERROR("Expected array of object paths but got different type"); + } + g_variant_unref(value); + g_variant_unref(result); + } + GDBusProxy* defaultDeviceProxy = m_dbus.getNetworkManagerPropertyProxy(defaultDevicePath.c_str()); + if(defaultDeviceProxy == NULL) + return false; + + error = nullptr; + result = g_dbus_proxy_call_sync( + defaultDeviceProxy, + "Get", + g_variant_new("(ss)", "org.freedesktop.NetworkManager.Device", "Interface"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error) { + NMLOG_ERROR("Error: Getting primary interface %s", error->message); + g_error_free(error); + return false; + } + + if (result != nullptr) { + GVariant* value; + g_variant_get(result, "(v)", &value); + if (g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) { + interface = g_variant_get_string(value, nullptr); + } else { + NMLOG_ERROR("Expected string but got different type"); + } + g_variant_unref(value); + g_variant_unref(result); + } + + g_object_unref(defaultDeviceProxy); + g_object_unref(deviceProxy); + g_object_unref(nmProxy); + return true; + } + + bool NetworkManagerClient::setInterfaceState(const std::string& interface, bool enable) + { + deviceInfo devInfo{}; + GError* error = nullptr; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + return false; + + GDBusProxy* deviceProxy = m_dbus.getNetworkManagerPropertyProxy(devInfo.path.c_str()); + if(deviceProxy == NULL) + return false; + NMLOG_INFO("interface %s state: %s", interface.c_str(), enable ? "enabled" : "disabled"); + + NMDeviceState deviceState = devInfo.state; + + if (enable) { + NMLOG_DEBUG("Enabling interface..."); + if (deviceState >= NM_DEVICE_STATE_UNAVAILABLE && devInfo.managed) // already enabled and managed + { + return true; + } + } else { + NMLOG_DEBUG("Disabling interface..."); + if (deviceState < NM_DEVICE_STATE_UNAVAILABLE && !devInfo.managed) // already disabled and unmanaged + { + return true; + } + else if (deviceState > NM_DEVICE_STATE_DISCONNECTED) { + NMLOG_DEBUG("Disconnecting device..."); + // Disconnect the device before setting it to unmanaged. + // This ensures that NetworkManager cleanly removes any IP addresses, routes, + // and DNS configuration associated with the interface. Setting an interface + // to unmanaged without disconnecting first may leave residual configuration + // that can cause networking issues. + GDBusProxy* deviceProxy = m_dbus.getNetworkManagerDeviceProxy(devInfo.path.c_str()); + if(deviceProxy != NULL) { + GVariant* disconnectResult = g_dbus_proxy_call_sync( + deviceProxy, + "Disconnect", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error) { + NMLOG_WARNING("Error disconnecting device: %s", error->message); + g_error_free(error); + error = nullptr; + } else if (disconnectResult) { + g_variant_unref(disconnectResult); + } + g_object_unref(deviceProxy); + } + + // Wait until device is truly disconnected + int retry = 24; // 12 seconds + NMDeviceState oldDevState = NM_DEVICE_STATE_UNKNOWN; + while (retry-- > 0) { + /* Force glib event processing to update state + * This below line will create an uncertain time wait. We are taking a fixed time interval of 12 seconds. + */ + // while (g_main_context_iteration(NULL, FALSE)); + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) { + break; + } + deviceState = devInfo.state; + if(oldDevState != deviceState) + { + oldDevState = deviceState; + NMLOG_WARNING("Device state: %d", deviceState); + } + + if (deviceState <= NM_DEVICE_STATE_DISCONNECTED) + break; + + g_usleep(500 * 1000); // 500ms (much faster response) + } + } + } + + if(deviceState > NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_WARNING("Device not fully disconnected (state: %d), setting to unmanaged state", deviceState); + } + + // Set the "Managed" property to enable/disable the device + /*GDBusProxy* propertyProxy = m_dbus.getNetworkManagerPropertyProxy(devInfo.path.c_str()); + if(propertyProxy == NULL) + return false;*/ + + GVariant* result = g_dbus_proxy_call_sync( + deviceProxy, + "Set", + g_variant_new("(ssv)", "org.freedesktop.NetworkManager.Device", "Managed", g_variant_new_boolean(enable)), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + bool success = (error == nullptr); + + if (error) { + NMLOG_ERROR("Error %s network interface: %s", (enable ? "enabling" : "disabling"), error->message); + g_error_free(error); + } else { + NMLOG_DEBUG("Network interface %s successfully %s", interface.c_str(), (enable ? "enabled" : "disabled")); + if (result != nullptr) { + g_variant_unref(result); + } + } + g_object_unref(deviceProxy); + + if(success) + { + // Set persistent marker file for specific interface + if(interface == GnomeUtils::getEthIfname()) + nmUtils::setMarkerFile(EthernetDisableMarker, enable); + else if(interface == GnomeUtils::getWifiIfname()) + nmUtils::setMarkerFile(WiFiDisableMarker, enable); + + // Auto-reconnection logic when interface is enabled + if(enable) { + // Wait for 1 sec to change the device state + sleep(1); + if(interface == GnomeUtils::getWifiIfname() && _instance != nullptr) { + const std::string lastConnectedSSID = _instance->getLastConnectedSSID(); + NMLOG_INFO("Activating connection '%s' ...", lastConnectedSSID.c_str()); + activateKnownConnection(GnomeUtils::getWifiIfname(), lastConnectedSSID); + } + else if(interface == GnomeUtils::getEthIfname()) { + NMLOG_INFO("Activating connection 'Wired connection 1' ..."); + // default wired connection name is 'Wired connection 1' + activateKnownConnection(GnomeUtils::getEthIfname(), "Wired connection 1"); + } + } + } + + return success; + } + + bool NetworkManagerClient::getInterfaceState(const std::string& interface, bool& isEnabled) + { + deviceInfo devInfo{}; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + return false; + + GError* error = nullptr; + GDBusProxy* deviceProxy = m_dbus.getNetworkManagerPropertyProxy(devInfo.path.c_str()); + if(deviceProxy == nullptr) + return false; + + // Call the "Get" method using the proxy + GVariant* result = g_dbus_proxy_call_sync( + deviceProxy, + "Get", + g_variant_new("(ss)", "org.freedesktop.NetworkManager.Device", "Managed"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error != nullptr) { + NMLOG_ERROR("Error getting network interface state: %s", error->message); + g_error_free(error); + } else { + NMLOG_DEBUG("Network interface state retrieved successfully: %s",devInfo.path.c_str()); + GVariant* managedVariant; + g_variant_get(result, "(v)", &managedVariant); + + isEnabled = g_variant_get_boolean(managedVariant); + g_variant_unref(managedVariant); + + NMLOG_DEBUG("Interface %s is %s", interface.c_str(), isEnabled ? "enabled" : "disabled"); + + g_variant_unref(result); + } + + return true; + } + + bool NetworkManagerClient::setIPSettings(const std::string& interface, const Exchange::INetworkManager::IPAddress& address) + { + std::string connectionPath; + + if(interface.empty() || (interface != GnomeUtils::getEthIfname() && interface != GnomeUtils::getWifiIfname())) + { + NMLOG_ERROR("Invalid interface name: %s", interface.c_str()); + return false; + } + + if (!GnomeUtils::getSettingsConnectionPath(m_dbus, connectionPath, interface)) + { + NMLOG_ERROR("Error: connection path not found for interface %s", interface.c_str()); + return false; + } + if (connectionPath.empty()) { + NMLOG_WARNING("No active connections available to edit for interface %s", interface.c_str()); + return true; + } + if(!updateIPSettings(m_dbus, connectionPath, address, interface)) + { + NMLOG_ERROR("Error: Failed to update route metric for Interface %s", interface.c_str()); + return false; + } + + return true; + } + + bool NetworkManagerClient::getIPSettings(std::string& interface, const std::string& ipversion, Exchange::INetworkManager::IPAddress& result) + { + std::string devicePath; + std::string addressStr; + guint32 prefix = 0; + const gchar *gatewayIp = nullptr; + gchar *dhcpServerIp = nullptr; + gchar **dnsList = nullptr; + gchar *ip4ConfigPath = nullptr; + gchar *dhcp4ConfigPath = nullptr; + gchar *dnsAddresses = nullptr; + gchar *dhcp6ConfigPath = nullptr; + gchar *ip6ConfigPath = nullptr; + GDBusProxy* ipv4Proxy = nullptr; + GDBusProxy* ipv6Proxy = nullptr; + const gchar *IPv4Method = nullptr; + const gchar *IPv6Method = nullptr; + deviceInfo devInfo{}; + GError *error = nullptr; + string ipversionStr; + + std::string wifiname = GnomeUtils::getWifiIfname(), ethname = GnomeUtils::getEthIfname(); + + if(interface.empty()) + { + if(Core::ERROR_NONE != _instance->GetPrimaryInterface(interface)) + { + NMLOG_WARNING("default interface get failed"); + return true; + } + if(interface.empty()) + { + NMLOG_DEBUG("default interface return empty default is wlan0"); + interface = wifiname; + } + } + else if(wifiname != interface && ethname != interface) + { + NMLOG_ERROR("interface: %s; not valid", interface.c_str()); + return false; + } + + if(ipversion.empty()) + { + ipversionStr = "IPV4"; + } + else + { + ipversionStr = ipversion; + } + + // Add caching optimization similar to RDK proxy + if (wifiname == interface) + { + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !_instance->m_wlanIPv4Address.ipaddress.empty()) + { + NMLOG_DEBUG("%s IPv4 address from cache", wifiname.c_str()); + result = _instance->m_wlanIPv4Address; + return true; + } + else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && !_instance->m_wlanIPv6Address.ipaddress.empty()) + { + NMLOG_DEBUG("%s IPv6 address from cache", wifiname.c_str()); + result = _instance->m_wlanIPv6Address; + return true; + } + } + else if (ethname == interface) + { + if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV4") && !_instance->m_ethIPv4Address.ipaddress.empty()) + { + NMLOG_DEBUG("%s IPv4 address from cache", ethname.c_str()); + result = _instance->m_ethIPv4Address; + return true; + } + else if(nmUtils::caseInsensitiveCompare(ipversionStr, "IPV6") && !_instance->m_ethIPv6Address.ipaddress.empty()) + { + NMLOG_DEBUG("%s IPv6 address from cache", ethname.c_str()); + result = _instance->m_ethIPv6Address; + return true; + } + } + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + { + return false; + } + + if(devInfo.state <= NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_WARNING("Device state is not a valid state: (%d)", devInfo.state); + return false; + } + else if (devInfo.state > NM_DEVICE_STATE_DISCONNECTED && devInfo.state < NM_DEVICE_STATE_IP_CHECK) + { + result.autoconfig = true; + if(ipversion.empty()) + result.ipversion = "IPv4"; + else + result.ipversion = ipversion; + return true; + } + + if(!GnomeUtils::getDeviceByIpIface(m_dbus, interface.c_str(), devicePath)) + return false; + GDBusProxy *deviceProxy = m_dbus.getNetworkManagerDeviceProxy(devicePath.c_str()); + if(deviceProxy == nullptr) + return false; + if(ipversion.empty() || (g_strcmp0(ipversion.c_str(), "IPv4") == 0)) + { + GVariant *ip4Property = g_dbus_proxy_get_cached_property(deviceProxy, "Ip4Config"); + if (ip4Property != nullptr) + { + g_variant_get(ip4Property, "o", &ip4ConfigPath); + g_variant_unref(ip4Property); + } + else + { + NMLOG_ERROR("Failed to get Ip4Config property"); + g_object_unref(deviceProxy); + } + + GVariant *dhcp4Property = g_dbus_proxy_get_cached_property(deviceProxy, "Dhcp4Config"); + if (dhcp4Property != nullptr) + { + g_variant_get(dhcp4Property, "o", &dhcp4ConfigPath); + g_variant_unref(dhcp4Property); + } + else + { + NMLOG_ERROR("Failed to get Dhcp4Config property"); + g_object_unref(deviceProxy); + } + + ipv4Proxy = m_dbus.getNetworkManagerIpv4Proxy(ip4ConfigPath); + g_free(ip4ConfigPath); + // Get the 'Addresses' property + if(ipv4Proxy != nullptr) + { + GVariant *addressesProperty = g_dbus_proxy_get_cached_property(ipv4Proxy, "Addresses"); + + if(addressesProperty != nullptr) + { + gsize numAddresses = g_variant_n_children(addressesProperty); + for (gsize i = 0; i < numAddresses; ++i) { + GVariant *addressArray = g_variant_get_child_value(addressesProperty, i); + GVariantIter iter; + guint32 addr; + + g_variant_iter_init(&iter, addressArray); + + if (g_variant_iter_next(&iter, "u", &addr)) { + addressStr = GnomeUtils::ip4ToString(addr); + NMLOG_DEBUG("IP Address: %s", addressStr.c_str()); + } + if (g_variant_iter_next(&iter, "u", &prefix)) { + NMLOG_DEBUG("Prefix: %d", prefix); + } + + g_variant_unref(addressArray); + } + + g_variant_unref(addressesProperty); + } + else + { + NMLOG_ERROR("Failed to get Addresses property"); + g_object_unref(ipv4Proxy); + } + + // Get the 'Gateway' property + GVariant *gatewayProperty = g_dbus_proxy_get_cached_property(ipv4Proxy, "Gateway"); + if (gatewayProperty != nullptr) { + // Fetch and print the Gateway IP + gatewayIp = g_variant_get_string(gatewayProperty, nullptr); + NMLOG_DEBUG("Gateway: %s", gatewayIp); + g_variant_unref(gatewayProperty); + } + else + { + NMLOG_ERROR("Failed to get Gateway property"); + g_variant_unref(addressesProperty); + g_object_unref(ipv4Proxy); + } + } + GDBusProxy* dhcpv4Proxy = m_dbus.getNetworkManagerDhcpv4Proxy(dhcp4ConfigPath); + g_free(dhcp4ConfigPath); + + if(dhcpv4Proxy != nullptr) + { + // Get the 'Options' property + GVariant *optionsProperty = g_dbus_proxy_get_cached_property(dhcpv4Proxy, "Options"); + + if (optionsProperty != nullptr) { + + GVariantIter *iter; + gchar *key; + GVariant *value; + + // Print the whole options property for debugging + gchar *optionsStr = g_variant_print(optionsProperty, TRUE); + NMLOG_DEBUG("Options property: %s", optionsStr); + g_free(optionsStr); + + g_variant_get(optionsProperty, "a{sv}", &iter); + + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "dhcp_server_identifier") == 0) { + dhcpServerIp = g_strdup(g_variant_get_string(value, nullptr)); + } else if (g_strcmp0(key, "domain_name_servers") == 0) { + dnsAddresses = g_strdup(g_variant_get_string(value, nullptr)); + } + g_variant_unref(value); + + if (dhcpServerIp && dnsAddresses) { + break; + } + } + + g_variant_iter_free(iter); + g_variant_unref(optionsProperty); + g_object_unref(dhcpv4Proxy); + + if (dhcpServerIp) { + NMLOG_DEBUG("DHCP server IP address: %s", dhcpServerIp); + } else { + dhcpServerIp = nullptr; + NMLOG_DEBUG("Failed to find DHCP server IP address"); + } + if (dnsAddresses) { + // DNS addresses are space-separated, we need to split them + dnsList = g_strsplit(dnsAddresses, " ", -1); + if (dnsList[0] != nullptr) { + NMLOG_DEBUG("Primary DNS: %s", dnsList[0]); + if (dnsList[1] != nullptr) { + NMLOG_DEBUG("Secondary DNS: %s", dnsList[1]); + } else { + NMLOG_DEBUG("Secondary DNS: Not available"); + } + } else { + NMLOG_DEBUG("Failed to parse DNS addresses"); + } + } else { + NMLOG_DEBUG("Failed to find DNS addresses"); + } + } + else + { + NMLOG_ERROR("Failed to get Options property"); + g_object_unref(dhcpv4Proxy); + } + } + result.ipversion = "IPv4"; + } + if((addressStr.empty() && !(g_strcmp0(ipversion.c_str(), "IPv4") == 0)) || g_strcmp0(ipversion.c_str(), "IPV6") == 0) + { + GVariant *ip6Property = g_dbus_proxy_get_cached_property(deviceProxy, "Ip6Config"); + if (ip6Property != nullptr) { + g_variant_get(ip6Property, "o", &ip6ConfigPath); + g_variant_unref(ip6Property); + } + else + { + NMLOG_ERROR("Failed to get Ip6Config property"); + g_object_unref(deviceProxy); + } + + GVariant *dhcp6Property = g_dbus_proxy_get_cached_property(deviceProxy, "Dhcp6Config"); + if (dhcp6Property != nullptr) { + g_variant_get(dhcp6Property, "o", &dhcp6ConfigPath); + g_variant_unref(dhcp6Property); + } + else + { + NMLOG_ERROR("Failed to get Dhcp6Config property"); + g_object_unref(deviceProxy); + } + + + ipv6Proxy = m_dbus.getNetworkManagerIpv6Proxy(ip6ConfigPath); + g_free(ip6ConfigPath); + if(ipv6Proxy != nullptr) + { + // Get the 'Addresses' property + GVariant *addressesProperty = g_dbus_proxy_get_cached_property(ipv6Proxy, "Addresses"); + if (addressesProperty != nullptr) + { + gsize numAddresses = g_variant_n_children(addressesProperty); + for (gsize i = 0; i < numAddresses; ++i) { + GVariant *addressTuple = g_variant_get_child_value(addressesProperty, i); + GVariant *addressArray = g_variant_get_child_value(addressTuple, 0); // Get the first byte array (IPv6 address) + GVariant *prefixVariant = g_variant_get_child_value(addressTuple, 1); // Get the prefix + prefix = g_variant_get_uint32(prefixVariant); + + uint8_t ipv6Addr[16]; + gsize addrLen; + gconstpointer addrData = g_variant_get_fixed_array(addressArray, &addrLen, 1); + if (addrLen == 16) { + memcpy(ipv6Addr, addrData, addrLen); + addressStr = GnomeUtils::ip6ToString(ipv6Addr); + if ((ipv6Addr[0] & 0xE0) == 0x20) // Check if the first three bits are within the range for global + { + break; + } + NMLOG_DEBUG("IPv6 Address: %s Prefix: %d", addressStr.c_str(), prefix); + } + + g_variant_unref(addressArray); + g_variant_unref(prefixVariant); + g_variant_unref(addressTuple); + } + } + else + { + NMLOG_ERROR("Failed to get Addresses property for IPv6"); + g_object_unref(ipv6Proxy); + } + g_variant_unref(addressesProperty); + + // Get the 'Gateway' property + GVariant *gatewayProperty = g_dbus_proxy_get_cached_property(ipv6Proxy, "Gateway"); + if (gatewayProperty != nullptr) + { + // Fetch and print the Gateway IP + gatewayIp = g_variant_get_string(gatewayProperty, nullptr); + NMLOG_DEBUG("IPv6 Gateway: %s", gatewayIp); + g_variant_unref(gatewayProperty); + } + else + { + gatewayIp = nullptr; + NMLOG_ERROR("Failed to get Gateway property for IPv6"); + g_object_unref(ipv6Proxy); + } + } + GDBusProxy* dhcpv6Proxy = m_dbus.getNetworkManagerDhcpv6Proxy(dhcp6ConfigPath); + g_free(dhcp6ConfigPath); + if(dhcpv6Proxy != nullptr) + { + // Get the 'Options' property + GVariant *optionsProperty = g_dbus_proxy_get_cached_property(dhcpv6Proxy, "Options"); + if (optionsProperty != nullptr) { + + GVariantIter *iter; + gchar *key; + GVariant *value; + gchar *ip6Address = nullptr; + + // Print the whole options property for debugging + gchar *optionsStr = g_variant_print(optionsProperty, TRUE); + NMLOG_DEBUG("Options property: %s", optionsStr); + g_free(optionsStr); + + g_variant_get(optionsProperty, "a{sv}", &iter); + + while (g_variant_iter_next(iter, "{&sv}", &key, &value)) { + gchar *valueStr = g_variant_print(value, TRUE); + NMLOG_DEBUG("Key: %s Value: %s", key, valueStr); + g_free(valueStr); + + if (g_strcmp0(key, "dhcp6_name_servers") == 0) { + dnsAddresses = g_strdup(g_variant_get_string(value, nullptr)); + } else if (g_strcmp0(key, "ip6_address") == 0) { + ip6Address = g_strdup(g_variant_get_string(value, nullptr)); + } + g_variant_unref(value); + } + + g_variant_iter_free(iter); + g_variant_unref(optionsProperty); + g_object_unref(dhcpv6Proxy); + + if (ip6Address) { + NMLOG_DEBUG("IPv6 Address: %s", ip6Address); + g_free(ip6Address); + } else { + NMLOG_ERROR("Failed to find the IPv6 address"); + } + + if (dnsAddresses) { + // DNS addresses are space-separated, we need to split them + dnsList = g_strsplit(dnsAddresses, " ", -1); + if (dnsList[0] != nullptr) { + NMLOG_DEBUG("Primary DNS: %s", dnsList[0]); + if (dnsList[1] != nullptr) { + NMLOG_DEBUG("Secondary DNS: %s ", dnsList[1]); + } else { + NMLOG_DEBUG("Secondary DNS: Not available"); + } + } else { + NMLOG_ERROR("Failed to parse DNS addresses"); + } + } else { + dnsList = nullptr; + std::cerr << "Failed to find DNS addresses" << std::endl; + } + } + else + { + NMLOG_ERROR("Failed to get Options property for DHCP6"); + g_object_unref(dhcpv6Proxy); + } + } + result.ipversion = "IPv6"; + } + if(addressStr.empty()) + { + result.autoconfig = true; + if(ipversion.empty()) + result.ipversion = "IPv4"; + } + std::string connectionPath; + if (!GnomeUtils::getSettingsConnectionPath(m_dbus, connectionPath, interface)) + { + NMLOG_ERROR("Error: connection path not found for interface %s", interface.c_str()); + return false; + } + + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath.c_str()); + + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating connection settings proxy"); + return false; + } + + GVariant *connectionSettings = g_dbus_proxy_call_sync( + settingsProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (connectionSettings == nullptr) { + NMLOG_ERROR("Error retrieving connection settings: %s", error->message); + g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *iterator; + GVariant *settingsDict; + const gchar *settingsKey; + + g_variant_get(connectionSettings, "(a{sa{sv}})", &iterator); + while (g_variant_iter_loop(iterator, "{&s@a{sv}}", &settingsKey, &settingsDict)) { + GVariantIter settingsIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&settingsIter, settingsDict); + while (g_variant_iter_loop(&settingsIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "method") == 0) { + if(g_strcmp0(settingsKey, "ipv4") == 0) + { + IPv4Method = g_variant_get_string(value, NULL); + NMLOG_DEBUG("IPV4 Method: %s\n", IPv4Method); + } + else if(g_strcmp0(settingsKey, "ipv6") == 0) + { + IPv6Method = g_variant_get_string(value, NULL); + NMLOG_DEBUG("IPV6 Method: %s\n", IPv6Method); + } + } + } + } + + result.autoconfig = true; + if(g_strcmp0(ipversion.c_str(), "IPv4") == 0) + result.autoconfig = (g_strcmp0(IPv4Method, "auto") == 0); + else if(g_strcmp0(ipversion.c_str(), "IPv6") == 0) + result.autoconfig = (g_strcmp0(IPv6Method, "auto") == 0); + result.ipaddress = addressStr; + result.prefix = prefix; + result.ula = ""; + result.dhcpserver = (dhcpServerIp != NULL) ? std::string(dhcpServerIp) : ""; + result.gateway = (gatewayIp != NULL) ? std::string(gatewayIp) : ""; + if (dnsList != NULL) + { + result.primarydns = (dnsList[0] != NULL) ? std::string(dnsList[0]) : ""; + result.secondarydns = (dnsList[1] != NULL) ? std::string(dnsList[1]) : ""; + } + else + { + result.primarydns = ""; + result.secondarydns = ""; + } + + // Cache the IP address + if(!result.ipaddress.empty()) + { + if(g_strcmp0(result.ipversion.c_str(), "IPv4") == 0) + { + if(ethname == interface) + _instance->m_ethIPv4Address = result; + else if(wifiname == interface) + _instance->m_wlanIPv4Address = result; + } + else if(g_strcmp0(result.ipversion.c_str(), "IPv6") == 0) + { + if(ethname == interface) + _instance->m_ethIPv6Address = result; + else if(wifiname == interface) + _instance->m_wlanIPv6Address = result; + } + } + + if(ipv4Proxy != NULL) + g_object_unref(ipv4Proxy); + if(ipv6Proxy != NULL) + g_object_unref(ipv6Proxy); + g_strfreev(dnsList); + g_free(dnsAddresses); + g_free(dhcpServerIp); + + return true; + } + + static bool getSSIDFromConnection(DbusMgr &m_dbus, const std::string connPath, std::string& ssid) + { + GError *error = NULL; + GDBusProxy *ConnProxy = NULL; + GVariant *settingsProxy= NULL, *connection= NULL, *gVarConn= NULL; + bool ret = false; + + ConnProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connPath.c_str()); + if(ConnProxy == NULL) + return false; + + settingsProxy = g_dbus_proxy_call_sync(ConnProxy, "GetSettings", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (!settingsProxy) { + g_dbus_error_strip_remote_error(error); + NMLOG_ERROR("Failed to get connection settings: %s", error->message); + g_error_free(error); + g_object_unref(ConnProxy); + return false; + } + + g_variant_get(settingsProxy, "(@a{sa{sv}})", &connection); + gVarConn = g_variant_lookup_value(connection, "connection", NULL); + + if(gVarConn == NULL) { + NMLOG_ERROR("connection Gvarient Error"); + g_variant_unref(settingsProxy); + g_object_unref(ConnProxy); + return false; + } + + if(gVarConn) + { + const char *connTyp = NULL; + const char *interfaceName = NULL; + G_VARIANT_LOOKUP(gVarConn, "type", "&s", &connTyp); + G_VARIANT_LOOKUP(gVarConn, "interface-name", "&s", &interfaceName); + if((strcmp(connTyp, "802-11-wireless") == 0) && strcmp(interfaceName, GnomeUtils::getWifiIfname()) == 0 ) + { + // 802-11-wireless.ssid: + GVariant *setting = NULL; + setting = g_variant_lookup_value(connection, "802-11-wireless", NULL); + if(setting) + { + GVariantIter iter; + GVariant *value = NULL; + const char *propertyName; + g_variant_iter_init(&iter, setting); + while (g_variant_iter_next(&iter, "{&sv}", &propertyName, &value)) { + if (strcmp(propertyName, "ssid") == 0) { + // Decode SSID from GVariant of type "ay" + gsize ssid_length = 0; + const guchar *ssid_data = static_cast(g_variant_get_fixed_array(value, &ssid_length, sizeof(guchar))); + if (ssid_data && ssid_length > 0 && ssid_length <= 32) { + ssid.assign(reinterpret_cast(ssid_data), ssid_length); + //NMLOG_DEBUG("SSID: %s", ssid.c_str()); + } else { + NMLOG_ERROR("Invalid SSID length: %zu (maximum is 32)", ssid_length); + ssid.empty(); + } + } + if(value) { + g_variant_unref(value); + value = NULL; + } + } + g_variant_unref(setting); + setting = NULL; + } + + if(!ssid.empty()) + { + // 802-11-wireless-security.key-mgmt: wpa-psk + const char* keyMgmt = NULL; + setting = g_variant_lookup_value(connection, "802-11-wireless-security", NULL); + if(setting != NULL) + { + G_VARIANT_LOOKUP(setting, "key-mgmt", "&s", &keyMgmt); + if(keyMgmt != NULL) + NMLOG_DEBUG("ssid: %s key-mgmt: %s", ssid.c_str(), keyMgmt); + g_variant_unref(setting); + setting = NULL; + } + ret = true; + } + } + else + ret = false; + g_variant_unref(gVarConn); + } + + if (connection) + g_variant_unref(connection); + if (settingsProxy) + g_variant_unref(settingsProxy); + g_object_unref(ConnProxy); + + return ret; + } + + static bool deleteConnection(DbusMgr& m_dbus, const std::string& path, std::string& ssid) + { + GError *error = NULL; + GDBusProxy *ConnProxy = NULL; + GVariant *deleteVar= NULL; + + ConnProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(path.c_str()); + if(ConnProxy == NULL) + return false; + + deleteVar = g_dbus_proxy_call_sync(ConnProxy, "Delete", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (!deleteVar) { + g_dbus_error_strip_remote_error(error); + NMLOG_ERROR("Failed to get connection settings: %s", error->message); + g_error_free(error); + g_object_unref(ConnProxy); + return false; + } + else + NMLOG_INFO("connection '%s' Removed path: %s", ssid.c_str(), path.c_str()); + + if (deleteVar) + g_variant_unref(deleteVar); + g_object_unref(ConnProxy); + + return true; + } + + bool NetworkManagerClient::getKnownSSIDs(std::list& ssids) + { + std::list paths; + if(!GnomeUtils::getConnectionPaths(m_dbus, paths)) + { + NMLOG_ERROR("Connection path fetch failed"); + return false; + } + + for (const std::string& path : paths) { + // NMLOG_DEBUG("connection path %s", path.c_str()); + std::string ssid; + if(getSSIDFromConnection(m_dbus, path, ssid) && !ssid.empty()) + ssids.push_back(ssid); + } + + if(ssids.empty()) + { + NMLOG_WARNING("no Known SSID list empty"); + return false; + } + return true; + } + + bool NetworkManagerClient::getConnectedSSID(Exchange::INetworkManager::WiFiSSIDInfo &ssidinfo) + { + GDBusProxy* wProxy = NULL; + GVariant* result = NULL; + gchar *activeApPath = NULL; + bool ret = false; + deviceInfo devInfo{}; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devInfo)) + return false; + + if(devInfo.path.empty() || devInfo.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_ERROR("access point state not valid %d", devInfo.state); + return false; + } + + wProxy = m_dbus.getNetworkManagerWirelessProxy(devInfo.path.c_str()); + if(wProxy == NULL) + return false; + + result = g_dbus_proxy_get_cached_property(wProxy, "ActiveAccessPoint"); + if (!result) { + NMLOG_ERROR("Failed to get ActiveAccessPoint property."); + g_object_unref(wProxy); + return false; + } + + g_variant_get(result, "o", &activeApPath); + + if(activeApPath != NULL && g_strcmp0(activeApPath, "/") != 0) + { + //NMLOG_DEBUG("ActiveAccessPoint property path %s", activeApPath); + if(GnomeUtils::getApDetails(m_dbus, activeApPath, ssidinfo)) + { + ret = true; + } + } + else + NMLOG_WARNING("active access point not found"); + + if(activeApPath != NULL) + g_free(activeApPath); + g_variant_unref(result); + g_object_unref(wProxy); + return ret; + } + + bool NetworkManagerClient::getMatchingSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo& ssidInfo, std::string& apPathStr) + { + GError* error = nullptr; + GDBusProxy* wProxy = nullptr; + deviceInfo devProperty{}; + Exchange::INetworkManager::WiFiSSIDInfo apInfo{}; + bool ret = false; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devProperty)) + return false; + + if(devProperty.path.empty() || devProperty.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_ERROR("no wifi device found"); + return false; + } + + wProxy = m_dbus.getNetworkManagerWirelessProxy(devProperty.path.c_str()); + if (wProxy == NULL) + return false; + + GVariant* result = g_dbus_proxy_call_sync(wProxy, "GetAllAccessPoints", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (error) { + NMLOG_ERROR("Error creating proxy: %s", error->message); + g_error_free(error); + g_object_unref(wProxy); + return false; + } + + GVariantIter* iter = NULL; + const gchar* apPath = NULL; + g_variant_get(result, "(ao)", &iter); + + while (g_variant_iter_loop(iter, "o", &apPath)) { + if(apPath == NULL) + continue; + // NMLOG_DEBUG("Access Point Path: %s", apPath); + if(GnomeUtils::getApDetails(m_dbus, apPath, apInfo)) + { + if(ssidInfo.ssid == apInfo.ssid) + { + ssidInfo = apInfo; + apPathStr = apPath; + ret = true; + break; + } + } + } + + g_variant_iter_free(iter); + g_variant_unref(result); + g_object_unref(wProxy); + + return ret; + } + + bool NetworkManagerClient::startWifiScan(const std::string ssid) + { + GError* error = NULL; + GDBusProxy* wProxy = NULL; + deviceInfo devInfo; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devInfo)) + return false; + + if(devInfo.path.empty() || devInfo.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_WARNING("access point not active"); + return false; + } + + wProxy = m_dbus.getNetworkManagerWirelessProxy(devInfo.path.c_str()); + if(wProxy == NULL) + return false; + + GVariant* timeVariant = NULL; + timeVariant = g_dbus_proxy_get_cached_property(wProxy, "LastScan"); + if (!timeVariant) { + NMLOG_ERROR("Failed to get LastScan properties"); + g_object_unref(wProxy); + return false; + } + + if (g_variant_is_of_type (timeVariant, G_VARIANT_TYPE_INT64)) { + gint64 timestamp; + timestamp = g_variant_get_int64 (timeVariant); + NMLOG_DEBUG("Last scan timestamp: %lld", static_cast(timestamp)); + } else { + g_warning("Unexpected variant type: %s", g_variant_get_type_string (timeVariant)); + } + + if (!ssid.empty()) + { + GVariantBuilder builder; + NMLOG_INFO("Starting WiFi scanning with SSID: %s", ssid.c_str()); + GVariantBuilder ssidArray; + g_variant_builder_init(&ssidArray, G_VARIANT_TYPE("aay")); + g_variant_builder_add_value( + &ssidArray, g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (const guint8 *)ssid.c_str(), ssid.length(),1) + ); + /* ssid GVariant = [['S', 'S', 'I', 'D']] */ + g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add(&builder, "{sv}", "ssids", g_variant_builder_end(&ssidArray)); + g_dbus_proxy_call_sync(wProxy, "RequestScan", g_variant_new("(a{sv})", builder), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + } + + else { + g_dbus_proxy_call_sync(wProxy, "RequestScan", g_variant_new("(a{sv})", NULL), + G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + } + + if (error) + { + NMLOG_ERROR("Error RequestScan: %s", error->message); + g_error_free(error); + g_object_unref(wProxy); + return false; + } + + g_object_unref(wProxy); + return true; + } + + bool NetworkManagerClient::isWifiScannedRecently(int timelimitInSec) + { + deviceInfo devInfo; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devInfo)) + { + NMLOG_ERROR("Failed to get WiFi device info"); + return false; + } + + if(devInfo.path.empty() || devInfo.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_ERROR("WiFi device not available for scan check"); + return false; + } + + GDBusProxy* wProxy = m_dbus.getNetworkManagerWirelessProxy(devInfo.path.c_str()); + if(wProxy == NULL) + { + NMLOG_ERROR("Failed to create wireless proxy for scan check"); + return false; + } + + // Get LastScan property + GVariant* timeVariant = g_dbus_proxy_get_cached_property(wProxy, "LastScan"); + if (!timeVariant) { + NMLOG_ERROR("Failed to get LastScan property"); + g_object_unref(wProxy); + return false; + } + + if (!g_variant_is_of_type(timeVariant, G_VARIANT_TYPE_INT64)) { + NMLOG_ERROR("Unexpected LastScan variant type: %s", g_variant_get_type_string(timeVariant)); + g_variant_unref(timeVariant); + g_object_unref(wProxy); + return false; + } + + gint64 last_scan_time = g_variant_get_int64(timeVariant); + g_variant_unref(timeVariant); + g_object_unref(wProxy); + + if (last_scan_time <= 0) { + NMLOG_INFO("No scan has been performed yet"); + return false; + } + + // Get current time in milliseconds + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + gint64 current_time_in_msec = (gint64)ts.tv_sec * 1000 + (gint64)ts.tv_nsec / 1000000; + + gint64 time_difference_in_seconds = (current_time_in_msec - last_scan_time) / 1000; + + NMLOG_DEBUG("Last scan: %lld ms ago, limit: %d sec", (long long)time_difference_in_seconds, timelimitInSec); + + if (time_difference_in_seconds <= timelimitInSec) { + NMLOG_DEBUG("WiFi was scanned recently (%lld seconds ago), skipping scan", (long long)time_difference_in_seconds); + return true; + } + + NMLOG_DEBUG("WiFi scan is needed, last scan was %lld seconds ago", (long long)time_difference_in_seconds); + return false; + } + + bool NetworkManagerClient::getDeviceInfo(const std::string& interface, deviceInfo& devInfo) + { + return GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo); + } + + bool updateConnctionAndactivate(DbusMgr& m_dbus, GVariantBuilder& connBuilder, const char* devicePath, const char* connPath) + { + GDBusProxy* proxy = nullptr; + GError* error = nullptr; + GVariant* result = nullptr; + + proxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connPath); + if(proxy == NULL) + return false; + + result = g_dbus_proxy_call_sync (proxy, "Update", + g_variant_new("(a{sa{sv}})", connBuilder), + G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error); + + if (error) { + NMLOG_ERROR("Failed to call Update : %s", error->message); + g_clear_error(&error); + g_object_unref(proxy); + return false; + } + + if(result) + g_variant_unref(result); + g_object_unref(proxy); + + proxy = m_dbus.getNetworkManagerProxy(); + if(proxy == NULL) + return false; + + if (error) { + NMLOG_ERROR( "Failed to create proxy: %s", error->message); + g_clear_error(&error); + return false; + } + const char* specificObject = "/"; + + result = g_dbus_proxy_call_sync (proxy, "ActivateConnection", + g_variant_new("(ooo)", connPath, devicePath, specificObject), + G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error); + + if (error) { + NMLOG_ERROR("Failed to call ActivateConnection: %s", error->message); + g_clear_error(&error); + g_object_unref(proxy); + return false; + } + + if(result) { + GVariant* activeConnVariant; + g_variant_get(result, "(@o)", &activeConnVariant); + const char* activeConnPath = g_variant_get_string(activeConnVariant, nullptr); + NMLOG_DEBUG("connPath %s; activeconn %s", connPath, activeConnPath); + g_variant_unref(activeConnVariant); + } + + if(result) + g_variant_unref(result); + if(proxy) + g_object_unref(proxy); + + return true; + } + + bool addNewConnctionAndactivate(DbusMgr& m_dbus, GVariantBuilder& connBuilder, const char* devicePath, bool persist, const char* specificObject) + { + GDBusProxy* proxy = nullptr; + GError* error = nullptr; + GVariant* result = nullptr; + + proxy = m_dbus.getNetworkManagerProxy(); + if(proxy == NULL) + return false; + + GVariant *connBuilderVariant = g_variant_builder_end(&connBuilder); + if(connBuilderVariant == NULL) + { + NMLOG_ERROR("Failed to build connection settings"); + g_object_unref(proxy); + return false; + } + + GVariantBuilder optionsBuilder; + g_variant_builder_init (&optionsBuilder, G_VARIANT_TYPE ("a{sv}")); + if(!persist) // by default it will be in disk mode + { + NMLOG_WARNING("wifi connection will not persist to the disk"); + g_variant_builder_add(&optionsBuilder, "{sv}", "persist", g_variant_new_string("volatile")); + } + //else + //g_variant_builder_add(&optionsBuilder, "{sv}", "persist", g_variant_new_string("disk")); + + GVariant *optionBuilderVariant = g_variant_builder_end(&optionsBuilder); + if(optionBuilderVariant == NULL) + { + NMLOG_ERROR("Failed to build options settings"); + g_object_unref(proxy); + return false; + } + + NMLOG_DEBUG("devicePath %s, specificObject %s", devicePath, specificObject); + result = g_dbus_proxy_call_sync (proxy, "AddAndActivateConnection2", + g_variant_new("(@a{sa{sv}}oo@a{sv})", connBuilderVariant, devicePath?: "/", specificObject?: "/", optionBuilderVariant), + G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error); + + if (result == NULL) { + if(error != NULL) + { + NMLOG_ERROR("Failed to call AddAndActivateConnection2: %s", error->message); + g_clear_error(&error); + } + g_object_unref(proxy); + return false; + } + + NMLOG_DEBUG("AddAndActivateConnection2 success !!!"); + + GVariant* pathVariant = NULL; + GVariant* activeConnVariant = NULL; + GVariant* resultDict = NULL; + + g_variant_get(result, "(@o@o@a{sv})", &pathVariant, &activeConnVariant, &resultDict); + + // Extract connection and active connection paths + const char* newConnPath = g_variant_get_string(pathVariant, nullptr); + const char* activeConnPath = g_variant_get_string(activeConnVariant, nullptr); + + NMLOG_INFO("newconn %s; activeconn %s",newConnPath, activeConnPath); + if(pathVariant) + g_variant_unref(pathVariant); + if(activeConnVariant) + g_variant_unref(activeConnVariant); + if(resultDict) + g_variant_unref(resultDict); + if(result) + g_variant_unref(result); + if(proxy) + g_object_unref(proxy); + return true; + } + + static inline std::string generateUUID() + { + uuid_t uuid{}; + char buffer[37] = {0}; // 36 + NULL + uuid_generate_random(uuid); + uuid_unparse_lower(uuid, buffer); + if(buffer[0] != '\0') + return std::string(buffer); + return std::string(""); + } + + static bool connectionBuilder(const Exchange::INetworkManager::WiFiConnectTo& ssidinfo, GVariantBuilder& connBuilder, bool iswpsAP = false) + { + GVariantBuilder settingsBuilder; + + g_variant_builder_init (&connBuilder, G_VARIANT_TYPE ("a{sa{sv}}")); + if(ssidinfo.ssid.empty() || ssidinfo.ssid.length() > 32) + { + NMLOG_WARNING("ssid name is missing or invalied"); + return false; + } + + NMLOG_INFO("ssid %s, security %d, persist %d", ssidinfo.ssid.c_str(), (int)ssidinfo.security, (int)ssidinfo.persist); + + /* Adding 'connection' settings */ + g_variant_builder_init (&settingsBuilder, G_VARIANT_TYPE ("a{sv}")); + std::string uuid = generateUUID(); + if(uuid.empty() || uuid.length() < 32) + { + NMLOG_ERROR("uuid generate failed"); + return false; + } + g_variant_builder_add (&settingsBuilder, "{sv}", "uuid", g_variant_new_string (uuid.c_str())); + g_variant_builder_add (&settingsBuilder, "{sv}", "id", g_variant_new_string (ssidinfo.ssid.c_str())); // connection id = ssid name + g_variant_builder_add (&settingsBuilder, "{sv}", "interface-name", g_variant_new_string (GnomeUtils::getWifiIfname())); + g_variant_builder_add (&settingsBuilder, "{sv}", "type", g_variant_new_string ("802-11-wireless")); + g_variant_builder_add (&connBuilder, "{sa{sv}}", "connection", &settingsBuilder); + /* Adding '802-11-wireless 'settings */ + g_variant_builder_init (&settingsBuilder, G_VARIANT_TYPE ("a{sv}")); + GVariant *ssidArray = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (const guint8 *)ssidinfo.ssid.c_str(), ssidinfo.ssid.length(), 1); + g_variant_builder_add (&settingsBuilder, "{sv}", "ssid", ssidArray); + g_variant_builder_add (&settingsBuilder, "{sv}", "mode", g_variant_new_string("infrastructure")); + if(!iswpsAP) // wps never be a hidden AP, it will be always visible + g_variant_builder_add (&settingsBuilder, "{sv}", "hidden", g_variant_new_boolean(true)); // set hidden: yes + g_variant_builder_add (&connBuilder, "{sa{sv}}", "802-11-wireless", &settingsBuilder); + + /* Adding '802-11-wireless-security' settings */ + g_variant_builder_init (&settingsBuilder, G_VARIANT_TYPE ("a{sv}")); + + switch(ssidinfo.security) + { + case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK: + case Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE: + { + if(Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE == ssidinfo.security) + { + NMLOG_DEBUG("802-11-wireless-security key-mgmt: 'sae'"); + g_variant_builder_add (&settingsBuilder, "{sv}", "key-mgmt", g_variant_new_string("sae")); + } + else + { + NMLOG_DEBUG("802-11-wireless-security key-mgmt: 'wpa-psk'"); + g_variant_builder_add (&settingsBuilder, "{sv}", "key-mgmt", g_variant_new_string("wpa-psk")); // WPA + WPA2 + WPA3 personal + } + + /* if ap is not a wps network */ + if(!iswpsAP) + { + if(ssidinfo.passphrase.empty() || ssidinfo.passphrase.length() < 8) + { + NMLOG_WARNING("wifi securtity type password erro length > 8"); + GVariant *sVariant = g_variant_builder_end(&settingsBuilder); + if(sVariant) + g_variant_unref(sVariant); + return false; + } + g_variant_builder_add (&settingsBuilder, "{sv}", "psk", g_variant_new_string(ssidinfo.passphrase.c_str())); // password + } + break; + } + case Exchange::INetworkManager::WIFI_SECURITY_NONE: + { + NMLOG_DEBUG("802-11-wireless-security key-mgmt: 'none'"); + // g_variant_builder_add (&settingsBuilder, "{sv}", "key-mgmt", g_variant_new_string("none")); // no password protection + break; + } + default: + { + // ToDo handile Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_EAP + NMLOG_WARNING("connection wifi securtity type not supported %d", ssidinfo.security); + GVariant *sVariant = g_variant_builder_end(&settingsBuilder); + if(sVariant) + g_variant_unref(sVariant); + return false; + } + } + + g_variant_builder_add (&connBuilder, "{sa{sv}}", "802-11-wireless-security", &settingsBuilder); + + // Get persistent hostname or fall back to device hostname + std::string hostname; + if (!GnomeUtils::readPersistentHostname(hostname)) { + const char* deviceHostname = "rdk-device"; // default hostname + hostname = deviceHostname; + NMLOG_DEBUG("No persistent hostname found, using device hostname: %s", hostname.c_str()); + } + NMLOG_INFO("DHCP hostname: %s", hostname.c_str()); + + /* Adding the 'ipv4' Setting */ + g_variant_builder_init (&settingsBuilder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&settingsBuilder, "{sv}", "method", g_variant_new_string ("auto")); + g_variant_builder_add (&settingsBuilder, "{sv}", "dhcp-hostname", g_variant_new_string(hostname.c_str())); + g_variant_builder_add (&settingsBuilder, "{sv}", "dhcp-send-hostname", g_variant_new_boolean(TRUE)); + g_variant_builder_add (&connBuilder, "{sa{sv}}", "ipv4", &settingsBuilder); + + /* Adding the 'ipv6' Setting */ + g_variant_builder_init (&settingsBuilder, G_VARIANT_TYPE ("a{sv}")); + g_variant_builder_add (&settingsBuilder, "{sv}", "method", g_variant_new_string ("auto")); + g_variant_builder_add (&settingsBuilder, "{sv}", "dhcp-hostname", g_variant_new_string(hostname.c_str())); + g_variant_builder_add (&settingsBuilder, "{sv}", "dhcp-send-hostname", g_variant_new_boolean(TRUE)); + g_variant_builder_add (&connBuilder, "{sa{sv}}", "ipv6", &settingsBuilder); + NMLOG_DEBUG("connection builder success..."); + return true; + } + + bool NetworkManagerClient::addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo& ssidinfo) + { + GVariantBuilder connBuilder; + bool ret = false, reuseConnection = false; + GVariant *result = NULL; + GError* error = NULL; + const char *newConPath = NULL; + std::string exsistingConn; + deviceInfo deviceProp; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), deviceProp)) + return false; + + if(deviceProp.path.empty() || deviceProp.state == NM_DEVICE_STATE_UNKNOWN) + { + NMLOG_WARNING("wlan0 interface not active"); + return false; + } + + std::list paths{}; + if(GnomeUtils::getWifiConnectionPaths(m_dbus, deviceProp.path.c_str(), paths)) + { + for (const std::string& path : paths) + { + std::string ssid{}; + if(getSSIDFromConnection(m_dbus, path, ssid) && ssid == ssidinfo.ssid) + { + exsistingConn = path; + NMLOG_DEBUG("same connection exsist (%s) updating ...", exsistingConn.c_str()); + reuseConnection = true; + break; // only one connection will be activate (same property only) + } + } + } + + if(!connectionBuilder(ssidinfo, connBuilder)) + { + NMLOG_WARNING("connection builder failed"); + GVariant *variant = g_variant_builder_end(&connBuilder); + if(variant) + g_variant_unref(variant); + return false; + } + + if(reuseConnection) + { + GDBusProxy* proxy = m_dbus.getNetworkManagerSettingsConnectionProxy(exsistingConn.c_str()); + + if(proxy != nullptr) + { + result = g_dbus_proxy_call_sync (proxy, "Update", + g_variant_new("(a{sa{sv}})", connBuilder), + G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error); + + if (error == nullptr) { + NMLOG_DEBUG("same connection updated success : %s", exsistingConn.c_str()); + ret = true; + } + else { + NMLOG_ERROR("Failed to call Update : %s", error->message); + g_clear_error(&error); + } + if(result) + g_variant_unref (result); + g_object_unref(proxy); + } + } + else + { + GDBusProxy *proxy = NULL; + proxy = m_dbus.getNetworkManagerSettingsProxy(); + if (proxy != nullptr) + { + result = g_dbus_proxy_call_sync (proxy, "AddConnection", + g_variant_new ("(a{sa{sv}})", &connBuilder), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + + if (error != nullptr) { + g_dbus_error_strip_remote_error (error); + NMLOG_ERROR ("Could not create NetworkManager/Settings proxy: %s", error->message); + g_error_free (error); + g_object_unref(proxy); + return false; + } + + if (result) { + g_variant_get (result, "(&o)", &newConPath); + NMLOG_INFO("Added a new connection: %s", newConPath); + ret = true; + g_variant_unref (result); + } else { + g_dbus_error_strip_remote_error (error); + NMLOG_ERROR("Error adding connection: %s", error->message); + g_clear_error (&error); + } + g_object_unref(proxy); + } + } + + return ret; + } + + bool NetworkManagerClient::removeKnownSSIDs(const std::string& ssid) + { + bool ret = false; + bool ssidSpecified = !ssid.empty(); + bool connectionFound = false; + + if(!ssidSpecified) + NMLOG_WARNING("ssid is not specified, Deleting all available wifi connections!"); + + std::list paths; + if(!GnomeUtils::getConnectionPaths(m_dbus, paths)) + { + NMLOG_ERROR("remove connection path fetch failed"); + return false; + } + + for (const std::string& path : paths) { + // NMLOG_DEBUG("remove connection path %s", path.c_str()); + std::string connSsid; + if(getSSIDFromConnection(m_dbus, path, connSsid)) { + // If SSID is specified, only delete matching connections + // If SSID is empty, delete all wireless connections + if (!ssidSpecified || connSsid == ssid) { + if(deleteConnection(m_dbus, path, connSsid)) { + NMLOG_INFO("delete '%s' connection ...", connSsid.c_str()); + connectionFound = true; + ret = true; + } + } + } + } + + if(!connectionFound) + { + if(ssidSpecified) + NMLOG_WARNING("'%s' no such connection profile", ssid.c_str()); + else + NMLOG_WARNING("No wifi connection profiles found!"); + } + + return ret; + } + + bool NetworkManagerClient::getWiFiSignalQuality(string& ssid, int& signalStrength, Exchange::INetworkManager::WiFiSignalQuality& quality) + { + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + float rssi = 0.0f; + float noise = 0.0f; + float floatSignalStrength = 0.0f; + unsigned int signalStrengthOut = 0; + + if(!getConnectedSSID(ssidInfo)) + { + NMLOG_ERROR("no wifi connected"); + return false; + } + else + { + ssid = ssidInfo.ssid; + if (ssidInfo.strength) + rssi = ssidInfo.strength; + if (ssidInfo.noise) + noise = ssidInfo.noise; + floatSignalStrength = (rssi - noise); + if (floatSignalStrength < 0) + floatSignalStrength = 0.0; + + signalStrengthOut = static_cast(floatSignalStrength); + NMLOG_INFO ("WiFiSignalQuality in dB = %u",signalStrengthOut); + + if (signalStrengthOut == 0) + { + quality = Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_DISCONNECTED; + signalStrength = 0; + } + else if (signalStrengthOut > 0 && signalStrengthOut < NM_WIFI_SNR_THRESHOLD_FAIR) + { + quality = Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_WEAK; + } + else if (signalStrengthOut > NM_WIFI_SNR_THRESHOLD_FAIR && signalStrengthOut < NM_WIFI_SNR_THRESHOLD_GOOD) + { + quality = Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_FAIR; + } + else if (signalStrengthOut > NM_WIFI_SNR_THRESHOLD_GOOD && signalStrengthOut < NM_WIFI_SNR_THRESHOLD_EXCELLENT) + { + quality = Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_GOOD; + } + else + { + quality = Exchange::INetworkManager::WiFiSignalQuality::WIFI_SIGNAL_EXCELLENT; + } + + signalStrength = signalStrengthOut; + + NMLOG_INFO("strength success %d dBm", signalStrength); + } + + return true; + } + + bool NetworkManagerClient::getWifiState(Exchange::INetworkManager::WiFiState &state) + { + deviceInfo deviceProp; + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_UNINSTALLED; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), deviceProp)) + return false; + + // Todo check NMDeviceStateReason for more information + switch(deviceProp.state) + { + case NM_DEVICE_STATE_ACTIVATED: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED; + break; + case NM_DEVICE_STATE_PREPARE: + case NM_DEVICE_STATE_CONFIG: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_PAIRING; + break; + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_IP_CONFIG: + case NM_DEVICE_STATE_IP_CHECK: + case NM_DEVICE_STATE_SECONDARIES: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTING; + break; + case NM_DEVICE_STATE_DEACTIVATING: + case NM_DEVICE_STATE_FAILED: + case NM_DEVICE_STATE_DISCONNECTED: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED; + break; + default: + state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISABLED; + } + NMLOG_DEBUG("networkmanager wifi state (%d) mapped state (%d) ", (int)deviceProp.state, (int)state); + return true; + } + + bool NetworkManagerClient::wifiConnect(const Exchange::INetworkManager::WiFiConnectTo& connectInfo, bool iswpsAP) + { + // TODO check sudo nmcli device wifi connect HomeNet password rafi@123 + // Error: Connection activation failed: Device disconnected by user or client error ? + GVariantBuilder connBuilder; + bool reuseConnection = false; + deviceInfo deviceProp; + std::string exsistingConn; + Exchange::INetworkManager::WiFiConnectTo ssidinfo = connectInfo; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), deviceProp)) + return false; + + if(deviceProp.path.empty() || deviceProp.state < NM_DEVICE_STATE_UNAVAILABLE) + { + NMLOG_ERROR("wlan0 interface not active"); + return false; + } + + // Check if already connected to the requested SSID + Exchange::INetworkManager::WiFiSSIDInfo currentSSID; + if(getConnectedSSID(currentSSID)) + { + if(ssidinfo.ssid == currentSSID.ssid) + { + NMLOG_INFO("'%s' already connected !", currentSSID.ssid.c_str()); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, currentSSID.ssid); + return true; + } + else + NMLOG_DEBUG("wifi already connected with %s AP", currentSSID.ssid.c_str()); + } + + std::list paths; + if(GnomeUtils::getWifiConnectionPaths(m_dbus, deviceProp.path.c_str(), paths)) + { + for (const std::string& path : paths) { + std::string ssid; + if(getSSIDFromConnection(m_dbus, path, ssid) && ssid == ssidinfo.ssid) + { + exsistingConn = path; + NMLOG_WARNING("same connection exsist (%s)", exsistingConn.c_str()); + reuseConnection = true; + break; // only one connection will be activate (same property only) + } + } + } + + /* if ap is available check the security is matching to user requested */ + Exchange::INetworkManager::WiFiSSIDInfo apInfo{}; + apInfo.ssid = ssidinfo.ssid; + std::string apPathStr = "/"; // default specific object path is "/" + if(getMatchingSSIDInfo(apInfo, apPathStr)) + { + if(ssidinfo.security != apInfo.security) + { + NMLOG_WARNING("user requested wifi security '%d' != AP supported security %d ", ssidinfo.security, apInfo.security); + ssidinfo.security = apInfo.security; + // NMLOG_DEBUG("ap path %s", apPathStr.c_str()); + } + } + else + { + NMLOG_WARNING("matching ssid (%s) not found in scanning result", ssidinfo.ssid.c_str()); + /* ssid not found in scan list so add to known ssid it will do a scanning and connect */ + if(ssidinfo.persist) + { + if(addToKnownSSIDs(ssidinfo)) + { + NMLOG_DEBUG("Adding to known ssid '%s' ", ssidinfo.ssid.c_str()); + return activateKnownConnection(GnomeUtils::getWifiIfname(), ssidinfo.ssid); + } + else + { + NMLOG_ERROR("Failed to add SSID '%s' to known SSIDs", ssidinfo.ssid.c_str()); + return false; + } + } + else + { + NMLOG_WARNING("SSID '%s' not found and persist is false, cannot connect", ssidinfo.ssid.c_str()); + return false; + } + } + + if(reuseConnection) + { + NMLOG_INFO("activating connection..."); + if(!connectionBuilder(ssidinfo, connBuilder, iswpsAP)) { + NMLOG_WARNING("connection builder failed"); + return false; + } + + if (ssidinfo.persist) + { + if(updateConnctionAndactivate(m_dbus, connBuilder, deviceProp.path.c_str(), exsistingConn.c_str())) + NMLOG_INFO("updated connection request success"); + else + { + NMLOG_ERROR("wifi connect request failed"); + return false; + } + } + else + { + // Don't persist changes, just activate existing connection + // Note: Any changes made by connectionBuilder will be temporary for this session only + NMLOG_DEBUG("activating existing connection without persisting changes '%s'", ssidinfo.ssid.c_str()); + if(GnomeUtils::activateConnection(m_dbus, exsistingConn, deviceProp.path)) + NMLOG_INFO("activated existing connection without persistence"); + else { + NMLOG_ERROR("failed to activate existing connection"); + return false; + } + } + } + else + { + NMLOG_DEBUG("creating new connection '%s' persist=%d", ssidinfo.ssid.c_str(), ssidinfo.persist); + if(!connectionBuilder(ssidinfo, connBuilder, iswpsAP)) { + NMLOG_WARNING("connection builder failed"); + return false; + } + if (ssidinfo.persist) + { + if(addNewConnctionAndactivate(m_dbus, connBuilder, deviceProp.path.c_str(), ssidinfo.persist, apPathStr.c_str())) + NMLOG_INFO("wifi connect request success"); + else + { + NMLOG_ERROR("wifi connect request failed"); + return false; + } + } + else + { + // Create temporary connection - add to memory only, do not save to disk + NMLOG_DEBUG("creating temporary connection for '%s'", ssidinfo.ssid.c_str()); + if(addNewConnctionAndactivate(m_dbus, connBuilder, deviceProp.path.c_str(), ssidinfo.persist, apPathStr.c_str())) + NMLOG_INFO("temporary wifi connect request success"); + else { + NMLOG_ERROR("temporary wifi connect request failed"); + return false; + } + } + } + + return true; + } + + bool NetworkManagerClient::wifiDisconnect() + { + GError* error = NULL; + GDBusProxy* wProxy = NULL; + deviceInfo devInfo; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devInfo) || devInfo.path.empty()) + return false; + + NMLOG_DEBUG("wifi device current state is %d !", devInfo.state); + if(devInfo.state <= NM_DEVICE_STATE_DISCONNECTED || devInfo.state == NM_DEVICE_STATE_FAILED || devInfo.state == NM_DEVICE_STATE_DEACTIVATING) + { + NMLOG_WARNING("wifi in disconnected state"); + return true; + } + + wProxy = m_dbus.getNetworkManagerDeviceProxy(devInfo.path.c_str()); + if(wProxy == NULL) + return false; + else + g_dbus_proxy_call_sync(wProxy, "Disconnect", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + return true; + if (error) { + NMLOG_ERROR("Error calling Disconnect method: %s", error->message); + g_error_free(error); + g_object_unref(wProxy); + return false; + } + else + NMLOG_INFO("wifi disconnected success"); + + g_object_unref(wProxy); + return true; + } + + bool NetworkManagerClient::activateKnownConnection(const std::string& interface, const std::string& knownConnectionID) + { + NMLOG_DEBUG("Activating known connection: interface=%s, connectionID=%s", interface.c_str(), knownConnectionID.c_str()); + + deviceInfo devInfo; + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, interface.c_str(), devInfo)) + { + NMLOG_ERROR("Failed to get device info for interface: %s", interface.c_str()); + return false; + } + + if(devInfo.path.empty() || devInfo.state < NM_DEVICE_STATE_UNAVAILABLE) + { + NMLOG_ERROR("Interface %s not active or available", interface.c_str()); + return false; + } + + // Set autoconnect to true for the device + GDBusProxy *deviceProxy = m_dbus.getNetworkManagerDeviceProxy(devInfo.path.c_str()); + if (deviceProxy != nullptr) { + GVariant *autoconnectValue = g_variant_new_boolean(TRUE); + GError *setError = nullptr; + g_dbus_proxy_call_sync( + deviceProxy, + "Set", + g_variant_new("(ssv)", "org.freedesktop.NetworkManager.Device", "Autoconnect", autoconnectValue), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &setError); + + if (setError) { + NMLOG_WARNING("Failed to set autoconnect for device %s: %s", interface.c_str(), setError->message); + g_error_free(setError); + } else { + NMLOG_DEBUG("Set autoconnect=true for device %s", interface.c_str()); + } + g_object_unref(deviceProxy); + } + + // Get all connections + GError *error = nullptr; + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsProxy(); + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating NetworkManager settings proxy"); + return false; + } + + GVariant *connectionsResult = g_dbus_proxy_call_sync( + settingsProxy, + "ListConnections", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error || connectionsResult == nullptr) { + NMLOG_ERROR("Error retrieving connections: %s", error ? error->message : "unknown error"); + if (error) g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *connectionIter; + const gchar *connectionPath; + std::string foundConnectionPath; + std::string firstWiFiConnectionPath; + + g_variant_get(connectionsResult, "(ao)", &connectionIter); + + // Find the requested connection or first WiFi connection + while (g_variant_iter_loop(connectionIter, "o", &connectionPath)) { + std::string ssid; + if(getSSIDFromConnection(m_dbus, connectionPath, ssid)) + { + NMLOG_DEBUG("Found WiFi connection: %s (SSID: %s)", connectionPath, ssid.c_str()); + + // If no specific connection requested, use first WiFi connection + if(firstWiFiConnectionPath.empty()) + firstWiFiConnectionPath = connectionPath; + + // If specific connection requested, look for exact match + if(!knownConnectionID.empty() && ssid == knownConnectionID) + { + foundConnectionPath = connectionPath; + NMLOG_INFO("Found matching connection for SSID: %s", knownConnectionID.c_str()); + break; + } + } + } + + g_variant_iter_free(connectionIter); + g_variant_unref(connectionsResult); + g_object_unref(settingsProxy); + + // Determine which connection to activate + std::string connectionToActivate; + if(!foundConnectionPath.empty()) + connectionToActivate = foundConnectionPath; + else if(knownConnectionID.empty() && !firstWiFiConnectionPath.empty()) + { + connectionToActivate = firstWiFiConnectionPath; + NMLOG_INFO("No specific connection requested, using first WiFi connection"); + } + else + { + NMLOG_ERROR("No suitable connection found. Requested: '%s'", knownConnectionID.c_str()); + return false; + } + + // Activate the connection + if(GnomeUtils::activateConnection(m_dbus, connectionToActivate, devInfo.path)) + { + NMLOG_INFO("Successfully activated known connection: %s", connectionToActivate.c_str()); + return true; + } + else + { + NMLOG_ERROR("Failed to activate connection: %s", connectionToActivate.c_str()); + return false; + } + } + + static bool getWpsPbcSSIDDetails(DbusMgr& m_dbus, const char* apPath, std::string& wpsApSsid) + { + guint32 flags = 0; + bool ret = false; + GVariant* ssidVariant = NULL; + GDBusProxy* proxy = m_dbus.getNetworkManagerAccessPointProxy(apPath); + if (proxy == NULL) + return false; + + if (GnomeUtils::getCachedPropertyU(proxy, "Flags", &flags) && flags & NM_802_11_AP_FLAGS_WPS_PBC) + { + gsize ssid_length = 0; + ssidVariant = g_dbus_proxy_get_cached_property(proxy,"Ssid"); + if (!ssidVariant) { + NMLOG_ERROR("Failed to get AP ssid properties."); + g_object_unref(proxy); + return false; + } + + const guchar *ssid_data = static_cast(g_variant_get_fixed_array(ssidVariant, &ssid_length, sizeof(guchar))); + if (ssid_data && ssid_length > 0 && ssid_length <= 32) + { + GVariant* result = NULL; + gchar *_bssid = NULL; + wpsApSsid.assign(reinterpret_cast(ssid_data), ssid_length); + result = g_dbus_proxy_get_cached_property(proxy,"HwAddress"); + if (!result) { + NMLOG_ERROR("Failed to get AP properties."); + g_object_unref(proxy); + return false; + } + g_variant_get(result, "s", &_bssid); + if(_bssid != NULL) { + NMLOG_INFO("WPS PBC AP found ssid: %s, bssid: %s", wpsApSsid.c_str(), _bssid); + ret = true; + g_free(_bssid); + } + g_variant_unref(result); + } + else + NMLOG_ERROR("WPS PBC Ap ssid Error !"); + + if(ssidVariant) + g_variant_unref(ssidVariant); + } + + if(proxy) + g_object_unref(proxy); + + return ret; + } + + static bool findWpsPbcSSID(DbusMgr& m_dbus, std::string& wpsApSsid) + { + GError* error = nullptr; + GDBusProxy* wProxy = nullptr; + deviceInfo devProperty{}; + bool isfound = false; + + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devProperty)) + return false; + + if(devProperty.path.empty() || devProperty.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_ERROR("no wifi device found"); + return false; + } + + wProxy = m_dbus.getNetworkManagerWirelessProxy(devProperty.path.c_str()); + if (wProxy == NULL) + return false; + + GVariant* result = g_dbus_proxy_call_sync(wProxy, "GetAllAccessPoints", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (error) { + NMLOG_ERROR("Error creating proxy: %s", error->message); + g_error_free(error); + g_object_unref(wProxy); + return false; + } + + GVariantIter* iter = NULL; + const gchar* apPath = NULL; + g_variant_get(result, "(ao)", &iter); + + while (g_variant_iter_loop(iter, "o", &apPath)) + { + guint32 flags = 0; + if(apPath == NULL) + continue; + // NMLOG_DEBUG("Access Point Path: %s", apPath); + isfound = getWpsPbcSSIDDetails(m_dbus, apPath, wpsApSsid); + if(isfound) + break; + } + + if(!isfound) + NMLOG_WARNING("WPS PBC AP not found in scanned result !"); + + g_variant_iter_free(iter); + g_variant_unref(result); + g_object_unref(wProxy); + + return isfound; + } + + bool NetworkManagerClient::isActiveApSameAsWps(const std::string& wpsSSID) + { + deviceInfo devProperty{}; + GDBusProxy* wProxy = nullptr; + GVariant* result = nullptr; + bool isSame = false; + + // Get WiFi device info + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devProperty)) + return false; + + if(devProperty.path.empty() || devProperty.state < NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_ERROR("no wifi device found"); + return false; + } + + // Get WiFi device proxy + wProxy = m_dbus.getNetworkManagerWirelessProxy(devProperty.path.c_str()); + if (wProxy == NULL) + return false; + + // Get active access point + result = g_dbus_proxy_get_cached_property(wProxy, "ActiveAccessPoint"); + if (!result) { + NMLOG_DEBUG("No active access point found"); + g_object_unref(wProxy); + return false; + } + + const gchar* activeApPath = nullptr; + g_variant_get(result, "o", &activeApPath); + + if (activeApPath && g_strcmp0(activeApPath, "/") != 0) { + // Get AP proxy and check SSID + GDBusProxy* apProxy = m_dbus.getNetworkManagerAccessPointProxy(activeApPath); + if (apProxy) { + GVariant* ssidVariant = g_dbus_proxy_get_cached_property(apProxy, "Ssid"); + if (ssidVariant) { + gsize ssid_length = 0; + const guchar *ssid_data = static_cast(g_variant_get_fixed_array(ssidVariant, &ssid_length, sizeof(guchar))); + if (ssid_data && ssid_length > 0 && ssid_length <= 32) { + std::string activeSSID(reinterpret_cast(ssid_data), ssid_length); + if (activeSSID == wpsSSID) { + NMLOG_INFO("Active AP matches WPS SSID: %s", wpsSSID.c_str()); + isSame = true; + } + } + g_variant_unref(ssidVariant); + } + g_object_unref(apProxy); + } + } + + g_variant_unref(result); + g_object_unref(wProxy); + return isSame; + } + + void NetworkManagerClient::wpsProcess() + { + m_wpsProcessRun = true; + m_wpsActionTriggered = false; + Exchange::INetworkManager::WiFiConnectTo ssidinfo{}; + Exchange::INetworkManager::WiFiState state; + deviceInfo devProperty{}; + bool wpsComplete = false; + NMLOG_INFO("WPS process started !"); + + if(!getWifiState(state)) + { + NMLOG_ERROR("wifi state error ! wps process stoped !"); + m_wpsProcessRun = false; + return; + } + + for(int retry = 0; retry < GDBUS_WPS_RETRY_COUNT; retry++) + { + if(m_wpsProcessRun.load() == false) // stop wps process if requested + break; + sleep(GDBUS_WPS_RETRY_WAIT_IN_MS); + if(m_wpsProcessRun.load() == false) + break; + + + // Get current device info for state monitoring + if(!GnomeUtils::getDeviceInfoByIfname(m_dbus, GnomeUtils::getWifiIfname(), devProperty)) + { + NMLOG_ERROR("Failed to get device info during WPS process"); + break; + } + + if(m_wpsActionTriggered) + { + // If WPS action started, monitor device state for completion + if(devProperty.state <= NM_DEVICE_STATE_DISCONNECTED) + { + NMLOG_WARNING("WPS process failed - device in disconnected state"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, ssidinfo.ssid); + break; + } + else if(devProperty.state > NM_DEVICE_STATE_NEED_AUTH) + { + NMLOG_INFO("WPS process completed successfully"); + wpsComplete = true; + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, ssidinfo.ssid); + break; + } + + NMLOG_INFO("WPS process not completed yet, device state: %d", devProperty.state); + if(retry >= GDBUS_WPS_RETRY_COUNT - 1) // Last retry attempt + { + NMLOG_ERROR("WPS process failed - timeout"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED, ssidinfo.ssid); + break; + } + continue; + } + + // Look for WPS PBC SSID + if(!findWpsPbcSSID(m_dbus, ssidinfo.ssid)) + { + if(!m_wpsActionTriggered) + { + // If WPS action not triggered, do a scanning request + startWifiScan(); + } + NMLOG_DEBUG("WPS process retrying: %d ", retry+1); + continue; + } + + // Check if already connected to the same WPS AP + if(isActiveApSameAsWps(ssidinfo.ssid)) + { + NMLOG_INFO("WPS process stopped - already connected to WPS AP '%s'", ssidinfo.ssid.c_str()); + wpsComplete = true; + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTED, ssidinfo.ssid); + break; + } + + if(!getWifiState(state)) + { + NMLOG_ERROR("wifi state error ! wps process stoped !"); + m_wpsProcessRun = false; + return; + } + + if(Exchange::INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED != state) + { + NMLOG_INFO("Disconnecting current connection for WPS process"); + wifiDisconnect(); + sleep(3); // Wait for disconnect to complete + } + + ssidinfo.security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; + ssidinfo.persist = true; + + NMLOG_INFO("Starting WPS connection to SSID: %s", ssidinfo.ssid.c_str()); + + // Mark WPS action as triggered and adjust retry count + m_wpsActionTriggered = true; + retry = GDBUS_WPS_RETRY_COUNT - 3; // Expecting WPS process will be completed in 30 sec (3 * 10sec retry) + + /* security mode will be updated in wifi connect function, if not mathing to wpa-psk */ + wifiConnect(ssidinfo, true); // isWps = true + } + + // Final status reporting + if(!wpsComplete && !m_wpsActionTriggered) + { + NMLOG_WARNING("WPS AP not found"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, ssidinfo.ssid); + } + else if(!wpsComplete && m_wpsActionTriggered) + { + NMLOG_INFO("WPS process error"); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED, ssidinfo.ssid); + } + + NMLOG_INFO("WPS process thread exit"); + m_wpsProcessRun = false; + } + + bool NetworkManagerClient::startWPS() + { + NMLOG_DEBUG("Start WPS %s", __FUNCTION__); + if(m_wpsProcessRun.load()) + { + NMLOG_WARNING("wps process WPS already running"); + return true; + } + + m_secretAgent.RegisterAgent(); + m_wpsthread = std::thread(&NetworkManagerClient::wpsProcess, this); + m_wpsthread.detach(); + return true; + } + + bool NetworkManagerClient::stopWPS() { + NMLOG_DEBUG("Stop WPS %s", __FUNCTION__); + m_wpsProcessRun = false; + m_wpsActionTriggered = false; + return m_secretAgent.UnregisterAgent(); + } + + bool NetworkManagerClient::modifyDefaultConnectionsConfig() + { + NMLOG_DEBUG("Initializing hostname settings for existing connections"); + + // Get persistent hostname or use default + std::string hostname; + if (!GnomeUtils::readPersistentHostname(hostname)) { + const char* deviceHostname = "rdk-device"; // default hostname + hostname = deviceHostname; + NMLOG_DEBUG("No persistent hostname found, using device hostname: %s", hostname.c_str()); + } + + GError *error = nullptr; + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsProxy(); + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating NetworkManager settings proxy"); + return false; + } + + // Get all connections + GVariant *connectionsResult = g_dbus_proxy_call_sync( + settingsProxy, + "ListConnections", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error || connectionsResult == nullptr) { + NMLOG_ERROR("Error retrieving connections for hostname init: %s", error ? error->message : "unknown error"); + if (error) g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *connectionIter; + const gchar *connectionPath; + bool success = true; + + g_variant_get(connectionsResult, "(ao)", &connectionIter); + + while (g_variant_iter_loop(connectionIter, "o", &connectionPath)) { + // Get connection settings to check interface name + GDBusProxy *connectionProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath); + if (connectionProxy == nullptr) { + NMLOG_WARNING("Failed to create proxy for connection during hostname init: %s", connectionPath); + continue; + } + + GVariant *settingsResult = g_dbus_proxy_call_sync( + connectionProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error || settingsResult == nullptr) { + NMLOG_WARNING("Failed to get settings for connection %s during hostname init: %s", + connectionPath, error ? error->message : "unknown error"); + if (error) { + g_error_free(error); + error = nullptr; + } + g_object_unref(connectionProxy); + continue; + } + + // Parse connection settings to get interface name + GVariantIter *settingsIter; + const gchar *settingName; + GVariant *settingDict; + std::string interfaceName; + std::string connectionType; + + g_variant_get(settingsResult, "(a{sa{sv}})", &settingsIter); + while (g_variant_iter_loop(settingsIter, "{&s@a{sv}}", &settingName, &settingDict)) { + if (g_strcmp0(settingName, "connection") == 0) { + GVariantIter dictIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&dictIter, settingDict); + while (g_variant_iter_loop(&dictIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "interface-name") == 0) { + interfaceName = g_variant_get_string(value, nullptr); + } else if (g_strcmp0(key, "type") == 0) { + connectionType = g_variant_get_string(value, nullptr); + } + } + } + } + g_variant_iter_free(settingsIter); + + // Only process Ethernet and WiFi connections + bool isValidInterface = false; + if (!interfaceName.empty()) { + if (interfaceName == GnomeUtils::getEthIfname() && + (connectionType == "802-3-ethernet")) { + isValidInterface = true; + } else if (interfaceName == GnomeUtils::getWifiIfname() && + (connectionType == "802-11-wireless")) { + isValidInterface = true; + } + } + + if (isValidInterface) { + NMLOG_DEBUG("Initializing hostname for existing connection: %s (interface: %s)", + connectionPath, interfaceName.c_str()); + + if (!updateHostnameSettings(m_dbus, std::string(connectionPath), hostname, interfaceName)) { + NMLOG_WARNING("Failed to initialize hostname for existing connection: %s", interfaceName.c_str()); + // Don't fail the whole operation for one connection + } else { + NMLOG_INFO("Successfully initialized hostname for existing connection: %s", interfaceName.c_str()); + } + } else { + NMLOG_DEBUG("Skipping hostname init for non-ethernet/wifi connection: %s (type: %s)", + interfaceName.c_str(), connectionType.c_str()); + } + + g_variant_unref(settingsResult); + g_object_unref(connectionProxy); + } + + g_variant_iter_free(connectionIter); + g_variant_unref(connectionsResult); + g_object_unref(settingsProxy); + + NMLOG_INFO("Completed hostname initialization for existing connections"); + return success; + } + + bool NetworkManagerClient::setHostname(const std::string& hostname) + { + NMLOG_DEBUG("Setting DHCP hostname to: %s", hostname.c_str()); + + // Validate hostname length (1-32 characters as per NetworkManager specs) + if(hostname.length() < 1 || hostname.length() > 32) + { + NMLOG_ERROR("Invalid hostname length: %zu (must be 1-32 characters)", hostname.length()); + return false; + } + + GError *error = nullptr; + GDBusProxy *settingsProxy = m_dbus.getNetworkManagerSettingsProxy(); + if (settingsProxy == nullptr) { + NMLOG_ERROR("Error creating NetworkManager settings proxy"); + return false; + } + + // Get all connections + GVariant *connectionsResult = g_dbus_proxy_call_sync( + settingsProxy, + "ListConnections", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error || connectionsResult == nullptr) { + NMLOG_ERROR("Error retrieving connections: %s", error ? error->message : "unknown error"); + if (error) g_error_free(error); + g_object_unref(settingsProxy); + return false; + } + + GVariantIter *connectionIter; + const gchar *connectionPath; + bool success = true; + + g_variant_get(connectionsResult, "(ao)", &connectionIter); + + while (g_variant_iter_loop(connectionIter, "o", &connectionPath)) { + // Get connection settings to check interface name + GDBusProxy *connectionProxy = m_dbus.getNetworkManagerSettingsConnectionProxy(connectionPath); + if (connectionProxy == nullptr) { + NMLOG_WARNING("Failed to create proxy for connection: %s", connectionPath); + continue; + } + + GVariant *settingsResult = g_dbus_proxy_call_sync( + connectionProxy, + "GetSettings", + nullptr, + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (error || settingsResult == nullptr) { + NMLOG_WARNING("Failed to get settings for connection %s: %s", + connectionPath, error ? error->message : "unknown error"); + if (error) { + g_error_free(error); + error = nullptr; + } + g_object_unref(connectionProxy); + continue; + } + + // Parse connection settings to get interface name + GVariantIter *settingsIter; + const gchar *settingName; + GVariant *settingDict; + std::string interfaceName; + std::string connectionType; + + g_variant_get(settingsResult, "(a{sa{sv}})", &settingsIter); + while (g_variant_iter_loop(settingsIter, "{&s@a{sv}}", &settingName, &settingDict)) { + if (g_strcmp0(settingName, "connection") == 0) { + GVariantIter dictIter; + const gchar *key; + GVariant *value; + + g_variant_iter_init(&dictIter, settingDict); + while (g_variant_iter_loop(&dictIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "interface-name") == 0) { + interfaceName = g_variant_get_string(value, nullptr); + } else if (g_strcmp0(key, "type") == 0) { + connectionType = g_variant_get_string(value, nullptr); + } + } + } + } + g_variant_iter_free(settingsIter); + + // Only process Ethernet and WiFi connections + bool isValidInterface = false; + if (!interfaceName.empty()) { + if (interfaceName == GnomeUtils::getEthIfname() && + (connectionType == "802-3-ethernet")) { + isValidInterface = true; + } else if (interfaceName == GnomeUtils::getWifiIfname() && + (connectionType == "802-11-wireless")) { + isValidInterface = true; + } + } + + if (isValidInterface) { + NMLOG_DEBUG("Updating hostname for interface: %s (connection: %s)", + interfaceName.c_str(), connectionPath); + + if (!updateHostnameSettings(m_dbus, std::string(connectionPath), hostname, interfaceName)) { + NMLOG_ERROR("Failed to update hostname for interface: %s", interfaceName.c_str()); + success = false; + } + } else { + NMLOG_DEBUG("Skipping non-ethernet/wifi connection: %s (type: %s)", + interfaceName.c_str(), connectionType.c_str()); + } + + g_variant_unref(settingsResult); + g_object_unref(connectionProxy); + } + + g_variant_iter_free(connectionIter); + g_variant_unref(connectionsResult); + g_object_unref(settingsProxy); + + if (success) { + // Write hostname to persistent storage + GnomeUtils::writePersistentHostname(hostname); + NMLOG_INFO("Successfully set DHCP hostname to: %s", hostname.c_str()); + } + + return success; + } + + } // WPEFramework +} // Plugin diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusClient.h b/plugin/gnome/gdbus/NetworkManagerGdbusClient.h new file mode 100644 index 000000000..512ab6377 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusClient.h @@ -0,0 +1,91 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include + +#include "NetworkManagerLogger.h" +#include "NetworkManagerGdbusMgr.h" +#include "NetworkManagerSecretAgent.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerGdbusUtils.h" +#include "INetworkManager.h" + +#define GDBUS_WPS_RETRY_WAIT_IN_MS 10 // 10 sec +#define GDBUS_WPS_RETRY_COUNT 10 + +namespace WPEFramework +{ + namespace Plugin + { + + class NetworkManagerClient + { + public: + static NetworkManagerClient* getInstance() + { + static NetworkManagerClient instance; + return &instance; + } + + NetworkManagerClient(const NetworkManagerClient&) = delete; + NetworkManagerClient& operator=(const NetworkManagerClient&) = delete; + + bool getAvailableInterfaces(std::vector& interfaceList); + bool setPrimaryInterface(const std::string& interface); + bool setInterfaceState(const std::string& interface, bool enable); + bool setIPSettings(const std::string& interface, const Exchange::INetworkManager::IPAddress& address); + bool getPrimaryInterface(std::string& interface); + bool getDefaultInterface(std::string& interface); + bool getInterfaceState(const std::string& interface, bool& isEnabled); + bool getIPSettings(std::string& interface, const std::string& ipversion, Exchange::INetworkManager::IPAddress& result); + bool getKnownSSIDs(std::list& ssids); + bool getConnectedSSID(Exchange::INetworkManager::WiFiSSIDInfo& ssidinfo); + bool addToKnownSSIDs(const Exchange::INetworkManager::WiFiConnectTo& ssidinfo); + bool removeKnownSSIDs(const std::string& ssid); + bool startWifiScan(const std::string ssid = ""); + bool isWifiScannedRecently(int timelimitInSec = 5); // default 5 sec as shortest scanning interval + bool getDeviceInfo(const std::string& interface, deviceInfo& devInfo); + bool wifiConnect(const Exchange::INetworkManager::WiFiConnectTo& connectInfo, bool iswpsAP = false); + bool wifiDisconnect(); + bool activateKnownConnection(const std::string& interface, const std::string& knownConnectionID = ""); + bool getWifiState(Exchange::INetworkManager::WiFiState &state); + bool getWiFiSignalQuality(std::string& ssid, int& signalStrength, Exchange::INetworkManager::WiFiSignalQuality& quality); + bool startWPS(); + bool stopWPS(); + bool setHostname(const std::string& hostname); + bool modifyDefaultConnectionsConfig(); + + private: + NetworkManagerClient(); + ~NetworkManagerClient(); + void wpsProcess(); + bool getMatchingSSIDInfo(Exchange::INetworkManager::WiFiSSIDInfo& ssidInfo, std::string& apPathStr); + bool isActiveApSameAsWps(const std::string& wpsSSID); + std::thread m_wpsthread; + std::atomic m_wpsProcessRun; + std::atomic m_wpsActionTriggered; + SecretAgent m_secretAgent; + DbusMgr m_dbus; + }; + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp new file mode 100644 index 000000000..8811f5dc7 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp @@ -0,0 +1,826 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + + +#include +#include +#include +#include +#include + +#include "NetworkManagerGdbusEvent.h" +#include "NetworkManagerGdbusUtils.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerLogger.h" +#include "INetworkManager.h" + +namespace WPEFramework +{ + namespace Plugin + { + + static NetworkManagerEvents *_NetworkManagerEvents = nullptr; + extern NetworkManagerImplementation* _instance; + + static std::string getInterafaceNameFormDevicePath(const char* DevicePath) + { + GDBusProxy *deviceProxy = NULL; + std::string ifname; + deviceProxy =_NetworkManagerEvents->eventDbus.getNetworkManagerDeviceProxy(DevicePath); + if (deviceProxy == NULL) + return ifname; + GVariant *ifaceVariant = g_dbus_proxy_get_cached_property(deviceProxy, "Interface"); + if (ifaceVariant == NULL) { + NMLOG_WARNING("Interface property get error"); + g_object_unref(deviceProxy); + return ifname; + } + + const gchar *ifaceName = g_variant_get_string(ifaceVariant, NULL); + if(ifaceName != nullptr) + ifname = ifaceName; + + g_variant_unref(ifaceVariant); + g_object_unref(deviceProxy); + return ifname; + } + + static void lastScanPropertiesChangedCB(GDBusProxy *proxy, GVariant *changedProps, + const gchar *invalidProps[], gpointer userData) { + + if (proxy == NULL) { + NMLOG_FATAL("cb doesn't have proxy "); + return; + } + + GVariant *lastScanVariant = g_variant_lookup_value(changedProps, "LastScan", NULL); + if (lastScanVariant == NULL) + return; + + gint64 lastScanTime = g_variant_get_int64(lastScanVariant); + NMLOG_DEBUG("LastScan value changed: %" G_GINT64_FORMAT, lastScanTime); + g_variant_unref(lastScanVariant); + + const gchar *objectPath = g_dbus_proxy_get_object_path(proxy); + if (objectPath == NULL) { + NMLOG_WARNING("Failed to get the proxy object path"); + return; + } + + NetworkManagerEvents::onAvailableSSIDsCb(objectPath); + } + + static bool subscribeForlastScanPropertyEvent(const gchar *wirelessPath) + { + // Todo clean the proxy first + NMLOG_DEBUG("Monitoring lastScan time property: %s", wirelessPath); + _NetworkManagerEvents->nmEvents.wirelessProxy = _NetworkManagerEvents->eventDbus.getNetworkManagerWirelessProxy(wirelessPath); + if (_NetworkManagerEvents->nmEvents.wirelessProxy == nullptr) { + return false; + } + g_signal_connect(_NetworkManagerEvents->nmEvents.wirelessProxy, "g-properties-changed", G_CALLBACK(lastScanPropertiesChangedCB), NULL); + return true; + } + + static void primaryConnectionChangedCB(GDBusProxy *proxy, GVariant *changedPropes, GStrv invalidatPropes, gpointer userData) + { + if (changedPropes == NULL) { + NMLOG_FATAL("cb doesn't have changed_properties "); + return; + } + + GVariant *primaryConnPathVariant = g_variant_lookup_value(changedPropes, "PrimaryConnection", NULL); + if (primaryConnPathVariant != NULL) + { + const gchar *primaryConnPath = NULL; + std::string newIface = ""; + primaryConnPath = g_variant_get_string(primaryConnPathVariant, NULL); + if(primaryConnPath && g_strcmp0(primaryConnPath, "/") == 0) + { + NMLOG_DEBUG("no active primary connection"); + // sending empty string if no active interface + NetworkManagerEvents::onActiveInterfaceChangeCb(std::string("Unknown")); + g_variant_unref(primaryConnPathVariant); + return; + } + else if(primaryConnPath) + { + GDBusProxy *activeProxy = NULL; + NMLOG_DEBUG("PrimaryConnection changed: %s", primaryConnPath); + activeProxy =_NetworkManagerEvents->eventDbus.getNetworkManagerActiveConnProxy(primaryConnPath); + if(activeProxy != NULL) + { + GVariant *typeVariant = g_dbus_proxy_get_cached_property(activeProxy, "Type"); + if (typeVariant != NULL) + { + const gchar *connectionType = g_variant_get_string(typeVariant, NULL); + if(connectionType != NULL) { + if (0 == strncmp("802-3-ethernet", connectionType, sizeof("802-3-ethernet")) && string("eth0_missing") != GnomeUtils::getEthIfname()) + NetworkManagerEvents::onActiveInterfaceChangeCb(std::string(GnomeUtils::getEthIfname())); + else if(0 == strncmp("802-11-wireless", connectionType, sizeof("802-11-wireless")) && string("wlan0_missing") != GnomeUtils::getWifiIfname()) + NetworkManagerEvents::onActiveInterfaceChangeCb(std::string(GnomeUtils::getWifiIfname())); + else + NMLOG_WARNING("active connection not an ethernet/wifi %s", connectionType); + } + else + NMLOG_WARNING("connection type NULL"); + g_variant_unref(typeVariant); + } + else + NMLOG_ERROR("PrimaryConnection Type property missing"); + g_object_unref(activeProxy); + } + } + else + NMLOG_WARNING("PrimaryConnection object missing"); + g_variant_unref(primaryConnPathVariant); + } + } + + static void deviceAddRemoveCb(GDBusProxy *proxy, gchar *senderName, gchar *signalName, + GVariant *parameters, gpointer userData) { + + if (signalName == NULL) { + NMLOG_FATAL("cb doesn't have signalName "); + return; + } + + const gchar *devicePath = NULL; + std::string ifname; + if (g_strcmp0(signalName, "DeviceAdded") == 0 || g_strcmp0(signalName, "DeviceRemoved") == 0) + { + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE_TUPLE)) { + GVariant *first_element = g_variant_get_child_value(parameters, 0); + + if (g_variant_is_of_type(first_element, G_VARIANT_TYPE_OBJECT_PATH)) { + devicePath = g_variant_get_string(first_element, NULL); + ifname = getInterafaceNameFormDevicePath(devicePath); + } else { + NMLOG_ERROR("varient type not object path %s", g_variant_get_type_string(first_element)); + } + g_variant_unref(first_element); + } + if (g_strcmp0(signalName, "DeviceAdded") == 0) { + NMLOG_INFO("Device Added: %s", ifname.c_str()); + if(ifname == GnomeUtils::getWifiIfname() || ifname == GnomeUtils::getEthIfname() ) + { + // Get device state to validate if it should be monitored + deviceInfo devInfo{}; + NetworkManagerEvents* nmEventsInstance = NetworkManagerEvents::getInstance(); + if(nmEventsInstance && GnomeUtils::getDeviceInfoByIfname(nmEventsInstance->eventDbus, ifname.c_str(), devInfo)) + { + NMDeviceState devState = devInfo.state; + // NM_DEVICE_STATE_UNAVAILABLE can occur for an Ethernet interface when it is disconnected (e.g., no cable connected). + bool isDeviceEnabled = devState >= NM_DEVICE_STATE_UNAVAILABLE && devState <= NM_DEVICE_STATE_ACTIVATED; + if(!isDeviceEnabled) + { + NMLOG_WARNING("device %s is not enabled, So no event monitor", ifname.c_str()); + return; + } + } + NMLOG_INFO("monitoring device: %s", ifname.c_str()); + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, ifname.c_str()); + // Only subscribe to LastScan if it's a WiFi device and not already monitored + if(ifname == GnomeUtils::getWifiIfname() && _NetworkManagerEvents->nmEvents.wirelessProxy == nullptr) + { + subscribeForlastScanPropertyEvent(devicePath); + } + // TODO monitor device + // monitorDevice() + } + } + else if(g_strcmp0(signalName, "DeviceRemoved")) { + NMLOG_INFO("Device Removed: %s", ifname.c_str()); + if(ifname == GnomeUtils::getWifiIfname() || ifname == GnomeUtils::getEthIfname() ) + { + NMLOG_INFO("monitoring device: %s", ifname.c_str()); + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, ifname.c_str()); + } + } + } + } + + static void deviceStateChangedCB(GDBusProxy *proxy, gchar *senderName, gchar *signalName, + GVariant *parameters, gpointer userData) { + + if (signalName == NULL || g_strcmp0(signalName, "StateChanged") != 0) { + return; + } + + static bool doPostEthUpEvent= false; + static bool doPostWlanUpEvent= false; + + const gchar *devicePath = g_dbus_proxy_get_object_path(proxy); + std::string ifname = getInterafaceNameFormDevicePath(devicePath); + if(ifname.empty()) + { + NMLOG_WARNING("interface name null"); + return; + } + guint32 oldState, newState, stateReason; + g_variant_get(parameters, "(uuu)", &newState, &oldState, &stateReason); + NMDeviceState deviceState = static_cast(newState); + NMDeviceStateReason deviceStateReason = static_cast(stateReason); + + NMLOG_DEBUG("%s: Old State: %u, New State: %u, Reason: %u", ifname.c_str(), oldState, newState, stateReason); + if(ifname == GnomeUtils::getWifiIfname()) + { + std::string wifiState; + switch (deviceStateReason) + { + case NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE: + wifiState = "WIFI_STATE_UNINSTALLED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED, wifiState); + break; + case NM_DEVICE_STATE_REASON_SSID_NOT_FOUND: + wifiState = "WIFI_STATE_SSID_NOT_FOUND"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND, wifiState); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT: // supplicant took too long to authenticate + case NM_DEVICE_STATE_REASON_NO_SECRETS: + wifiState = "WIFI_STATE_AUTHENTICATION_FAILED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED, wifiState); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED: // 802.1x supplicant failed + wifiState = "WIFI_STATE_ERROR"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_ERROR, wifiState); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED: // 802.1x supplicant configuration failed + wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED, wifiState); + break; + case NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT: // 802.1x supplicant disconnected + wifiState = "WIFI_STATE_INVALID_CREDENTIALS"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS, wifiState); + break; + default: + { + switch (deviceState) + { + case NM_DEVICE_STATE_UNKNOWN: + wifiState = "WIFI_STATE_UNINSTALLED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_UNINSTALLED, wifiState); + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, GnomeUtils::getWifiIfname()); + doPostWlanUpEvent= true; + break; + case NM_DEVICE_STATE_UNMANAGED: + wifiState = "WIFI_STATE_DISABLED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISABLED, wifiState); + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, GnomeUtils::getWifiIfname()); + doPostWlanUpEvent= true; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + wifiState = "WIFI_STATE_DISCONNECTED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_DISCONNECTED, wifiState); + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, GnomeUtils::getWifiIfname()); + NetworkManagerEvents::onAddressChangeCb(GnomeUtils::getWifiIfname(), false, false); + NetworkManagerEvents::onAddressChangeCb(GnomeUtils::getWifiIfname(), false, true); + break; + case NM_DEVICE_STATE_PREPARE: + wifiState = "WIFI_STATE_PAIRING"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_PAIRING, wifiState); + break; + case NM_DEVICE_STATE_CONFIG: + wifiState = "WIFI_STATE_CONNECTING"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTING, wifiState); + break; + case NM_DEVICE_STATE_IP_CONFIG: + wifiState = "NM_DEVICE_STATE_IP_CONFIG"; + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, GnomeUtils::getWifiIfname()); + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTED, wifiState); + break; + case NM_DEVICE_STATE_IP_CHECK: + wifiState = "NM_DEVICE_STATE_IP_CHECK"; + //NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, GnomeUtils::getWifiIfname()); + break; + case NM_DEVICE_STATE_SECONDARIES: + wifiState = "NM_DEVICE_STATE_SECONDARIES"; + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, GnomeUtils::getWifiIfname()); + break; + case NM_DEVICE_STATE_ACTIVATED: + wifiState = "WIFI_STATE_CONNECTED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTED, wifiState); + break; + case NM_DEVICE_STATE_DEACTIVATING: + wifiState = "WIFI_STATE_CONNECTION_LOST"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST, wifiState); + break; + case NM_DEVICE_STATE_FAILED: + wifiState = "WIFI_STATE_CONNECTION_FAILED"; + NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED, wifiState); + break; + case NM_DEVICE_STATE_NEED_AUTH: + //NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED); + wifiState = "WIFI_STATE_CONNECTION_INTERRUPTED"; + break; + default: + wifiState = "Un handiled"; + } + + if(doPostWlanUpEvent && deviceState > NM_DEVICE_STATE_UNMANAGED) + { + doPostWlanUpEvent = false; + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, GnomeUtils::getWifiIfname()); + } + } + } + NMLOG_INFO("wifi state: %s; reason: %d", wifiState.c_str(), static_cast(deviceStateReason)); + } + else if(ifname == GnomeUtils::getEthIfname()) + { + switch (deviceState) + { + case NM_DEVICE_STATE_UNKNOWN: + case NM_DEVICE_STATE_UNMANAGED: + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_DISABLED, GnomeUtils::getEthIfname()); + doPostEthUpEvent= true; + break; + case NM_DEVICE_STATE_UNAVAILABLE: + case NM_DEVICE_STATE_DISCONNECTED: + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, GnomeUtils::getEthIfname()); + NetworkManagerEvents::onAddressChangeCb(GnomeUtils::getEthIfname(), false, false); + NetworkManagerEvents::onAddressChangeCb(GnomeUtils::getEthIfname(), false, true); + break; + case NM_DEVICE_STATE_PREPARE: + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, GnomeUtils::getEthIfname()); + break; + case NM_DEVICE_STATE_IP_CONFIG: + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, GnomeUtils::getEthIfname()); + case NM_DEVICE_STATE_NEED_AUTH: + case NM_DEVICE_STATE_SECONDARIES: + case NM_DEVICE_STATE_ACTIVATED: + case NM_DEVICE_STATE_DEACTIVATING: + default: + NMLOG_WARNING("Unhandiled state change eth0"); + + if(doPostEthUpEvent && deviceState > NM_DEVICE_STATE_UNMANAGED) + { + doPostEthUpEvent = false; + NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, GnomeUtils::getEthIfname()); + } + } + + NMLOG_INFO("eth0 state: %d; reason: %d", static_cast(deviceState), static_cast(deviceStateReason)); + } + } + + static void onConnectionSignalReceivedCB (GDBusProxy *proxy, gchar *senderName, gchar *signalName, + GVariant *parameters, gpointer userData) { + if (g_strcmp0(senderName, "NewConnection") == 0) { + NMLOG_INFO("new connection added success"); + NMLOG_DEBUG("Parameters: %s", g_variant_print(parameters, TRUE)); + } else if (g_strcmp0(signalName, "ConnectionRemoved") == 0) { + NMLOG_INFO("connection remove success"); + NMLOG_DEBUG("Parameters: %s", g_variant_print(parameters, TRUE)); + } + } + + static void ipV4addressChangeCb(GDBusProxy *proxy, GVariant *changedProps, GStrv invalidProps, gpointer userData) + { + if (changedProps == NULL || proxy == NULL) { + NMLOG_FATAL("cb doesn't have changed properties "); + return; + } + + GVariant *addressVariant = g_variant_lookup_value(changedProps, "Addresses", NULL); + if (addressVariant != NULL) + { + const char* iface = static_cast(userData); + if(iface != NULL ) + { + std::string ipAddr; uint32_t prifix; + if(GnomeUtils::getIPv4AddrFromIPv4ConfigProxy(proxy, ipAddr, prifix)) + NetworkManagerEvents::onAddressChangeCb(std::string(iface), true, false, ipAddr); + } + g_variant_unref(addressVariant); + } + } + + static void ipV6addressChangeCb(GDBusProxy *proxy, GVariant *changedProps, GStrv invalidProps, gpointer userData) + { + if (changedProps == NULL || proxy == NULL) { + NMLOG_FATAL("cb doesn't have changed properties "); + return; + } + + GVariant *addressVariant = g_variant_lookup_value(changedProps, "Addresses", NULL); + if (addressVariant != NULL) + { + const char* iface = static_cast(userData); + if(iface != NULL) + { + std::string ipAddr; uint32_t prifix; + if(GnomeUtils::getIPv6AddrFromIPv6ConfigProxy(proxy, ipAddr, prifix)) + NetworkManagerEvents::onAddressChangeCb(std::string(iface), true, true, ipAddr); + } + + g_variant_unref(addressVariant); + } + else + { + GVariantIter *propsIter= NULL; + const gchar *propertyName= NULL; + GVariant *propertyValue= NULL; + + // Iterate over all changed properties + g_variant_get(changedProps, "a{sv}", &propsIter); + while (g_variant_iter_loop(propsIter, "{&sv}", &propertyName, &propertyValue)) { + NMLOG_DEBUG("Other Property: %s", propertyName); + // if(propertyValue) + // g_variant_unref(changedProps); + } + g_variant_iter_free(propsIter); + } + } + + static void monitorDevice(const gchar *devicePath) + { + std::string ifname = getInterafaceNameFormDevicePath(devicePath); + gpointer userdata = NULL; + if(ifname.empty()) + { + NMLOG_WARNING("interface is missing for the device(%s)", devicePath); + return; + } + + if(ifname != GnomeUtils::getEthIfname() && ifname != GnomeUtils::getWifiIfname()) + { + NMLOG_DEBUG("interface is not eth0/wlan0: %s", ifname.c_str()); + return; + } + + GDBusProxy *deviceProxy =_NetworkManagerEvents->eventDbus.getNetworkManagerDeviceProxy(devicePath); + if(deviceProxy == nullptr) + return; + + g_signal_connect(deviceProxy, "g-signal", G_CALLBACK(deviceStateChangedCB), NULL); + NMLOG_DEBUG("Monitoring device: %s", devicePath); + if(ifname == GnomeUtils::getWifiIfname()) + { + _NetworkManagerEvents->nmEvents.wirelessDeviceProxy = deviceProxy; + _NetworkManagerEvents->nmEvents.wifiDevicePath = devicePath; + userdata = _NetworkManagerEvents->wlanIfname; // TODO change to GnomeUtils::getWifiIfname() + subscribeForlastScanPropertyEvent(devicePath); + } + else if(ifname == GnomeUtils::getEthIfname()) + { + _NetworkManagerEvents->nmEvents.wiredDeviceProxy = deviceProxy; + _NetworkManagerEvents->nmEvents.ethDevicePath = devicePath; + userdata = _NetworkManagerEvents->ethIfname; + } + + const gchar *ipv4ConfigPath = NULL; + const gchar *ipv6ConfigPath = NULL; + GVariant *ip4Config = g_dbus_proxy_get_cached_property(deviceProxy, "Ip4Config"); + if (ip4Config) + { + ipv4ConfigPath = g_variant_get_string(ip4Config, NULL); + NMLOG_DEBUG("Monitoring ip4_config_path: %s", ipv4ConfigPath); + } + + GVariant *ip6Config = g_dbus_proxy_get_cached_property(deviceProxy, "Ip6Config"); + if(ip6Config) + { + ipv6ConfigPath = g_variant_get_string(ip6Config, NULL); + NMLOG_DEBUG("Monitoring ip6_config_path: %s", ipv6ConfigPath); + } + + if(ipv4ConfigPath) + { + GDBusProxy *ipV4Proxy = _NetworkManagerEvents->eventDbus.getNetworkManagerIpv4Proxy(ipv4ConfigPath); + g_signal_connect(ipV4Proxy, "g-properties-changed", G_CALLBACK(ipV4addressChangeCb), userdata); + if(ifname == GnomeUtils::getEthIfname()) + _NetworkManagerEvents->nmEvents.ethIPv4Proxy = ipV4Proxy; + else if(ifname == GnomeUtils::getWifiIfname()) + _NetworkManagerEvents->nmEvents.wlanIPv4Proxy = ipV4Proxy; + } + + if(ipv6ConfigPath) + { + GDBusProxy *ipV6Proxy = _NetworkManagerEvents->eventDbus.getNetworkManagerIpv6Proxy(ipv6ConfigPath); + g_signal_connect(ipV6Proxy, "g-properties-changed", G_CALLBACK(ipV6addressChangeCb), userdata); + if(ifname == GnomeUtils::getEthIfname()) + _NetworkManagerEvents->nmEvents.ethIPv6Proxy = ipV6Proxy; + else if(ifname == GnomeUtils::getWifiIfname()) + _NetworkManagerEvents->nmEvents.wlanIPv6Proxy = ipV6Proxy; + } + + if(ip4Config) + g_variant_unref(ip4Config); + if(ip6Config) + g_variant_unref(ip6Config); + } + + void* NetworkManagerEvents::networkMangerEventMonitor(void *arg) + { + if(arg == NULL) + { + NMLOG_FATAL("function argument error: nm event monitor failed"); + return nullptr; + } + + NMEvents *nmEvents = static_cast(arg); + nmEvents->networkManagerProxy = _NetworkManagerEvents->eventDbus.getNetworkManagerProxy(); + if (nmEvents->networkManagerProxy == NULL) { + return nullptr; + } + + g_signal_connect(nmEvents->networkManagerProxy, "g-signal", G_CALLBACK(deviceAddRemoveCb), NULL); + g_signal_connect(nmEvents->networkManagerProxy, "g-properties-changed", G_CALLBACK(primaryConnectionChangedCB), NULL); + + GVariant *devices = g_dbus_proxy_get_cached_property(nmEvents->networkManagerProxy, "Devices"); + if (devices != NULL) { + GVariantIter iter; + gchar *devicePath = NULL; + + g_variant_iter_init(&iter, devices); + while (g_variant_iter_loop(&iter, "&o", &devicePath)) { + monitorDevice(devicePath); + } + + g_variant_unref(devices); + } + + nmEvents->settingsProxy = _NetworkManagerEvents->eventDbus.getNetworkManagerSettingsProxy(); + if (nmEvents->settingsProxy == NULL) + NMLOG_WARNING("Settings proxy failed no connection event registerd"); + else + g_signal_connect(nmEvents->settingsProxy, "g-signal", G_CALLBACK(onConnectionSignalReceivedCB), NULL); + + NMLOG_INFO("registered all networkmnager dbus events"); + g_main_loop_run(nmEvents->loop); + + if(nmEvents->wirelessDeviceProxy) + g_object_unref(nmEvents->wirelessDeviceProxy); + if(nmEvents->wirelessProxy) + g_object_unref(nmEvents->wirelessProxy); + if(nmEvents->wiredDeviceProxy) + g_object_unref(nmEvents->wiredDeviceProxy); + if(nmEvents->networkManagerProxy) + g_object_unref(nmEvents->networkManagerProxy); + if(nmEvents->settingsProxy) + g_object_unref(nmEvents->settingsProxy); + if(nmEvents->ethIPv4Proxy) + g_object_unref(nmEvents->ethIPv4Proxy); + if(nmEvents->ethIPv6Proxy) + g_object_unref(nmEvents->ethIPv6Proxy); + if(nmEvents->wlanIPv4Proxy) + g_object_unref(nmEvents->wlanIPv4Proxy); + if(nmEvents->wlanIPv6Proxy) + g_object_unref(nmEvents->wlanIPv6Proxy); + + NMLOG_WARNING("unregistered all event monitor"); + return nullptr; + } + + bool NetworkManagerEvents::startNetworkMangerEventMonitor() + { + NMLOG_DEBUG("starting gnome event monitor..."); + + if(!isEventThrdActive) { + isEventThrdActive = true; + eventThrdID = g_thread_new("nm_event_thrd", NetworkManagerEvents::networkMangerEventMonitor, &nmEvents); + } + return true; + } + + void NetworkManagerEvents::stopNetworkMangerEventMonitor() + { + + if (nmEvents.loop != NULL) { + g_main_loop_quit(nmEvents.loop); + } + if (eventThrdID) { + g_thread_join(eventThrdID); + eventThrdID = NULL; + NMLOG_WARNING("gnome event monitor stoped"); + } + isEventThrdActive = false; + } + + NetworkManagerEvents::~NetworkManagerEvents() + { + NMLOG_INFO("~NetworkManagerEvents"); + stopNetworkMangerEventMonitor(); + } + + NetworkManagerEvents* NetworkManagerEvents::getInstance() + { + static NetworkManagerEvents instance; + return &instance; + } + + NetworkManagerEvents::NetworkManagerEvents() + { + NMLOG_DEBUG("NetworkManagerEvents"); + nmEvents.loop = g_main_loop_new(NULL, FALSE); + if(nmEvents.loop == NULL) { + NMLOG_FATAL("GMain loop failed Fatal Error: Event will not work"); + return; + } + strncpy(wlanIfname, GnomeUtils::getWifiIfname(), sizeof(wlanIfname)); + strncpy(ethIfname, GnomeUtils::getEthIfname(), sizeof(ethIfname)); + _NetworkManagerEvents = this; + startNetworkMangerEventMonitor(); + } + + /* Gnome networkmanger events call backs */ + void NetworkManagerEvents::onActiveInterfaceChangeCb(std::string newIface) + { + static std::string oldIface = "Unknown"; + if(oldIface != newIface) + { + NMLOG_INFO("old interface - %s new interface - %s", oldIface.c_str(), newIface.c_str()); + oldIface = newIface; + if(_instance != nullptr) + _instance->ReportActiveInterfaceChange(oldIface, newIface); + } + } + + void NetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::InterfaceState newState, std::string iface) + { + std::string state; + switch (newState) + { + case Exchange::INetworkManager::INTERFACE_ADDED: + state = "INTERFACE_ADDED"; + break; + case Exchange::INetworkManager::INTERFACE_LINK_UP: + state = "INTERFACE_LINK_UP"; + break; + case Exchange::INetworkManager::INTERFACE_LINK_DOWN: + state = "INTERFACE_LINK_DOWN"; + break; + case Exchange::INetworkManager::INTERFACE_ACQUIRING_IP: + state = "INTERFACE_ACQUIRING_IP"; + break; + case Exchange::INetworkManager::INTERFACE_REMOVED: + state = "INTERFACE_REMOVED"; + break; + case Exchange::INetworkManager::INTERFACE_DISABLED: + state = "INTERFACE_DISABLED"; + break; + default: + state = "Unknown"; + } + + NMLOG_DEBUG("%s interface state changed - %s", iface.c_str(), state.c_str()); + if(_instance != nullptr && (iface == GnomeUtils::getWifiIfname() || iface == GnomeUtils::getEthIfname())) + _instance->ReportInterfaceStateChange(static_cast(newState), iface); + } + + void NetworkManagerEvents::onWIFIStateChanged(Exchange::INetworkManager::WiFiState state, std::string& wifiStateStr) + { + NMLOG_DEBUG("wifi state changed: %d ; NM wifi: %s", state, wifiStateStr.c_str()); + if(_instance != nullptr) + _instance->ReportWiFiStateChange(static_cast(state), ""); + } + + void NetworkManagerEvents::onAddressChangeCb(std::string iface, bool acquired, bool isIPv6, std::string ipAddress) + { + static std::map ipv6Map; + static std::map ipv4Map; + + if (isIPv6) + { + if (acquired == false) { // ip lost event + if(ipv6Map.size() < 1) + { + return; // no ipv6 added yet no event posting + } + ipAddress = ipv6Map[iface]; + ipv6Map[iface].clear(); + } + else // acquired + { + if (ipAddress.compare(0, 5, "fe80:") == 0 || + ipAddress.compare(0, 6, "fe80::") == 0) { + NMLOG_DEBUG("It's link-local ip"); + return; // It's link-local + } + /* same ip comes multiple time so avoding that */ + if (ipv6Map[iface].find(ipAddress) == std::string::npos && !ipAddress.empty()) { + ipv6Map[iface] = ipAddress; // SLAAC protocol may include multip ipv6 address last one will save + } + else + return; // skip same ip event posting if empty request + } + } + else + { + if (acquired == false) { // ip lost event + if(ipv4Map.size() < 1) + { + return; // no ipv4 added yet no event posting + } + ipAddress = ipv4Map[iface]; + ipv4Map[iface].clear(); + } + else + { + if(!ipAddress.empty()) + ipv4Map[iface] = ipAddress; + else + return; + } + } + + Exchange::INetworkManager::IPStatus ipStatus{}; + if(acquired) + ipStatus = Exchange::INetworkManager::IP_ACQUIRED; + + if(_instance != nullptr) + _instance->ReportIPAddressChange(iface, isIPv6?"IPv6":"IPv4", ipAddress, ipStatus); + + NMLOG_INFO("iface:%s - ipaddress:%s - %s - %s", iface.c_str(), ipAddress.c_str(), acquired?"acquired":"lost", isIPv6?"isIPv6":"isIPv4"); + } + + void NetworkManagerEvents::onAvailableSSIDsCb(const char* wifiDevicePath) + { + GDBusProxy* wProxy = nullptr; + GError* error = nullptr; + + NMLOG_DEBUG("wifi scanning completed ..."); + if(_NetworkManagerEvents->doScanNotify == false) { + return; + } + + _NetworkManagerEvents->doScanNotify = false; + wProxy = _NetworkManagerEvents->eventDbus.getNetworkManagerWirelessProxy(wifiDevicePath); + if(wProxy == NULL) + return; + + GVariant* result = g_dbus_proxy_call_sync(wProxy, "GetAllAccessPoints", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (error) { + NMLOG_ERROR("Error creating proxy: %s", error->message); + g_error_free(error); + g_object_unref(wProxy); + return; + } + + GVariantIter* iter = NULL; + const gchar* apPath = NULL; + JsonArray ssidList = JsonArray(); + bool oneSSIDFound = false; + + g_variant_get(result, "(ao)", &iter); + if(iter == NULL) + { + NMLOG_ERROR("GetAllAccessPoints GVariantIter returned NULL"); + g_variant_unref(result); + g_object_unref(wProxy); + return; + } + while (g_variant_iter_loop(iter, "o", &apPath)) { + Exchange::INetworkManager::WiFiSSIDInfo wifiInfo; + // NMLOG_DEBUG("Access Point Path: %s", apPath); + if(apPath != NULL && GnomeUtils::getApDetails(_NetworkManagerEvents->eventDbus, apPath, wifiInfo)) + { + JsonObject ssidObj; + if(GnomeUtils::convertSsidInfoToJsonObject(wifiInfo, ssidObj)) + { + ssidList.Add(ssidObj); + oneSSIDFound = true; + } + } + } + + if(oneSSIDFound) { + std::string ssids{}; + ssidList.ToString(ssids); + if(_instance != nullptr) + _instance->ReportAvailableSSIDs(ssidList); + NMLOG_DEBUG("scanned ssids: %s", ssids.c_str()); + } + + g_variant_iter_free(iter); + g_variant_unref(result); + g_object_unref(wProxy); + } + + void NetworkManagerEvents::setwifiScanOptions(bool doNotify) + { + doScanNotify.store(doNotify); + if(!doScanNotify) + NMLOG_DEBUG("stoped periodic wifi scan result notify"); + } + + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusEvent.h b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.h new file mode 100644 index 000000000..d25a198b9 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusEvent.h @@ -0,0 +1,84 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include + +#include "NetworkManagerGdbusMgr.h" +#include "INetworkManager.h" + +namespace WPEFramework +{ + namespace Plugin + { + + typedef struct {; + + GDBusProxy *wiredDeviceProxy; // eth0 dev + GDBusProxy *wirelessDeviceProxy; // wlan0 dev + GDBusProxy *wirelessProxy; // wireless interface + GDBusProxy *networkManagerProxy; // networkmanager main bus + GDBusProxy *settingsProxy; // settings + + GDBusProxy *ethIPv4Proxy; + GDBusProxy *ethIPv6Proxy; + GDBusProxy *wlanIPv4Proxy; + GDBusProxy *wlanIPv6Proxy; + + std::string wifiDevicePath; + std::string ethDevicePath; + + GMainLoop *loop; + } NMEvents; + + class NetworkManagerEvents + { + + public: + static void onInterfaceStateChangeCb(Exchange::INetworkManager::InterfaceState newState, std::string iface); // ReportInterfaceStateChangedEvent + static void onAddressChangeCb(std::string iface, bool acquired, bool isIPv6, std::string ipAddress = ""); // ReportIPAddressChangedEvent + static void onActiveInterfaceChangeCb(std::string newInterface); // ReportActiveInterfaceChangedEvent + static void onAvailableSSIDsCb(const char* wifiDevicePath); // ReportAvailableSSIDsEvent + static void onWIFIStateChanged(Exchange::INetworkManager::WiFiState state, std::string& wifiStateStr); // ReportWiFiStateChangedEvent + + public: + static NetworkManagerEvents* getInstance(); + bool startNetworkMangerEventMonitor(); + void stopNetworkMangerEventMonitor(); + void setwifiScanOptions(bool doNotify); + + private: + static void* networkMangerEventMonitor(void *arg); + NetworkManagerEvents(); + ~NetworkManagerEvents(); + std::atomicisEventThrdActive = {false}; + std::atomicdoScanNotify = {true}; + GThread *eventThrdID; + public: + NMEvents nmEvents{}; + DbusMgr eventDbus; + char wlanIfname[16]; + char ethIfname[16]; + }; + + } // Plugin +} // WPEFramework diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusMgr.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusMgr.cpp new file mode 100644 index 000000000..71d3374e2 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusMgr.cpp @@ -0,0 +1,329 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include + +#include "NetworkManagerGdbusMgr.h" +#include "NetworkManagerLogger.h" + +namespace WPEFramework +{ + namespace Plugin + { + + DbusMgr::DbusMgr() : connection(NULL) + { + GError* error = NULL; + NMLOG_INFO("DbusMgr"); + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection) { + NMLOG_FATAL("Error connecting to system D-Bus bus: %s ", error->message); + g_error_free(error); + } + flags = G_DBUS_PROXY_FLAGS_NONE; + } + + DbusMgr::~DbusMgr() { + NMLOG_INFO("~DbusMgr"); + if (connection) { + g_object_unref(connection); + connection = NULL; + } + } + + GDBusConnection* DbusMgr::getConnection() + { + if (connection) { + return connection; + } + + GError* error = NULL; + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!connection && error) { + NMLOG_FATAL("Error reconnecting to system bus: %s ", error->message); + g_error_free(error); + return NULL; + } + + flags = G_DBUS_PROXY_FLAGS_NONE; + return connection; + } + + GDBusProxy* DbusMgr::getNetworkManagerProxy() + { + GError* error = NULL; + nmProxy = NULL; + nmProxy = g_dbus_proxy_new_sync( + getConnection(), G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + "org.freedesktop.NetworkManager", + NULL, + &error + ); + + if (nmProxy == NULL || error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating D-Bus proxy: %s", error->message); + g_error_free(error); + return NULL; + } + + return nmProxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerSettingsProxy() + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/Settings", + "org.freedesktop.NetworkManager.Settings", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerSettingsConnectionProxy(const char* connPath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + connPath, + "org.freedesktop.NetworkManager.Settings.Connection", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerDeviceProxy(const char* devicePath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + devicePath, + "org.freedesktop.NetworkManager.Device", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerActiveConnProxy(const char* activePath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + activePath, + "org.freedesktop.NetworkManager.Connection.Active", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerAccessPointProxy(const char* apPath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + apPath, + "org.freedesktop.NetworkManager.AccessPoint", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerWirelessProxy(const char* wirelessDevPath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + wirelessDevPath, + "org.freedesktop.NetworkManager.Device.Wireless", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerIpv4Proxy(const char* ipConfigPath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + ipConfigPath, + "org.freedesktop.NetworkManager.IP4Config", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerIpv6Proxy(const char* ipConfigPath) + { + GError* error = NULL; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), flags, NULL, "org.freedesktop.NetworkManager", + ipConfigPath, + "org.freedesktop.NetworkManager.IP6Config", + NULL, + &error + ); + + if (error != NULL) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating proxy: %s", error->message); + g_clear_error(&error); + return NULL; + } + + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerDhcpv4Proxy(const char* dhcpConfigPath) + { + GError* error = nullptr; + GDBusProxy *proxy = g_dbus_proxy_new_sync( + getConnection(), + flags, + nullptr, + "org.freedesktop.NetworkManager", + dhcpConfigPath, + "org.freedesktop.NetworkManager.DHCP4Config", + nullptr, + &error); + + if (proxy == nullptr) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating dhcpv4 proxy: %s", error->message); + g_clear_error(&error); + return nullptr; + } + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerDhcpv6Proxy(const char* dhcpConfigPath) + { + GError* error = nullptr; + GDBusProxy *proxy = g_dbus_proxy_new_sync( + getConnection(), + flags, + nullptr, + "org.freedesktop.NetworkManager", + dhcpConfigPath, + "org.freedesktop.NetworkManager.DHCP6Config", + nullptr, + &error); + + if (proxy == nullptr) { + g_dbus_error_strip_remote_error(error); + NMLOG_FATAL("Error creating dhcpv6 proxy: %s", error->message); + g_clear_error(&error); + return nullptr; + } + return proxy; + } + + GDBusProxy* DbusMgr::getNetworkManagerPropertyProxy(const char* devicePath) + { + GError* error = nullptr; + GDBusProxy* proxy = g_dbus_proxy_new_sync( + getConnection(), + flags, + nullptr, + "org.freedesktop.NetworkManager", + devicePath, + "org.freedesktop.DBus.Properties", + nullptr, + &error + ); + + if (proxy == nullptr) { + g_dbus_error_strip_remote_error(error); + NMLOG_ERROR("Failed to create property proxy: %s", error->message); + g_clear_error(&error); + return nullptr; + } + return proxy; + } + + } // Plugin +} // WPEFramework + diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusMgr.h b/plugin/gnome/gdbus/NetworkManagerGdbusMgr.h new file mode 100644 index 000000000..a6c2f81ed --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusMgr.h @@ -0,0 +1,59 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include + +/* include NetworkManager.h for the defines, but we don't link against libnm. */ +#include + +namespace WPEFramework +{ + namespace Plugin + { + class DbusMgr { + public: + DbusMgr(); + ~DbusMgr(); + + GDBusProxy* getNetworkManagerProxy(); + GDBusProxy* getNetworkManagerSettingsProxy(); + GDBusProxy* getNetworkManagerSettingsConnectionProxy(const char* connPath); + GDBusProxy* getNetworkManagerDeviceProxy(const char* devicePath); + GDBusProxy* getNetworkManagerWirelessProxy(const char* wirelessDevPath); + GDBusProxy* getNetworkManagerIpv4Proxy(const char* ipConfigPath); + GDBusProxy* getNetworkManagerIpv6Proxy(const char* ipConfigPath); + GDBusProxy* getNetworkManagerActiveConnProxy(const char* activePath); + GDBusProxy* getNetworkManagerAccessPointProxy(const char* apPath); + GDBusConnection* getConnection(); + GDBusProxy* getNetworkManagerPropertyProxy(const char* devicePath); + GDBusProxy* getNetworkManagerDhcpv4Proxy(const char* dhcpConfigPath); + GDBusProxy* getNetworkManagerDhcpv6Proxy(const char* dhcpConfigPath); + + private: + GDBusConnection* connection; + GDBusProxyFlags flags; + GDBusProxy *nmProxy = NULL; + }; + + } +} diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp new file mode 100644 index 000000000..33254fc4d --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusProxy.cpp @@ -0,0 +1,409 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "NetworkManagerImplementation.h" +#include "NetworkManagerGdbusClient.h" +#include "NetworkManagerGdbusEvent.h" +#include "../NetworkManagerGnomeUtils.h" +#include "NetworkManagerGdbusUtils.h" + +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using namespace std; + +namespace WPEFramework +{ + namespace Plugin + { + NetworkManagerImplementation* _instance = nullptr; + NetworkManagerClient* _nmGdbusClient = nullptr; + NetworkManagerEvents* _nmGdbusEvents = nullptr; + void NetworkManagerInternalEventHandler(const char *owner, int eventId, void *data, size_t len) + { + return; + } + + void NetworkManagerImplementation::getInitialConnectionState() + { + // check the connection state and post event + Exchange::INetworkManager::IInterfaceDetailsIterator* _interfaces{}; + uint32_t rc = GetAvailableInterfaces(_interfaces); + + if (Core::ERROR_NONE == rc) + { + if (_interfaces != nullptr) + { + Exchange::INetworkManager::InterfaceDetails iface{}; + while (_interfaces->Next(iface) == true) + { + Core::JSON::EnumType type{iface.type}; + if(iface.enabled) + { + NMLOG_INFO("'%s' interface is enabled", iface.name.c_str()); + // ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_ADDED, iface.name); + if(iface.connected) + { + NMLOG_INFO("'%s' interface is connected", iface.name.c_str()); + ReportActiveInterfaceChange(iface.name, iface.name); + std::string ipversion = {}; + Exchange::INetworkManager::IPAddress addr; + rc = GetIPSettings(iface.name, ipversion, addr); + if (Core::ERROR_NONE == rc) + { + if(!addr.ipaddress.empty()) { + NMLOG_INFO("'%s' interface have ip '%s'", iface.name.c_str(), addr.ipaddress.c_str()); + ReportIPAddressChange(iface.name, addr.ipversion, addr.ipaddress, Exchange::INetworkManager::IP_ACQUIRED); + } + } + } + } + } + + _interfaces->Release(); + } + } + else + NMLOG_INFO("GetAvailableInterfaces Failed"); + } + + void NetworkManagerImplementation::platform_logging(const NetworkManagerLogger::LogLevel& level) + { + /* set networkmanager daemon log level based on current plugin log level */ + if(NetworkManagerLogger::DEBUG_LEVEL != level) + return; + nmUtils::setNetworkManagerlogLevelToTrace(); + } + + void NetworkManagerImplementation::platform_deinit() + { + return; + } + + void NetworkManagerImplementation::platform_init() + { + ::_instance = this; + _nmGdbusClient = NetworkManagerClient::getInstance(); + _nmGdbusEvents = NetworkManagerEvents::getInstance(); + getInitialConnectionState(); + nmUtils::getDeviceProperties(); // get interface name form '/etc/device.proprties' + _nmGdbusClient->modifyDefaultConnectionsConfig(); + + // Set default interface based on device state + deviceInfo ethDevInfo, wifiDevInfo; + if(_nmGdbusClient->getDeviceInfo(GnomeUtils::getEthIfname(), ethDevInfo)) + { + if(ethDevInfo.state > NM_DEVICE_STATE_DISCONNECTED && ethDevInfo.state < NM_DEVICE_STATE_DEACTIVATING) + setDefaultInterface(GnomeUtils::getEthIfname()); + else + setDefaultInterface(GnomeUtils::getWifiIfname()); + } + else + setDefaultInterface(GnomeUtils::getWifiIfname()); + + NMLOG_INFO("default interface is %s", getDefaultInterface().c_str()); + + // Start event monitoring + _nmGdbusEvents->startNetworkMangerEventMonitor(); + } + + uint32_t NetworkManagerImplementation::GetAvailableInterfaces (Exchange::INetworkManager::IInterfaceDetailsIterator*& interfacesItr/* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + std::vector interfaceList; + if(_nmGdbusClient->getAvailableInterfaces(interfaceList)) + { + for (const auto& iface : interfaceList) + { + if (iface.name == "eth0") + m_ethEnabled.store(iface.enabled); + else if (iface.name == "wlan0") + m_wlanEnabled.store(iface.enabled); + } + rc = Core::ERROR_NONE; + } + else + NMLOG_ERROR("GetAvailableInterfaces failed"); + using Implementation = RPC::IteratorType; + interfacesItr = Core::Service::Create(interfaceList); + if(interfacesItr == nullptr){ + return Core::ERROR_GENERAL; + } + + return rc; + } +#if 0 + uint32_t NetworkManagerImplementation::GetPrimaryInterface (string& interface /* @out */) + { + if(_nmGdbusClient->getPrimaryInterface(interface)) + return Core::ERROR_NONE; + else + { + if(_nmGdbusClient->getDefaultInterface(interface)) + { + _instance->setDefaultInterface(interface); + return Core::ERROR_NONE; + } + else + return Core::ERROR_GENERAL; + } + } +#endif + uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool enabled /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->setInterfaceState(interface, enabled)) + { + if(interface == nmUtils::wlanIface() && _instance != NULL) + _instance->m_wlanEnabled.store(enabled); + else if(interface == nmUtils::ethIface() && _instance != NULL) + _instance->m_ethEnabled.store(enabled); + rc = Core::ERROR_NONE; + } + else + NMLOG_ERROR("SetInterfaceState failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::GetInterfaceState(const string& interface/* @in */, bool& isEnabled /* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->getInterfaceState(interface, isEnabled)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("GetInterfaceState failed"); + return rc; + } + + /* @brief Get IP Address Of the Interface */ + uint32_t NetworkManagerImplementation::GetIPSettings(string& interface /* @inout */, const string &ipversion /* @in */, IPAddress& result /* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->getIPSettings(interface, ipversion, result)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("GetIPSettings failed"); + return rc; + } + + /* @brief Set IP Address Of the Interface */ + uint32_t NetworkManagerImplementation::SetIPSettings(const string& interface /* @in */, const IPAddress& address /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if (("IPv4" != address.ipversion) && ("IPv6" != address.ipversion)) + { + return Core::ERROR_BAD_REQUEST; + } + if(_nmGdbusClient->setIPSettings(interface, address)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("SetIPSettings failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + _nmGdbusEvents->setwifiScanOptions(true); /* Enable event posting */ + if(_nmGdbusClient->startWifiScan()) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("StartWiFiScan failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::StopWiFiScan(void) + { + uint32_t rc = Core::ERROR_GENERAL; + _nmGdbusEvents->setwifiScanOptions(false); /* disable event posting */ + rc = Core::ERROR_NONE; + return rc; + } + + uint32_t NetworkManagerImplementation::GetKnownSSIDs(IStringIterator*& ssids /* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + std::list ssidList; + if(_nmGdbusClient->getKnownSSIDs(ssidList) && !ssidList.empty()) + { + ssids = Core::Service::Create(ssidList); + if(ssids == nullptr) { + return Core::ERROR_GENERAL; + } + rc = Core::ERROR_NONE; + } + else + NMLOG_ERROR("GetKnownSSIDs failed"); + + return rc; + } + + uint32_t NetworkManagerImplementation::AddToKnownSSIDs(const WiFiConnectTo& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->addToKnownSSIDs(ssid)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("addToKnownSSIDs failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::RemoveKnownSSID(const string& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->removeKnownSSIDs(ssid)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("RemoveKnownSSID failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::WiFiConnect(const WiFiConnectTo& ssid /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + // Check the last scanning time and if it exceeds 5 sec do a rescanning + if(!_nmGdbusClient->isWifiScannedRecently()) + { + _nmGdbusEvents->setwifiScanOptions(false); /* Enable event posting */ + if(!_nmGdbusClient->startWifiScan()) + NMLOG_WARNING("scanning failed but try to connect"); + } + + if(ssid.ssid.empty() && _instance != NULL) + { + const string lastConnectedSSID = getLastConnectedSSID(); + NMLOG_WARNING("ssid is empty activating last connected ssid (%s) !", lastConnectedSSID.c_str()); + if(_nmGdbusClient->activateKnownConnection(GnomeUtils::getWifiIfname(), lastConnectedSSID)) + rc = Core::ERROR_NONE; + } + else if(ssid.ssid.size() <= 32) + { + if(_nmGdbusClient->wifiConnect(ssid)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("WiFiConnect failed"); + } + else + NMLOG_WARNING("SSID is invalid"); + + return rc; + } + + uint32_t NetworkManagerImplementation::WiFiDisconnect(void) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->wifiDisconnect()) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("WiFiDisconnect failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->getConnectedSSID(ssidInfo)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("getConnectedSSID failed"); + return rc; + } + +#if 0 + uint32_t NetworkManagerImplementation::GetWiFiSignalQuality(string& ssid /* @out */, string& strength /* @out */, WiFiSignalQuality& quality /* @out */) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->getWiFiSignalQuality(ssid, strength, quality)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("GetWiFiSignalQuality failed"); + return rc; + } +#endif + + uint32_t NetworkManagerImplementation::GetWifiState(WiFiState &state) + { + uint32_t rc = Core::ERROR_GENERAL; + if(_nmGdbusClient->getWifiState(state)) + rc = Core::ERROR_NONE; + else + NMLOG_ERROR("GetWifiState failed"); + return rc; + } + + uint32_t NetworkManagerImplementation::StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) + { + uint32_t rc = Core::ERROR_NONE; + if(method == WIFI_WPS_SERIALIZED_PIN || method == WIFI_WPS_PIN) + { + NMLOG_ERROR("WPS PIN method is not supported as of now"); + return Core::ERROR_RPC_CALL_FAILED; + } + _nmGdbusEvents->setwifiScanOptions(false); + if(!_nmGdbusClient->startWifiScan()) + { + NMLOG_WARNING("scanning request failed; trying to connect wps"); + } + + if(_nmGdbusClient->startWPS()) + NMLOG_INFO ("start WPS success"); + else { + rc = Core::ERROR_GENERAL; + NMLOG_ERROR("start WPS failed"); + } + + return rc; + } + + uint32_t NetworkManagerImplementation::StopWPS(void) + { + uint32_t rc = Core::ERROR_NONE; + if(_nmGdbusClient->stopWPS()) + NMLOG_INFO ("stop success"); + else { + NMLOG_ERROR("stop WPS failed"); + rc = Core::ERROR_GENERAL; + } + return rc; + } + + /* @brief Set the dhcp hostname */ + uint32_t NetworkManagerImplementation::SetHostname(const string& hostname /* @in */) + { + uint32_t rc = Core::ERROR_GENERAL; + + if(hostname.length() < 1 || hostname.length() > 32) + { + NMLOG_ERROR("Invalid hostname length: %zu", hostname.length()); + return Core::ERROR_BAD_REQUEST; + } + + if(_nmGdbusClient->setHostname(hostname)) + { + NMLOG_INFO("Successfully set hostname to: %s", hostname.c_str()); + rc = Core::ERROR_NONE; + } + else + { + NMLOG_ERROR("SetHostname failed for hostname: %s", hostname.c_str()); + } + + return rc; + } + } +} diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp new file mode 100644 index 000000000..ad1659a96 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp @@ -0,0 +1,769 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include +#include + +#include "NetworkManagerLogger.h" +#include "NetworkManagerGdbusUtils.h" +#include "NetworkManagerGdbusMgr.h" +#include "NetworkManagerImplementation.h" +#include +#include // for struct in_addr + +namespace WPEFramework +{ + namespace Plugin + { + static const char* ifnameEth = "eth0"; + static const char* ifnameWlan = "wlan0"; + + bool GnomeUtils::getIPv4AddrFromIPv4ConfigProxy(GDBusProxy *ipProxy, std::string& ipAddr, uint32_t& prefix) + { + GVariantIter *iter; + GVariantIter *dictIter; + const gchar *key; + GVariant *value; + gchar *address = nullptr; + bool ret = false; + + if(ipProxy == nullptr) + { + NMLOG_ERROR("ipv4 ipProxy is null"); + return false; + } + + GVariant *addressDataVariant = g_dbus_proxy_get_cached_property(ipProxy, "AddressData"); + if (addressDataVariant != NULL) { + g_variant_get(addressDataVariant, "aa{sv}", &iter); + while (g_variant_iter_loop(iter, "a{sv}", &dictIter)) { + while (g_variant_iter_loop(dictIter, "{&sv}", &key, &value)) { + if (g_strcmp0(key, "address") == 0) { + address = g_variant_dup_string(value, NULL); + if(address!= NULL) + { + ipAddr = address; // strlen of "255.255.255.255" + NMLOG_DEBUG("Address: %s", ipAddr.c_str()); + ret = true; + } + else + ret = false; + g_free(address); + } else if (g_strcmp0(key, "prefix") == 0) { + prefix = g_variant_get_uint32(value); + NMLOG_DEBUG("Prefix: %u", prefix); + } + } + } + g_variant_iter_free(iter); + g_variant_unref(addressDataVariant); + } + return ret; + } + + bool GnomeUtils::getIPv6AddrFromIPv6ConfigProxy( GDBusProxy *ipProxy, std::string& ipAddr, uint32_t& prefix) + { + GVariantIter *iter = NULL; + GVariantIter *dictIter = NULL; + const gchar *key = NULL; + GVariant *value = NULL; + gchar *address = NULL; + std::string ipAddress{}; + if(ipProxy == NULL) + { + NMLOG_ERROR("ipv6 ipProxy is null"); + return false; + } + + GVariant *addressDataVariant = g_dbus_proxy_get_cached_property(ipProxy, "AddressData"); + if (addressDataVariant != NULL) { + g_variant_get(addressDataVariant, "aa{sv}", &iter); + while (g_variant_iter_loop(iter, "a{sv}", &dictIter)) { + while (g_variant_iter_loop(dictIter, "{&sv}", &key, &value)) + { + if (g_strcmp0(key, "address") == 0) + { + address = g_variant_dup_string(value, NULL); + if(address!= NULL) + { + ipAddress = address; // strlen of "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF" + if (ipAddress.compare(0, 5, "fe80:") == 0 || + ipAddress.compare(0, 6, "fe80::") == 0) { // It's link-local starts with fe80 + NMLOG_DEBUG("link-local ip: %s", address); + } + else + { + // TODO use ip list multiple ipv6 SLACC adress may occure + ipAddr = ipAddress; + NMLOG_DEBUG("ipv6 addr: %s", ipAddress.c_str()); + } + } + g_free(address); + } + else if (g_strcmp0(key, "prefix") == 0) + { + prefix = g_variant_get_uint32(value); + NMLOG_DEBUG("Prefix: %u", prefix); + } + } + } + g_variant_iter_free(iter); + g_variant_unref(addressDataVariant); + } + + if(ipAddr.empty()) + return false; + return true; + } + + bool GnomeUtils::convertSsidInfoToJsonObject(Exchange::INetworkManager::WiFiSSIDInfo& wifiInfo, JsonObject& ssidObj) + { + std::string freq{}; + + ssidObj["ssid"] = wifiInfo.ssid; + ssidObj["security"] = static_cast(wifiInfo.security); + ssidObj["strength"] = wifiInfo.strength; + ssidObj["frequency"] = wifiInfo.frequency; + return true; + } + + // TODO change this function + const char* GnomeUtils::getWifiIfname() { return ifnameWlan; } + const char* GnomeUtils::getEthIfname() { return ifnameEth; } + + uint8_t GnomeUtils::wifiSecurityModeFromApFlags(const std::string& ssid, guint32 flags, guint32 wpaFlags, guint32 rsnFlags) + { + uint8_t security = Exchange::INetworkManager::WIFI_SECURITY_NONE; + if ((flags != NM_802_11_AP_FLAGS_PRIVACY) && (wpaFlags == NM_802_11_AP_SEC_NONE) && (rsnFlags == NM_802_11_AP_SEC_NONE)) // Open network + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_NONE; + else if ((rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_PSK) && (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE)) // WPA2/WPA3 Transition + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; + else if (rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_SAE) // Pure WPA3 (SAE only): WPA3-Personal + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE; + else if (wpaFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X || rsnFlags & NM_802_11_AP_SEC_KEY_MGMT_802_1X) // WPA2/WPA3 Enterprise: EAP present in either WPA or RSN + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_EAP; + else + security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; // WPA2-PSK + + //NMLOG_INFO("ap [%s] security str %s", ssid.c_str(), nmUtils::getSecurityModeString(flags, wpaFlags, rsnFlags).c_str()); + return security; + } + + bool GnomeUtils::getCachedPropertyU(GDBusProxy* proxy, const char* propertiy, uint32_t *value) + { + GVariant* result = NULL; + result = g_dbus_proxy_get_cached_property(proxy, propertiy); + if (result == NULL) { + NMLOG_ERROR("Failed to get '%s' properties", propertiy); + return false; + } + + if (g_variant_is_of_type (result, G_VARIANT_TYPE_UINT32)) { + *value = g_variant_get_uint32(result); + //NMLOG_DEBUG("%s: %d", propertiy, *value); + } + else + NMLOG_WARNING("Unexpected type returned property: %s", g_variant_get_type_string(result)); + g_variant_unref(result); + return true; + } + + bool GnomeUtils::getCachedPropertyBoolean(GDBusProxy* proxy, const char* property, bool *value) + { + GVariant* result = nullptr; + result = g_dbus_proxy_get_cached_property(proxy, property); + if (result == nullptr) { + NMLOG_ERROR("Failed to get '%s' properties", property); + return false; + } + + if (g_variant_is_of_type(result, G_VARIANT_TYPE_BOOLEAN)) { + *value = g_variant_get_boolean(result); + } + else + NMLOG_WARNING("Unexpected type returned property: %s", g_variant_get_type_string(result)); + g_variant_unref(result); + return true; + } + + bool GnomeUtils::getDevicePropertiesByPath(DbusMgr& m_dbus, const char* devicePath, deviceInfo& properties) + { + GVariant *devicesVar = NULL; + GDBusProxy* nmProxy = NULL; + u_int32_t value = 0; + bool managedValue; + + nmProxy = m_dbus.getNetworkManagerDeviceProxy(devicePath); + if(nmProxy == NULL) + return false; + + if(GnomeUtils::getCachedPropertyU(nmProxy, "DeviceType", &value)) + properties.deviceType = static_cast(value); + else + NMLOG_ERROR("'DeviceType' property failed"); + + if(GnomeUtils::getCachedPropertyBoolean(nmProxy, "Managed", &managedValue)) + properties.managed = managedValue; + else + NMLOG_ERROR("'Managed' property failed"); + devicesVar = g_dbus_proxy_get_cached_property(nmProxy, "HwAddress"); + if (devicesVar) { + const gchar *mac = g_variant_get_string(devicesVar, NULL); + if(mac != NULL) + { + properties.MAC = mac; + } + g_variant_unref(devicesVar); + } + else + NMLOG_ERROR("'mac' property failed"); + + devicesVar = g_dbus_proxy_get_cached_property(nmProxy, "Interface"); + if (devicesVar) { + const gchar *iface = g_variant_get_string(devicesVar, NULL); + if(iface != NULL) + { + properties.interface = iface; + } + g_variant_unref(devicesVar); + } + else + NMLOG_ERROR("'Interface' property failed"); + + guint32 devState, stateReason; + devicesVar = g_dbus_proxy_get_cached_property(nmProxy, "StateReason"); + if (devicesVar) { + g_variant_get(devicesVar, "(uu)", &devState, &stateReason); + properties.state = static_cast(devState); + properties.stateReason = static_cast(stateReason); + g_variant_unref(devicesVar); + } + else + NMLOG_ERROR("'StateReason' property failed"); + + g_object_unref(nmProxy); + return true; + } + + bool GnomeUtils::getDeviceInfoByIfname(DbusMgr& m_dbus, const char* ifaceName, deviceInfo& properties) + { + GError *error = NULL; + GVariant *devicesVar = NULL; + bool ret = false; + + GDBusProxy* nmProxy = m_dbus.getNetworkManagerProxy(); + if(nmProxy == NULL) + return false; + + devicesVar = g_dbus_proxy_call_sync(nmProxy, "GetDevices", NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + if (error) { + NMLOG_ERROR("Error calling GetDevices method: %s", error->message); + g_error_free(error); + g_object_unref(nmProxy); + return false; + } + + GVariantIter* iter = NULL; + gchar* devicePath = NULL; + g_variant_get(devicesVar, "(ao)", &iter); + while (iter != NULL && g_variant_iter_loop(iter, "o", &devicePath)) + { + if(devicePath == NULL ) + continue; + + if(getDevicePropertiesByPath(m_dbus, devicePath, properties) && properties.interface == ifaceName) + { + properties.path = devicePath; + g_free(devicePath); + ret = true; + break; + } + } + + g_variant_iter_free(iter); + if(!ret) + NMLOG_ERROR("'%s' interface not found", ifaceName); + if(devicesVar) + g_variant_unref(devicesVar); + if(nmProxy) + g_object_unref(nmProxy); + return ret; + } + + bool GnomeUtils::getDeviceByIpIface(DbusMgr& m_dbus, const gchar *ifaceName, std::string& path) + { + GError *error = nullptr; + GDBusProxy* nmProxy = m_dbus.getNetworkManagerProxy(); + if(nmProxy == NULL) + return false; + + GVariant *result = g_dbus_proxy_call_sync( + nmProxy, + "GetDeviceByIpIface", + g_variant_new("(s)", ifaceName), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error); + + if (result == nullptr) { + NMLOG_ERROR("Error calling GetDeviceByIpIface: %s", error->message); + g_clear_error(&error); + g_object_unref(nmProxy); + return false; + } + + gchar *devicePath; + g_variant_get(result, "(o)", &devicePath); + + g_variant_unref(result); + g_object_unref(nmProxy); + + path = std::string(devicePath); + g_free(devicePath); + + return true; + } + + bool GnomeUtils::getApDetails(DbusMgr& m_dbus, const char* apPath, Exchange::INetworkManager::WiFiSSIDInfo& wifiInfo) + { + guint32 flags= 0, wpaFlags= 0, rsnFlags= 0, freq= 0, bitrate= 0; + uint8_t strength = 0; + gint16 noise = 0; + NM80211Mode mode = NM_802_11_MODE_UNKNOWN; + bool ret = false; + GVariant* ssidVariant = NULL; + + GDBusProxy* proxy = m_dbus.getNetworkManagerAccessPointProxy(apPath); + if (proxy == NULL) { + return false; + } + + gsize ssid_length = 0; + ssidVariant = g_dbus_proxy_get_cached_property(proxy,"Ssid"); + if (!ssidVariant) { + NMLOG_ERROR("Failed to get AP properties."); + g_object_unref(proxy); + return false; + } + + const guchar *ssid_data = static_cast(g_variant_get_fixed_array(ssidVariant, &ssid_length, sizeof(guchar))); + if (ssid_data && ssid_length > 0 && ssid_length <= 32) + { + GVariant* result = NULL; + gchar *_bssid = NULL; + wifiInfo.ssid.assign(reinterpret_cast(ssid_data), ssid_length); + result = g_dbus_proxy_get_cached_property(proxy,"HwAddress"); + if (!result) { + NMLOG_ERROR("Failed to get AP properties."); + g_object_unref(proxy); + return false; + } + g_variant_get(result, "s", &_bssid); + if(_bssid != NULL) { + wifiInfo.bssid = _bssid; + g_free(_bssid); + } + g_variant_unref(result); + result = NULL; + result = g_dbus_proxy_get_cached_property(proxy,"Strength"); + if (!result) { + NMLOG_ERROR("Failed to get AP properties."); + g_object_unref(proxy); + return false; + } + g_variant_get(result, "y", &strength); + wifiInfo.strength = GnomeUtils::convertPercentageToSignalStrength((int)strength); + g_variant_unref(result); + + GnomeUtils::getCachedPropertyU(proxy, "Flags", &flags); + GnomeUtils::getCachedPropertyU(proxy, "WpaFlags", &wpaFlags); + GnomeUtils::getCachedPropertyU(proxy, "RsnFlags", &rsnFlags); + GnomeUtils::getCachedPropertyU(proxy, "Mode", (guint32*)&mode); + GnomeUtils::getCachedPropertyU(proxy, "Frequency", &freq); + GnomeUtils::getCachedPropertyU(proxy, "MaxBitrate", &bitrate); + + wifiInfo.frequency = ((double)freq/1000); + wifiInfo.rate = bitrate; + wifiInfo.security = static_cast(wifiSecurityModeFromApFlags(wifiInfo.ssid, flags, wpaFlags, rsnFlags)); + if(noise <= 0 && noise >= DEFAULT_NOISE) + wifiInfo.noise = noise; + else + wifiInfo.noise = 0; + + // NMLOG_DEBUG("SSID: %s", wifiInfo.m_ssid.c_str()); + // NMLOG_DEBUG("bssid %s", wifiInfo.m_bssid.c_str()); + // NMLOG_DEBUG("strength : %s dbm (%d%%)", wifiInfo.m_signalStrength.c_str(), strength); + // NMLOG_DEBUG("frequency : %f MHz", wifiInfo.m_frequency); + // NMLOG_DEBUG("bitrate : %s kbit/s", wifiInfo.m_rate.c_str()); + // NMLOG_DEBUG("securityMode : %d", wifiInfo.m_securityMode); + + // TODO add noice + ret = true; + } + else { + // NMLOG_DEBUG("Invalid/hidden SSID: %zu (maximum is 32)", ssid_length); + ret = false; + } + + if(ssidVariant) + g_variant_unref(ssidVariant); + if(proxy) + g_object_unref(proxy); + + return ret; + } + + // Function to convert percentage (0-100) to dBm string + int GnomeUtils::convertPercentageToSignalStrength(int percentage) { + + if (percentage <= 0 || percentage > 100) { + return 0; + } + + /* + * -30 dBm to -50 dBm: Excellent signal strength. + * -50 dBm to -60 dBm: Very good signal strength. + * -60 dBm to -70 dBm: Good signal strength; acceptable for basic internet browsing. + * -70 dBm to -80 dBm: Weak signal; performance may degrade, slower speeds, and possible dropouts. + * -80 dBm to -90 dBm: Very poor signal; likely unusable or highly unreliable. + * Below -90 dBm: Disconnected or too weak to establish a stable connection. + */ + + // dBm range: -30 dBm (strong) to -90 dBm (weak) + const int max_dBm = -30; + const int min_dBm = -90; + int dBm_value = max_dBm + ((min_dBm - max_dBm) * (100 - percentage)) / 100; + return dBm_value; + } + + bool GnomeUtils::getWifiConnectionPaths(DbusMgr& m_dbus, const char* devicePath, std::list& paths) + { + GDBusProxy* nmProxy = NULL; + GVariant* result = NULL; + bool ret = false; + nmProxy = m_dbus.getNetworkManagerDeviceProxy(devicePath); + if(nmProxy == NULL) + return false; + + result = g_dbus_proxy_get_cached_property(nmProxy, "AvailableConnections"); + if (!result) { + NMLOG_ERROR("Failed to get AvailableConnections property."); + g_object_unref(nmProxy); + return ret; + } + + GVariantIter* iter = NULL; + gchar* connPath = NULL; + g_variant_get(result, "ao", &iter); + while (g_variant_iter_loop(iter, "o", &connPath)) + { + if(connPath == NULL) + continue; + if(g_strcmp0(connPath, "/") != 0) + { + paths.push_back(connPath); + // NMLOG_DEBUG("AvailableConnections Path: %s", connPath); + ret = true; + } + } + + g_variant_iter_free(iter); + g_variant_unref(result); + g_object_unref(nmProxy); + return ret; + } + + bool GnomeUtils::getConnectionPaths(DbusMgr& m_dbus, std::list& pathsList) + { + GDBusProxy *sProxy= NULL; + GError *error= NULL; + GVariant *listProxy = NULL; + char **paths = NULL; + + sProxy = m_dbus.getNetworkManagerSettingsProxy(); + if(sProxy == NULL) + return false; + + listProxy = g_dbus_proxy_call_sync(sProxy, + "ListConnections", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if(listProxy == NULL) + { + if (!error) { + NMLOG_ERROR("ListConnections failed: %s", error->message); + g_error_free(error); + g_object_unref(sProxy); + return false; + } + else + NMLOG_ERROR("ListConnections proxy failed no error message"); + } + + g_variant_get(listProxy, "(^ao)", &paths); + g_variant_unref(listProxy); + + if(paths == NULL) + { + NMLOG_WARNING("no connection path available"); + g_object_unref(sProxy); + return false; + } + + for (int i = 0; paths[i]; i++) { + //NMLOG_DEBUG("Connection path: %s", paths[i]); + pathsList.push_back(paths[i]); + } + + g_strfreev(paths); + g_object_unref(sProxy); + if(pathsList.empty()) + return false; + + return true; + } + + bool GnomeUtils::activateConnection(DbusMgr& m_dbus, const std::string& connectionProfile, const std::string& devicePath) + { + GError* error = nullptr; + GDBusProxy* nmProxy = m_dbus.getNetworkManagerProxy(); + if(nmProxy == NULL) + return false; + + GVariant* result = g_dbus_proxy_call_sync( + nmProxy, + "ActivateConnection", + g_variant_new("(ooo)", connectionProfile.c_str(), devicePath.c_str(), "/"), + G_DBUS_CALL_FLAGS_NONE, + -1, + nullptr, + &error + ); + + if (error) { + NMLOG_ERROR("ActivateConnection Error: %s", error->message); + g_error_free(error); + } else if (result != nullptr) { + g_variant_unref(result); + } + return true; + } + + bool GnomeUtils::getSettingsConnectionPath(DbusMgr &m_dbus, std::string& connectionPath, const std::string& interface) + { + GDBusProxy *nmProxy = nullptr; + bool found = false; + nmProxy = m_dbus.getNetworkManagerProxy(); + if(nmProxy == nullptr) + return false; + GVariant *activeConnections = g_dbus_proxy_get_cached_property(nmProxy, "ActiveConnections"); + if (activeConnections == nullptr) { + NMLOG_ERROR("Error retrieving active connections"); + g_object_unref(nmProxy); + return false; + } + + GVariantIter iter; + g_variant_iter_init(&iter, activeConnections); + gchar *activeConnectionPath = nullptr; + while (g_variant_iter_loop(&iter, "o", &activeConnectionPath)) { + GDBusProxy *activeConnectionProxy = m_dbus.getNetworkManagerActiveConnProxy(activeConnectionPath); + + if (activeConnectionProxy == nullptr) { + NMLOG_ERROR("Error creating active connection proxy"); + continue; + } + + GVariant *devicesVar = g_dbus_proxy_get_cached_property(activeConnectionProxy, "Devices"); + if (devicesVar == nullptr) { + NMLOG_ERROR("Error retrieving devices property"); + g_object_unref(activeConnectionProxy); + continue; + } + + GVariantIter devicesIter; + g_variant_iter_init(&devicesIter, devicesVar); + gchar *devicePath = nullptr; + + while (g_variant_iter_loop(&devicesIter, "o", &devicePath)) { + GDBusProxy *deviceProxy = m_dbus.getNetworkManagerDeviceProxy(devicePath); + + if (deviceProxy == nullptr) { + NMLOG_ERROR("Error creating device proxy"); + continue; + } + + GVariant *ifaceProperty = g_dbus_proxy_get_cached_property(deviceProxy, "Interface"); + if (ifaceProperty) { + const gchar *iface = g_variant_get_string(ifaceProperty, nullptr); + if (interface == iface) { + found = true; + GVariant *connectionProperty = g_dbus_proxy_get_cached_property(activeConnectionProxy, "Connection"); + if (connectionProperty) { + connectionPath = g_variant_get_string(connectionProperty, nullptr); + g_variant_unref(connectionProperty); + } else { + NMLOG_ERROR("Error retrieving connection property"); + } + + g_variant_unref(ifaceProperty); + g_object_unref(deviceProxy); + break; + } + g_variant_unref(ifaceProperty); + } + + g_object_unref(deviceProxy); + } + g_variant_unref(devicesVar); + g_object_unref(activeConnectionProxy); + + if (found) { + break; + } + } + + g_variant_unref(activeConnections); + g_object_unref(nmProxy); + return true; + } + + // Convert IPv4 string to network byte order (NBO) + uint32_t GnomeUtils::ip4StrToNBO(const std::string &ipAddress) + { + struct in_addr addr; + if (inet_pton(AF_INET, ipAddress.c_str(), &addr) != 1) { + NMLOG_ERROR("Invalid IPv4 address format: %s", ipAddress.c_str()); + } + return addr.s_addr; + } + + // Convert an IPv6 string address to an array of bytes + std::array GnomeUtils::ip6StrToNBO(const std::string &ipAddress) + { + struct in6_addr addr6; + inet_pton(AF_INET6, ipAddress.c_str(), &addr6); + std::array ip6{}; + std::memcpy(ip6.data(), &addr6, 16); + return ip6; + } + + // Helper function to convert a raw IPv4 address to human-readable format + std::string GnomeUtils::ip4ToString(uint32_t ip) { + ip = ntohl(ip); // Convert from network to host byte order + char buf[INET_ADDRSTRLEN] = {0}; + snprintf(buf, sizeof(buf), "%u.%u.%u.%u", + (ip >> 24) & 0xFF, + (ip >> 16) & 0xFF, + (ip >> 8) & 0xFF, + ip & 0xFF); + return std::string(buf); + } + + // Helper function to convert a raw IPv6 address to human-readable format + std::string GnomeUtils::ip6ToString(const uint8_t *ipv6) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, ipv6, buf, sizeof(buf)); + return std::string(buf); + } + + // Helper function to add elements from GVariant to GVariantBuilder + void GnomeUtils::addGvariantToBuilder(GVariant *variant, GVariantBuilder *builder, gboolean excludeRouteMetric) { + GVariantIter iter; + + const gchar *key; + GVariant *value; + g_variant_iter_init(&iter, variant); + + // Iterate through key-value pairs in the dictionary + while (g_variant_iter_loop(&iter, "{&sv}", &key, &value)) { + if (excludeRouteMetric && strcmp(key, "route-metric") == 0) { + // Skip adding this key-value pair if exclude_route_metric is TRUE and key is "route-metric" + continue; + } + g_variant_builder_add(builder, "{sv}", key, value); + } + } + + bool GnomeUtils::writePersistentHostname(const std::string& hostname) + { + constexpr const char* HostnameFile = "/opt/persistent/nm.plugin.hostname"; + + if (hostname.empty()) { + NMLOG_ERROR("Cannot write empty hostname to persistent storage"); + return false; + } + + std::ofstream file(HostnameFile); + if (!file.is_open()) { + NMLOG_ERROR("Failed to open %s for writing", HostnameFile); + return false; + } + + file << hostname; + bool success = !file.fail(); + file.close(); + + if (success) + NMLOG_DEBUG("Successfully wrote hostname '%s' to %s", hostname.c_str(), HostnameFile); + else + NMLOG_ERROR("Error writing hostname to %s", HostnameFile); + + return success; + } + + bool GnomeUtils::readPersistentHostname(std::string& hostname) + { + constexpr const char* HostnameFile = "/opt/persistent/nm.plugin.hostname"; + + std::ifstream file(HostnameFile); + if (!file.is_open()) { + NMLOG_DEBUG("Could not open %s - file may not exist yet", HostnameFile); + hostname = ""; + return false; + } + + std::string line; + if (std::getline(file, line)) { + // Remove any whitespace, newlines, etc. + line.erase(line.find_last_not_of("\r\n\t") + 1); + line.erase(0, line.find_first_not_of("\r\n\t")); + hostname = line; + file.close(); + + NMLOG_INFO("Read persistent hostname: '%s'", hostname.c_str()); + return true; + } + + NMLOG_WARNING("Persistent hostname file exists but is empty"); + hostname = ""; + file.close(); + return false; + } + } // Plugin +} // WPEFramework + diff --git a/plugin/gnome/gdbus/NetworkManagerGdbusUtils.h b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.h new file mode 100644 index 000000000..e79c64297 --- /dev/null +++ b/plugin/gnome/gdbus/NetworkManagerGdbusUtils.h @@ -0,0 +1,99 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include +#include +#include + +/* include NetworkManager.h for the defines, but we don't link against libnm. */ +// #include +#include +#include "INetworkManager.h" +#include "NetworkManagerGdbusMgr.h" + +using namespace std; + +struct apInfo { + string ssid; + string bssid; + NM80211ApFlags flag; + NM80211ApSecurityFlags wpaFlag; + NM80211ApSecurityFlags rsnFlag; + NM80211Mode mode; + guint32 frequency; + guint32 bitrate; + guint32 strength; +}; + +struct deviceInfo +{ + std::string interface; + std::string activeConnPath; + std::string path; + std::string MAC; + bool managed; + NMDeviceState state; + NMDeviceStateReason stateReason; + NMDeviceType deviceType; +}; + +#define G_VARIANT_LOOKUP(dict, key, format, ...) \ + if (!g_variant_lookup(dict, key, format, __VA_ARGS__)) {\ + NMLOG_ERROR("g_variant Key '%s' not found", key); \ + return FALSE; \ + } + +namespace WPEFramework +{ + namespace Plugin + { + class GnomeUtils { + public: + static bool getDeviceByIpIface(DbusMgr& m_dbus, const gchar *iface_name, std::string& path); + static bool getApDetails(DbusMgr& m_dbus, const char* apPath, Exchange::INetworkManager::WiFiSSIDInfo& wifiInfo); + static bool getConnectionPaths(DbusMgr& m_dbus, std::list& pathsList); + static bool getWifiConnectionPaths(DbusMgr& m_dbus, const char* devicePath, std::list& paths); + static bool getDevicePropertiesByPath(DbusMgr& m_dbus, const char* devPath, deviceInfo& properties); + static bool getDeviceInfoByIfname(DbusMgr& m_dbus, const char* ifname, deviceInfo& properties); + static bool getCachedPropertyU(GDBusProxy* proxy, const char* propertiy, uint32_t *value); + static bool getCachedPropertyBoolean(GDBusProxy* proxy, const char* property, bool *value); + static bool getIPv4AddrFromIPv4ConfigProxy(GDBusProxy *ipProxy, std::string& ipAddr, uint32_t& prifix); + static bool getIPv6AddrFromIPv6ConfigProxy(GDBusProxy *ipProxy, std::string& ipAddr, uint32_t& prifix); + static bool activateConnection(DbusMgr& m_dbus, const std::string& connectionProfile, const std::string& devicePath); + static bool getSettingsConnectionPath(DbusMgr &m_dbus, std::string& connectionPath, const std::string& interface); + static uint32_t ip4StrToNBO(const std::string &ipAddress); + static std::array ip6StrToNBO(const std::string &ipAddress); + static std::string ip4ToString(uint32_t ip); + static std::string ip6ToString(const uint8_t *ipv6); + static void addGvariantToBuilder(GVariant *variant, GVariantBuilder *builder, gboolean excludeRouteMetric); + + static bool convertSsidInfoToJsonObject(Exchange::INetworkManager::WiFiSSIDInfo& wifiInfo, JsonObject& ssidObj); + static int convertPercentageToSignalStrength(int percentage); + static uint8_t wifiSecurityModeFromApFlags(const std::string& ssid, guint32 flags, guint32 wpaFlags, guint32 rsnFlags); + static const char* getWifiIfname(); + static const char* getEthIfname(); + static bool writePersistentHostname(const std::string& hostname); + static bool readPersistentHostname(std::string& hostname); + }; + + } // Plugin +} // WPEFramework + diff --git a/plugin/gnome/scripts/nm_interface_handler.sh b/plugin/gnome/scripts/nm_interface_handler.sh new file mode 100644 index 000000000..cf6d786fc --- /dev/null +++ b/plugin/gnome/scripts/nm_interface_handler.sh @@ -0,0 +1,79 @@ +#!/bin/sh +############################################################################## +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## + +# nm_interface_handler.sh +# +# This script manages Ethernet and Wi-Fi interfaces based on marker files. +# If specific marker files exist, it marks eth0 or wlan0 as unmanaged +# in NetworkManager, preventing automatic configuration. +# +# Invocation: +# Called automatically during system startup as part of the thunder-startup-services +# WPEFramework NetworkManager plugin service using ExecStartPre in: +# wpeframework-networkmanager.service + +. /etc/device.properties + +# Get BOOT_TYPE +if [ -f /tmp/bootType ]; then + BOOT_TYPE=$(grep "BOOT_TYPE" /tmp/bootType | cut -d '=' -f 2) +else + BOOT_TYPE="" +fi + +# Ethernet +NSM_ETH_MARKER="/opt/persistent/ethernet_disallowed" +NM_ETH_MARKER="/opt/persistent/ethernet.interface.disable" +ETH_INTERFACE="$ETHERNET_INTERFACE" + +# Wi-Fi +NSM_WIFI_MARKER="/opt/persistent/wifi_disallowed" +NM_WIFI_MARKER="/opt/persistent/wifi.interface.disable" +WIFI_INTERFACE="$WIFI_INTERFACE" + +if [ "$BOOT_TYPE" = "BOOT_MIGRATION" ]; then + echo "`/bin/timestamp` :$0: migration boot removing ethernet and wifi disable marker file" >> /opt/logs/NMMonitor.log + rm -f "$NSM_ETH_MARKER" + rm -f "$NSM_WIFI_MARKER" + exit 0 +fi + +# Rename legacy netsrvmger markers to networkmanager markers if they exist +if [ -f "$NSM_ETH_MARKER" ]; then + mv "$NSM_ETH_MARKER" "$NM_ETH_MARKER" +fi + +if [ -f "$NSM_WIFI_MARKER" ]; then + mv "$NSM_WIFI_MARKER" "$NM_WIFI_MARKER" +fi + +# If networkmanager Ethernet marker exists, make eth0 unmanaged +if [ -f "$NM_ETH_MARKER" ]; then + echo "`/bin/timestamp` :$0: disabling eth0 interface " >> /opt/logs/NMMonitor.log + nmcli device disconnect "$ETH_INTERFACE" + nmcli dev set "$ETH_INTERFACE" managed no +fi + +# If networkmanager Wi-Fi marker exists, make wlan0 unmanaged +if [ -f "$NM_WIFI_MARKER" ]; then + echo "`/bin/timestamp` :$0: disabling wlan0 interface " >> /opt/logs/NMMonitor.log + nmcli device disconnect "$WIFI_INTERFACE" + nmcli dev set "$WIFI_INTERFACE" managed no +fi diff --git a/plugin/gnome/systemd/gnome-networkmanager-migration.conf b/plugin/gnome/systemd/gnome-networkmanager-migration.conf new file mode 100644 index 000000000..a8dd0d057 --- /dev/null +++ b/plugin/gnome/systemd/gnome-networkmanager-migration.conf @@ -0,0 +1,5 @@ +[Unit] +After=NM_Bootstrap.service + +[Service] +ExecStartPre=/lib/rdk/nm_interface_handler.sh diff --git a/plugin/gnome/systemd/nm-powermanager.conf b/plugin/gnome/systemd/nm-powermanager.conf new file mode 100644 index 000000000..7e8c3f01a --- /dev/null +++ b/plugin/gnome/systemd/nm-powermanager.conf @@ -0,0 +1,4 @@ +[Unit] +# Ensure NetworkManager starts after PowerManager +Wants=wpeframework-powermanager.service +After=wpeframework-powermanager.service diff --git a/NetworkManagerRDKProxy.cpp b/plugin/rdk/NetworkManagerRDKProxy.cpp similarity index 52% rename from NetworkManagerRDKProxy.cpp rename to plugin/rdk/NetworkManagerRDKProxy.cpp index 32316f398..e8be4680b 100644 --- a/NetworkManagerRDKProxy.cpp +++ b/plugin/rdk/NetworkManagerRDKProxy.cpp @@ -1,7 +1,26 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ #include "NetworkManagerImplementation.h" #include "NetworkManagerConnectivity.h" -#include "WiFiSignalStrengthMonitor.h" +#include "NetworkManagerRDKProxy.h" #include "libIBus.h" +#include using namespace WPEFramework; using namespace WPEFramework::Plugin; @@ -10,357 +29,10 @@ using namespace std; namespace WPEC = WPEFramework::Core; namespace WPEJ = WPEFramework::Core::JSON; -#define IARM_BUS_NM_SRV_MGR_NAME "NET_SRV_MGR" - -#define MAX_IP_ADDRESS_LEN 46 -#define NETSRVMGR_INTERFACES_MAX 16 -#define MAX_ENDPOINTS 5 -#define MAX_ENDPOINT_SIZE 512 -#define MAX_URI_LEN 512 -#define MAX_HOST_NAME_LEN 128 -#define DEFAULT_PING_PACKETS 15 -#define CIDR_NETMASK_IP_LEN 32 -#define INTERFACE_SIZE 10 -#define INTERFACE_LIST 50 -#define MAX_IP_FAMILY_SIZE 10 - - -#define BUFF_LENGTH_64 65 -#define BUFF_LENGTH_256 257 -#define BUFF_MAX 1025 -#define BUFF_MAC 18 -#define BUFF_MIN 17 -#define BUFF_LENGTH_4 4 -#define SSID_SIZE 33 -#define BSSID_BUFF 20 -#define BUFF_LENGTH_24 24 -#define PASSPHRASE_BUFF 385 -#define MAX_SSIDLIST_BUF (48*1024) -#define MAX_FILE_PATH_LEN 4096 - -typedef enum _NetworkManager_EventId_t { - IARM_BUS_NETWORK_MANAGER_EVENT_SET_INTERFACE_ENABLED=50, - IARM_BUS_NETWORK_MANAGER_EVENT_SET_INTERFACE_CONTROL_PERSISTENCE, - IARM_BUS_NETWORK_MANAGER_EVENT_WIFI_INTERFACE_STATE, - IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, - IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, - IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, - IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE, - IARM_BUS_NETWORK_MANAGER_EVENT_INTERNET_CONNECTION_CHANGED, - IARM_BUS_NETWORK_MANAGER_MAX, -} IARM_Bus_NetworkManager_EventId_t; - -typedef enum _IARM_Bus_NMgr_WiFi_EventId_t { - IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged = 1, - IARM_BUS_WIFI_MGR_EVENT_onError, - IARM_BUS_WIFI_MGR_EVENT_onSSIDsChanged, - IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, - IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDsIncr, - IARM_BUS_WIFI_MGR_EVENT_onWIFILNFStateChanged, - IARM_BUS_WIFI_MGR_EVENT_MAX, /*!< Maximum event id*/ -} IARM_Bus_NMgr_WiFi_EventId_t; - - -typedef struct { - char name[16]; - char mac[20]; - unsigned int flags; -} NetSrvMgr_Interface_t; - -typedef struct { - unsigned char size; - NetSrvMgr_Interface_t interfaces[NETSRVMGR_INTERFACES_MAX]; -} IARM_BUS_NetSrvMgr_InterfaceList_t; - -typedef enum _NetworkManager_GetIPSettings_ErrorCode_t -{ - NETWORK_IPADDRESS_ACQUIRED, - NETWORK_IPADDRESS_NOTFOUND, - NETWORK_NO_ROUTE_INTERFACE, - NETWORK_NO_DEFAULT_ROUTE, - NETWORK_DNS_NOT_CONFIGURED, - NETWORK_INVALID_IPADDRESS, -} NetworkManager_GetIPSettings_ErrorCode_t; - -typedef struct { - char interface[16]; - char ipversion[16]; - bool autoconfig; - char ipaddress[MAX_IP_ADDRESS_LEN]; - char netmask[MAX_IP_ADDRESS_LEN]; - char gateway[MAX_IP_ADDRESS_LEN]; - char dhcpserver[MAX_IP_ADDRESS_LEN]; - char primarydns[MAX_IP_ADDRESS_LEN]; - char secondarydns[MAX_IP_ADDRESS_LEN]; - bool isSupported; - NetworkManager_GetIPSettings_ErrorCode_t errCode; -} IARM_BUS_NetSrvMgr_Iface_Settings_t; - -typedef struct { - char interface[16]; - char gateway[MAX_IP_ADDRESS_LEN]; -} IARM_BUS_NetSrvMgr_DefaultRoute_t; - -typedef struct { - char interface[16]; - bool status; -} IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t; - -typedef IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t IARM_BUS_NetSrvMgr_Iface_EventInterfaceEnabledStatus_t; -typedef IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t; - -typedef struct { - char interface[16]; - char ip_address[MAX_IP_ADDRESS_LEN]; - bool is_ipv6; - bool acquired; -} IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t; - -typedef struct { - char oldInterface[16]; - char newInterface[16]; -} IARM_BUS_NetSrvMgr_Iface_EventDefaultInterface_t; - -typedef struct -{ - char server[MAX_HOST_NAME_LEN]; - uint16_t port; - bool ipv6; - char interface[16]; - uint16_t bind_timeout; - uint16_t cache_timeout; - bool sync; - char public_ip[MAX_IP_ADDRESS_LEN]; -} IARM_BUS_NetSrvMgr_Iface_StunRequest_t; - -typedef struct -{ - bool disableConnectivityTest; -} IARM_BUS_NetSrvMgr_configurePNI_t; - - - -/*! Supported values are NONE - 0, WPA - 1, WEP - 2*/ -typedef enum _SsidSecurity -{ - NET_WIFI_SECURITY_NONE = 0, - NET_WIFI_SECURITY_WEP_64, - NET_WIFI_SECURITY_WEP_128, - NET_WIFI_SECURITY_WPA_PSK_TKIP, - NET_WIFI_SECURITY_WPA_PSK_AES, - NET_WIFI_SECURITY_WPA2_PSK_TKIP, - NET_WIFI_SECURITY_WPA2_PSK_AES, - NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP, - NET_WIFI_SECURITY_WPA_ENTERPRISE_AES, - NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, - NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES, - NET_WIFI_SECURITY_WPA_WPA2_PSK, - NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE, - NET_WIFI_SECURITY_WPA3_PSK_AES, - NET_WIFI_SECURITY_WPA3_SAE, - NET_WIFI_SECURITY_NOT_SUPPORTED = 99, -} SsidSecurity; - - -/*! Event states associated with WiFi connection */ -typedef enum _WiFiStatusCode_t { - WIFI_UNINSTALLED, /**< The device was in an installed state, and was uninstalled */ - WIFI_DISABLED, /**< The device is installed (or was just installed) and has not yet been enabled */ - WIFI_DISCONNECTED, /**< The device is not connected to a network */ - WIFI_PAIRING, /**< The device is not connected to a network, but not yet connecting to a network */ - WIFI_CONNECTING, /**< The device is attempting to connect to a network */ - WIFI_CONNECTED, /**< The device is successfully connected to a network */ - WIFI_FAILED /**< The device has encountered an unrecoverable error with the wifi adapter */ -} WiFiStatusCode_t; - -/*! Error code: A recoverable, unexpected error occurred, - * as defined by one of the following values */ -typedef enum _WiFiErrorCode_t { - WIFI_SSID_CHANGED, /**< The SSID of the network changed */ - WIFI_CONNECTION_LOST, /**< The connection to the network was lost */ - WIFI_CONNECTION_FAILED, /**< The connection failed for an unknown reason */ - WIFI_CONNECTION_INTERRUPTED, /**< The connection was interrupted */ - WIFI_INVALID_CREDENTIALS, /**< The connection failed due to invalid credentials */ - WIFI_NO_SSID, /**< The SSID does not exist */ - WIFI_UNKNOWN, /**< Any other error */ - WIFI_AUTH_FAILED /**< The connection failed due to auth failure */ -} WiFiErrorCode_t; - -typedef enum _WiFiLNFState_t { - LNF_SSID_CONNECTED, /**< Connected to the LNF network */ - LNF_SSID_CONNECT_FAILED, /**< failed connect to the LNF network */ - LNF_ENDPOINT_BACKOFF_TIME, /**< lnf in last requested backoff time */ - LNF_ENDPOINT_CONNECTED, /**< lnf connected to end point and get the LFAT */ - LNF_RECEIVED_PRIVATE_SSID_INFO, /**< lnf process Got private cridential*/ - LNF_SWITCHING_TO_PRIVATE, /**< lnf connected to private*/ - LNF_SSID_DISCONNECTED, /**< disconnected form lnf ssid */ - LNF_ERROR /**< common lnf error replay from lnf library */ -} WiFiLNFState_t; - -typedef struct _IARM_Bus_WiFiSrvMgr_WPS_Parameters_t -{ - bool pbc; - char pin[9]; - bool status; -} IARM_Bus_WiFiSrvMgr_WPS_Parameters_t; - - -/*! LNF states */ -typedef enum _WiFiLNFStatusCode_t { - LNF_UNITIALIZED, /**< Network manager hasn't started the LNF process */ - LNF_IN_PROGRESS, /**< Network manager has started LNF, and waiting for operation to complete */ - CONNECTED_LNF, /**< Connected to the LNF network */ - CONNECTED_PRIVATE, /** Connected to a network that is not LNF */ - DISCONNECTED_NO_LNF_GATEWAY_DETECTED, /**< unable to connect to LNF network */ - DISCONNECTED_GET_LFAT_FAILED, /**< client wasn't able to acquire an LFAT */ - DISCONNECTED_CANT_CONNECT_TO_PRIVATE // client could obtain LFAT, but couldn't connect to private network */ - /**< The device has encountered an unrecoverable error with the wifi adapter */ -} WiFiLNFStatusCode_t; - - -typedef struct _setWiFiAdapter -{ - bool enable; -} setWiFiAdapter; - -typedef struct _WiFiConnection -{ - char ssid[SSID_SIZE]; - char bssid[BSSID_BUFF]; - char security[BUFF_LENGTH_64]; - char passphrase[PASSPHRASE_BUFF]; - SsidSecurity security_mode; - char security_WEPKey[PASSPHRASE_BUFF]; - char security_PSK[PASSPHRASE_BUFF]; - char eapIdentity[BUFF_LENGTH_256]; - char carootcert[MAX_FILE_PATH_LEN]; - char clientcert[MAX_FILE_PATH_LEN]; - char privatekey[MAX_FILE_PATH_LEN]; - bool persistSSIDInfo; -} WiFiConnection; - -typedef struct _WiFiConnectedSSIDInfo -{ - char ssid[SSID_SIZE]; /**< The name of connected SSID. */ - char bssid[BSSID_BUFF]; /**< The the Basic Service Set ID (mac address). */ - char band[BUFF_MIN]; /**< The frequency band at which the client is conneted to. */ - int securityMode; /**< Current WiFi Security Mode used for connection. */ - int frequency; /**< The Frequency wt which the client is connected to. */ - float rate; /**< The Physical data rate in Mbps */ - float noise; /**< The average noise strength in dBm. */ - float signalStrength; /**< The RSSI value in dBm. */ - -} WiFiConnectedSSIDInfo_t; - -typedef struct _WiFiPairedSSIDInfo -{ - char ssid[SSID_SIZE]; /**< The name of connected SSID. */ - char bssid[BSSID_BUFF]; /**< The the Basic Service Set ID (mac address). */ - char security[BUFF_LENGTH_64]; /**< security of AP */ - SsidSecurity secMode; -} WiFiPairedSSIDInfo_t; - - -typedef enum _WiFiConnectionTypeCode_t { - WIFI_CON_UNKNOWN, - WIFI_CON_WPS, - WIFI_CON_MANUAL, - WIFI_CON_LNF, - WIFI_CON_PRIVATE, - WIFI_CON_MAX, -} WiFiConnectionTypeCode_t; - - -typedef struct _IARM_Bus_WiFiSrvMgr_Param_t { - union { - WiFiLNFStatusCode_t wifiLNFStatus; - WiFiStatusCode_t wifiStatus; - setWiFiAdapter setwifiadapter; - WiFiConnection connect; - WiFiConnection saveSSID; - WiFiConnection clearSSID; - WiFiConnectedSSIDInfo_t getConnectedSSID; - WiFiPairedSSIDInfo_t getPairedSSIDInfo; - WiFiConnectionTypeCode_t connectionType; - struct getPairedSSID { - char ssid[SSID_SIZE]; - } getPairedSSID; - bool isPaired; - } data; - bool status; -} IARM_Bus_WiFiSrvMgr_Param_t; - -typedef struct _IARM_BUS_NetSrvMgr_Iface_EventData_t { - union { - char activeIface[INTERFACE_SIZE]; - char allNetworkInterfaces[INTERFACE_LIST]; - char setInterface[INTERFACE_SIZE]; - char activeIfaceIpaddr[MAX_IP_ADDRESS_LEN]; - }; - char interfaceCount; - bool isInterfaceEnabled; - bool persist; - char ipfamily[MAX_IP_FAMILY_SIZE]; -} IARM_BUS_NetSrvMgr_Iface_EventData_t; - -typedef struct _IARM_BUS_WiFiSrvMgr_EventData_t { - union { - struct _WIFI_STATECHANGE_DATA { - WiFiStatusCode_t state; - } wifiStateChange; - struct _WIFI_ERROR { - WiFiErrorCode_t code; - } wifiError; - struct _WIFI_SSID_LIST { - char ssid_list[MAX_SSIDLIST_BUF]; - bool more_data; - } wifiSSIDList; - struct _WIFI_LNF_STATE { - WiFiLNFState_t state; - float backOffTime; - }wifilnfState; - } data; -} IARM_BUS_WiFiSrvMgr_EventData_t; - -typedef struct _wifiSsidData_t { - char jdata[MAX_SSIDLIST_BUF]; /**< Buffer containing the serialized data. */ - size_t jdataLen; /**< Length of the data buffer. */ -} wifiSsidData_t; - -typedef struct _IARM_Bus_WiFiSrvMgr_SsidList_Param_t { - wifiSsidData_t curSsids; - bool status; -} IARM_Bus_WiFiSrvMgr_SsidList_Param_t; - - -#define IARM_BUS_NETSRVMGR_API_getInterfaceList "getInterfaceList" -#define IARM_BUS_NETSRVMGR_API_getDefaultInterface "getDefaultInterface" -#define IARM_BUS_NETSRVMGR_API_setDefaultInterface "setDefaultInterface" -#define IARM_BUS_NETSRVMGR_API_isInterfaceEnabled "isInterfaceEnabled" -#define IARM_BUS_NETSRVMGR_API_setInterfaceEnabled "setInterfaceEnabled" -#define IARM_BUS_NETSRVMGR_API_setIPSettings "setIPSettings" -#define IARM_BUS_NETSRVMGR_API_getIPSettings "getIPSettings" -#define IARM_BUS_NETSRVMGR_API_isAvailable "isAvailable" -#define IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsync "getAvailableSSIDsAsync" /**< Retrieve array of strings representing SSIDs */ -#define IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsyncIncr "getAvailableSSIDsAsyncIncr" /**< Retrieve array of strings representing SSIDs in an incremental way */ -#define IARM_BUS_WIFI_MGR_API_stopProgressiveWifiScanning "stopProgressiveWifiScanning" /**< Stop any in-progress wifi progressive scanning thread */ -#define IARM_BUS_WIFI_MGR_API_initiateWPSPairing2 "initiateWPSPairing2" /**< Initiate connection via WPS via either Push Button or PIN */ -#define IARM_BUS_WIFI_MGR_API_cancelWPSPairing "cancelWPSPairing" /**< Cancel in-progress WPS */ -#define IARM_BUS_WIFI_MGR_API_getConnectedSSID "getConnectedSSID" /**< Return properties of the currently connected SSID */ -#define IARM_BUS_WIFI_MGR_API_saveSSID "saveSSID" /**< Save SSID and passphrase */ -#define IARM_BUS_WIFI_MGR_API_clearSSID "clearSSID" /**< Clear given SSID */ -#define IARM_BUS_WIFI_MGR_API_connect "connect" /**< Connect with given or saved SSID and passphrase */ -#define IARM_BUS_WIFI_MGR_API_disconnectSSID "disconnectSSID" /**< Disconnect from current SSID */ -#define IARM_BUS_WIFI_MGR_API_getCurrentState "getCurrentState" /**< Retrieve current state */ - - namespace WPEFramework { namespace Plugin { - const float signalStrengthThresholdExcellent = -50.0f; - const float signalStrengthThresholdGood = -60.0f; - const float signalStrengthThresholdFair = -67.0f; NetworkManagerImplementation* _instance = nullptr; Exchange::INetworkManager::WiFiState to_wifi_state(WiFiStatusCode_t code) { @@ -399,14 +71,90 @@ namespace WPEFramework return Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS; case WIFI_AUTH_FAILED: return Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED; - case WIFI_NO_SSID: - return Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND; + case WIFI_NO_SSID: + return Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND; case WIFI_UNKNOWN: return Exchange::INetworkManager::WIFI_STATE_ERROR; } return Exchange::INetworkManager::WIFI_STATE_INVALID; } + static inline uint32_t mapToLegacySecurityMode(const uint32_t securityMode) + { + if (securityMode == 0) + return 0; /* NET_WIFI_SECURITY_NONE */ + else if (securityMode == 1) + return 6; /* NET_WIFI_SECURITY_WPA2_PSK_AES */ + else if (securityMode == 2) + return 14; /* NET_WIFI_SECURITY_WPA3_SAE */ + else if (securityMode == 3) + return 12; /* NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE */ + + return 0; /* NET_WIFI_SECURITY_NONE */ + } + + static inline uint32_t mapToNewSecurityMode(const uint32_t legacyMode) + { + if ((legacyMode == NET_WIFI_SECURITY_NONE) || + (legacyMode == NET_WIFI_SECURITY_WEP_64) || + (legacyMode == NET_WIFI_SECURITY_WEP_128)) + { + return 0; /* WIFI_SECURITY_NONE */ + } + else if ((legacyMode == NET_WIFI_SECURITY_WPA_PSK_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA_PSK_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA2_PSK_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA2_PSK_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA_WPA2_PSK) || + (legacyMode == NET_WIFI_SECURITY_WPA3_PSK_AES)) + { + return 1; /* WIFI_SECURITY_WPA_PSK */ + } + else if (legacyMode == NET_WIFI_SECURITY_WPA3_SAE) + { + return 2; /* WIFI_SECURITY_SAE */ + } + else if ((legacyMode == NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA_ENTERPRISE_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP) || + (legacyMode == NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES) || + (legacyMode == NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE)) + { + return 3; /* WIFI_SECURITY_EAP */ + } + + return 0; /* WIFI_SECURITY_NONE */ + } + + static bool alreadySentRecently(std::string &interface) + { + // Use std::chrono::steady_clock for measuring elapsed time. + // steady_clock is monotonic and not affected by system time changes (e.g., NTP synchronization), + // ensuring timing is reliable even if the system clock is adjusted. + static std::chrono::steady_clock::time_point eth_last_time = std::chrono::steady_clock::time_point{}; + static std::chrono::steady_clock::time_point wlan_last_time = std::chrono::steady_clock::time_point{}; + auto now = std::chrono::steady_clock::now(); + + if (interface == "eth0") + { + if (eth_last_time != std::chrono::steady_clock::time_point{} && + std::chrono::duration_cast(now - eth_last_time).count() < 500) { // 500 milliseconds threshold + return true; + } + eth_last_time = now; + } + else if (interface == "wlan0") + { + if (wlan_last_time != std::chrono::steady_clock::time_point{} && + std::chrono::duration_cast(now - wlan_last_time).count() < 500) { // 500 milliseconds threshold + return true; + } + wlan_last_time = now; + } + + return false; + } + void NetworkManagerInternalEventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len) { LOG_ENTRY_FUNCTION(); @@ -435,9 +183,9 @@ namespace WPEFramework if(interface == "eth0" || interface == "wlan0") { if (e->status) - ::_instance->ReportInterfaceStateChangedEvent(Exchange::INetworkManager::INTERFACE_ADDED, interface); + ::_instance->ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_ADDED, interface); else - ::_instance->ReportInterfaceStateChangedEvent(Exchange::INetworkManager::INTERFACE_REMOVED, interface); + ::_instance->ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_REMOVED, interface); } break; } @@ -448,9 +196,9 @@ namespace WPEFramework NMLOG_INFO ("IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS :: %s", interface.c_str()); if(interface == "eth0" || interface == "wlan0") { if (e->status) - ::_instance->ReportInterfaceStateChangedEvent(Exchange::INetworkManager::INTERFACE_LINK_UP, interface); + ::_instance->ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_LINK_UP, interface); else - ::_instance->ReportInterfaceStateChangedEvent(Exchange::INetworkManager::INTERFACE_LINK_DOWN, interface); + ::_instance->ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_LINK_DOWN, interface); } break; } @@ -458,10 +206,23 @@ namespace WPEFramework { IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t *e = (IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t*) data; interface = e->interface; - NMLOG_INFO ("IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS :: %s -- %s", interface.c_str(), e->ip_address); + NMLOG_INFO ("IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS: %s - %s - %s", interface.c_str(), e->ip_address, e->acquired?"Acquired":"Lost"); if(interface == "eth0" || interface == "wlan0") { - ::_instance->ReportIPAddressChangedEvent(interface, e->acquired, e->is_ipv6, string(e->ip_address)); + string ipversion("IPv4"); + Exchange::INetworkManager::IPStatus status = Exchange::INetworkManager::IP_LOST; + if(!e->is_ipv6 && alreadySentRecently(interface)) + { + NMLOG_INFO("Already sent recently, skipping IP address change event for %s", interface.c_str()); + break; + } + + if (e->is_ipv6) + ipversion = "IPv6"; + if (e->acquired) + status = Exchange::INetworkManager::IP_ACQUIRED; + + ::_instance->ReportIPAddressChange(interface, ipversion, string(e->ip_address), status); } break; } @@ -473,12 +234,12 @@ namespace WPEFramework oldInterface = e->oldInterface; newInterface = e->newInterface; NMLOG_INFO ("IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE %s :: %s..", oldInterface.c_str(), newInterface.c_str()); - if(oldInterface != "eth0" || oldInterface != "wlan0") - oldInterface == ""; /* assigning "null" if the interface is not eth0 or wlan0 */ - if(newInterface != "eth0" || newInterface != "wlan0") - newInterface == ""; /* assigning "null" if the interface is not eth0 or wlan0 */ + if(oldInterface != "eth0" && oldInterface != "wlan0") + oldInterface = ""; /* assigning "null" if the interface is not eth0 or wlan0 */ + if(newInterface != "eth0" && newInterface != "wlan0") + newInterface = ""; /* assigning "null" if the interface is not eth0 or wlan0 */ - ::_instance->ReportActiveInterfaceChangedEvent(oldInterface, newInterface); + ::_instance->ReportActiveInterfaceChange(oldInterface, newInterface); break; } case IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs: @@ -487,6 +248,8 @@ namespace WPEFramework NMLOG_INFO ("IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs"); std::string serialized(e->data.wifiSSIDList.ssid_list); JsonObject eventDocument; + JsonArray ssidsUpdated; + uint32_t security; WPEC::OptionalType error; if (!WPEJ::IElement::FromString(serialized, eventDocument, error)) { NMLOG_ERROR("Failed to parse JSON document containing SSIDs. Due to: %s", WPEJ::ErrorDisplayMessage(error).c_str()); @@ -498,10 +261,20 @@ namespace WPEFramework } JsonArray ssids = eventDocument["getAvailableSSIDs"].Array(); - string json; - ssids.ToString(json); - ::_instance->ReportAvailableSSIDsEvent(json); + for (int i = 0; i < ssids.Length(); i++) + { + JsonObject object = ssids[i].Object(); + security = object["security"].Number(); + JsonObject newObject; + newObject["ssid"] = object["ssid"]; + newObject["security"] = mapToNewSecurityMode(security); + newObject["strength"] = object["signalStrength"]; + newObject["frequency"] = object["frequency"]; + ssidsUpdated.Add(newObject); + } + ::_instance->ReportAvailableSSIDs(ssidsUpdated); + break; } case IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged: { @@ -509,7 +282,7 @@ namespace WPEFramework Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WIFI_STATE_DISCONNECTED; NMLOG_INFO("Event IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged received; state=%d", e->data.wifiStateChange.state); state = to_wifi_state(e->data.wifiStateChange.state); - ::_instance->ReportWiFiStateChangedEvent(state); + ::_instance->ReportWiFiStateChange(state, ""); break; } case IARM_BUS_WIFI_MGR_EVENT_onError: @@ -517,7 +290,7 @@ namespace WPEFramework IARM_BUS_WiFiSrvMgr_EventData_t* e = (IARM_BUS_WiFiSrvMgr_EventData_t *) data; Exchange::INetworkManager::WiFiState state = errorcode_to_wifi_state(e->data.wifiError.code); NMLOG_INFO("Event IARM_BUS_WIFI_MGR_EVENT_onError received; code=%d", e->data.wifiError.code); - ::_instance->ReportWiFiStateChangedEvent(state); + ::_instance->ReportWiFiStateChange(state, ""); break; } default: @@ -531,45 +304,149 @@ namespace WPEFramework NMLOG_WARNING("WARNING - cannot handle IARM events without a Network plugin instance!"); } - void NetworkManagerImplementation::retryIarmEventRegistration() - { - m_registrationThread = thread(&NetworkManagerImplementation::threadEventRegistration, this); - - } - void NetworkManagerImplementation::threadEventRegistration() + void NetworkManagerImplementation::threadEventRegistration(bool iarmInit, bool iarmConnect) { - IARM_Result_t res = IARM_RESULT_SUCCESS; - IARM_Result_t retVal = IARM_RESULT_SUCCESS; - do + uint32_t retry = 0; + char c; + IARM_Result_t retInit = IARM_RESULT_IPCCORE_FAIL; + IARM_Result_t retConnect = IARM_RESULT_IPCCORE_FAIL; + IARM_Result_t retIPC = IARM_RESULT_IPCCORE_FAIL; + do { - char c; - uint32_t retry = 0; - retVal = IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isAvailable, (void *)&c, sizeof(c)); - if(retVal != IARM_RESULT_SUCCESS){ - NMLOG_ERROR("threadEventRegistration: NetSrvMgr is not available. Failed to activate NetworkManager Plugin, retrying count = %d", retry); - usleep(500*1000); + if(iarmInit != true) + { + retInit= IARM_Bus_Init("netsrvmgr-thunder"); + if(retInit != IARM_RESULT_SUCCESS && retInit != IARM_RESULT_INVALID_STATE) + { + NMLOG_ERROR("IARM_Bus_Init failure, retrying. %d: %d", retry, retInit); + usleep(500 * 1000); + retry++; + continue; + } + iarmInit = true; + } + + if(iarmConnect != true) + { + retConnect = IARM_Bus_Connect(); + if(retConnect != IARM_RESULT_SUCCESS) + { + NMLOG_ERROR("IARM_Bus_Connect failure, retrying. %d: %d", retry, retConnect); + usleep(500 * 1000); + retry++; + continue; + } + iarmConnect = true; + } + + retIPC = IARM_Bus_Call_with_IPCTimeout(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isAvailable, (void *)&c, sizeof(c), (1000*10)); + if (retIPC != IARM_RESULT_SUCCESS) + { + NMLOG_ERROR("NetSrvMgr is not available. Failed to activate NetworkManager Plugin.%d: %d", retry, retIPC); + usleep(500 * 1000); retry++; + continue; } - }while(retVal != IARM_RESULT_SUCCESS); + } while(retIPC != IARM_RESULT_SUCCESS); + + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERNET_CONNECTION_CHANGED, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onError, NetworkManagerInternalEventHandler); + IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, NetworkManagerInternalEventHandler); + + NMLOG_INFO("threadEventRegistration successfully subscribed to IARM event for NetworkManager Plugin"); + /* + * Read current network state and post the event. + * Useful if NetworkManager plugin or WPEFramework is restarted + * or netsrvmgr misses to post iarm events during bootup. + */ + getInitialConnectionState(); + } - if(retVal != IARM_RESULT_SUCCESS) - { - NMLOG_ERROR("threadEventRegistration NetSrvMgr is not available. Failed to activate NetworkManager Plugin, retrying new cycle"); - } - else + void NetworkManagerImplementation::getInitialConnectionState() + { + // check the connection state and post event + Exchange::INetworkManager::IInterfaceDetailsIterator* _interfaces{}; + uint32_t rc = GetAvailableInterfaces(_interfaces); + size_t interfaceCount = 0; + Exchange::INetworkManager::InterfaceDetails iface{}; + + if (Core::ERROR_NONE == rc) { - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERNET_CONNECTION_CHANGED, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onError, NetworkManagerInternalEventHandler); - IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, NetworkManagerInternalEventHandler); + if (_interfaces != nullptr) + { + while (_interfaces->Next(iface)) + { + if(iface.enabled && iface.connected) + { + interfaceCount++; + } + } + _interfaces->Reset(0); + while (_interfaces->Next(iface) == true) + { + if((interfaceCount == 2 && "eth0" == iface.name) || interfaceCount == 1) + { + Core::JSON::EnumType type{iface.type}; + if(iface.enabled) + { + NMLOG_INFO("'%s' interface is enabled", iface.name.c_str()); + // ReportInterfaceStateChange(Exchange::INetworkManager::INTERFACE_ADDED, iface.name); + if(iface.connected) + { + NMLOG_INFO("'%s' interface is connected", iface.name.c_str()); + if(getDefaultInterface() != iface.name) + ReportActiveInterfaceChange(getDefaultInterface(), iface.name); + Exchange::INetworkManager::IPAddress addrv4; + Exchange::INetworkManager::IPAddress addrv6; + std::string ipversion = "IPv4"; + rc = GetIPSettings(iface.name, ipversion, addrv4); + if (Core::ERROR_NONE == rc) + { + if(!addrv4.ipaddress.empty()) { + NMLOG_INFO("'%s' interface have ip '%s'", iface.name.c_str(), addrv4.ipaddress.c_str()); + ReportIPAddressChange(iface.name, addrv4.ipversion, addrv4.ipaddress, Exchange::INetworkManager::IP_ACQUIRED); + } + } + ipversion = "IPv6"; + rc = GetIPSettings(iface.name, ipversion, addrv6); + if (Core::ERROR_NONE == rc) + { + if(!addrv6.ipaddress.empty()) { + NMLOG_INFO("'%s' interface have ip '%s'", iface.name.c_str(), addrv6.ipaddress.c_str()); + ReportIPAddressChange(iface.name, addrv6.ipversion, addrv6.ipaddress, Exchange::INetworkManager::IP_ACQUIRED); + } + } + } + } + } + } - NMLOG_INFO("threadEventRegistration successfully subscribed to IARM event for NetworkManager Plugin"); + _interfaces->Release(); + } } + } + void NetworkManagerImplementation::platform_logging(const NetworkManagerLogger::LogLevel& level) + { + // TODO set the netsrvmgr logLevel + return; + } + + /* @brief Set the dhcp hostname */ + uint32_t NetworkManagerImplementation::SetHostname(const string& hostname /* @in */) + { + // TODO: Implement setting the DHCP hostname for netsrvmgr + return Core::ERROR_NONE; + } + + void NetworkManagerImplementation::platform_deinit() + { + return; } void NetworkManagerImplementation::platform_init() @@ -578,36 +455,64 @@ namespace WPEFramework char c; ::_instance = this; + uint32_t retry = 0; + bool iarmInit = false; + bool iarmConnect = false; + + IARM_Result_t retInit = IARM_RESULT_IPCCORE_FAIL; + IARM_Result_t retConnect = IARM_RESULT_IPCCORE_FAIL; + IARM_Result_t retIPC = IARM_RESULT_IPCCORE_FAIL; - IARM_Result_t res = IARM_Bus_Init("netsrvmgr-thunder"); - NMLOG_INFO("IARM_Bus_Init: %d", res); - if (res == IARM_RESULT_SUCCESS || res == IARM_RESULT_INVALID_STATE /* already inited or connected */) { - res = IARM_Bus_Connect(); - NMLOG_INFO("IARM_Bus_Connect: %d", res); - } else { - NMLOG_ERROR("IARM_Bus_Init failure: %d", res); - } - - - IARM_Result_t retVal = IARM_RESULT_SUCCESS; + do + { + if(iarmInit != true) + { + retInit = IARM_Bus_Init("netsrvmgr-thunder"); + if(retInit != IARM_RESULT_SUCCESS && retInit != IARM_RESULT_INVALID_STATE) + { + NMLOG_ERROR("IARM_Bus_Init failure,retrying.%d: %d", retry, retInit); + usleep(500 * 1000); + retry++; + continue; + } + retInit = IARM_RESULT_SUCCESS; + iarmInit = true; + } + if(iarmConnect != true) + { + retConnect = IARM_Bus_Connect(); + if(retConnect != IARM_RESULT_SUCCESS) + { + NMLOG_ERROR("IARM_Bus_Connect failed,retrying. %d: %d", retry, retConnect); + usleep(500 * 1000); + retry++; + continue; + } + iarmConnect = true; + } - uint32_t retry = 0; - do{ - retVal = IARM_Bus_Call_with_IPCTimeout(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isAvailable, (void *)&c, sizeof(c), (1000*10)); - if(retVal != IARM_RESULT_SUCCESS){ - NMLOG_INFO("NetSrvMgr is not available. Failed to activate NetworkManager Plugin, retry = %d", retry); - usleep(500*1000); + retIPC = IARM_Bus_Call_with_IPCTimeout(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isAvailable, (void *)&c, sizeof(c), (1000*10)); + if (retIPC != IARM_RESULT_SUCCESS) + { + NMLOG_ERROR("NetSrvMgr is not available. Failed to activate NetworkManager Plugin. %d: %d", retry, retIPC); + usleep(500 * 1000); retry++; + continue; } - }while((retVal != IARM_RESULT_SUCCESS) && (retry < 3)); + if(retIPC == IARM_RESULT_SUCCESS && retConnect == IARM_RESULT_SUCCESS && retInit == IARM_RESULT_SUCCESS) + { + break; + } + }while(retry < 3); - if(retVal != IARM_RESULT_SUCCESS) + if(retIPC != IARM_RESULT_SUCCESS) { string msg = "NetSrvMgr is not available"; - NMLOG_INFO("NETWORK_NOT_READY: The NetSrvMgr Component is not available.Retrying in separate thread ::%s::", msg.c_str()); - retryIarmEventRegistration(); + NMLOG_ERROR("NETWORK_NOT_READY: The NetSrvMgr Component is not available.Retrying in separate thread ::%s::", msg.c_str()); + m_registrationThread = thread(&NetworkManagerImplementation::threadEventRegistration, this, iarmInit, iarmConnect); } - else { + else + { IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, NetworkManagerInternalEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, NetworkManagerInternalEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, NetworkManagerInternalEventHandler); @@ -616,6 +521,13 @@ namespace WPEFramework IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged, NetworkManagerInternalEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onError, NetworkManagerInternalEventHandler); IARM_Bus_RegisterEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, NetworkManagerInternalEventHandler); + /* + * Read current network state and post the event. + * Useful if NetworkManager plugin or WPEFramework is restarted + * or netsrvmgr misses to post iarm events during bootup. + */ + std::thread connStateThread = std::thread(&NetworkManagerImplementation::getInitialConnectionState, this); + connStateThread.join(); // seprate thread will not use the wpeframework thread pool } } @@ -623,32 +535,41 @@ namespace WPEFramework { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_BUS_NetSrvMgr_InterfaceList_t list; + IARM_BUS_NetSrvMgr_InterfaceList_t list{}; if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getInterfaceList, (void*)&list, sizeof(list))) { std::vector interfaceList; for (int i = 0; i < list.size; i++) { - NMLOG_INFO ("Interface Name = %s", list.interfaces[i].name); + NMLOG_DEBUG("Interface Name = %s", list.interfaces[i].name); string interfaceName(list.interfaces[i].name); if (("eth0" == interfaceName) || ("wlan0" == interfaceName)) { InterfaceDetails tmp; /* Update the interface as per RDK NetSrvMgr */ - if ("eth0" == interfaceName) - tmp.m_type = string("ETHERNET"); - else if ("wlan0" == interfaceName) - tmp.m_type = string("WIFI"); - - tmp.m_name = interfaceName; - tmp.m_mac = string(list.interfaces[i].mac); - tmp.m_isEnabled = ((list.interfaces[i].flags & IFF_UP) != 0); - tmp.m_isConnected = ((list.interfaces[i].flags & IFF_RUNNING) != 0); + tmp.name = interfaceName; + tmp.mac = string(list.interfaces[i].mac); + tmp.enabled = ((list.interfaces[i].flags & IFF_UP) != 0); + tmp.connected = ((list.interfaces[i].flags & IFF_RUNNING) != 0); + if ("eth0" == interfaceName) { + tmp.type = Exchange::INetworkManager::INTERFACE_TYPE_ETHERNET; + m_ethConnected.store(tmp.connected); + m_ethEnabled.store(tmp.enabled); + } + else if ("wlan0" == interfaceName) { + tmp.type = Exchange::INetworkManager::INTERFACE_TYPE_WIFI; + m_wlanConnected.store(tmp.connected); + m_wlanEnabled.store(tmp.enabled); + } + interfaceList.push_back(tmp); } } using Implementation = RPC::IteratorType; interfacesItr = Core::Service::Create(interfaceList); + if(interfacesItr == nullptr) { + return Core::ERROR_GENERAL; + } rc = Core::ERROR_NONE; } @@ -659,17 +580,19 @@ namespace WPEFramework return rc; } - +#if 0 /* @brief Get the active Interface used for external world communication */ uint32_t NetworkManagerImplementation::GetPrimaryInterface (string& interface /* @out */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; IARM_BUS_NetSrvMgr_DefaultRoute_t defaultRoute = {0}; + if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, (void*)&defaultRoute, sizeof(defaultRoute))) { NMLOG_INFO ("Call to %s for %s returned interface = %s, gateway = %s", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getDefaultInterface, defaultRoute.interface, defaultRoute.gateway); - interface = m_defaultInterface = defaultRoute.interface; + interface = defaultRoute.interface; + setDefaultInterface(defaultRoute.interface); rc = Core::ERROR_NONE; } else @@ -678,40 +601,8 @@ namespace WPEFramework } return rc; } - - /* @brief Set the active Interface used for external world communication */ - uint32_t NetworkManagerImplementation::SetPrimaryInterface (const string& interface/* @in */) - { - LOG_ENTRY_FUNCTION(); - uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_BUS_NetSrvMgr_Iface_EventData_t iarmData = { 0 }; - iarmData.persist = true; - - /* Netsrvmgr returns eth0 & wlan0 as primary interface but when we want to set., we must set ETHERNET or WIFI*/ - //TODO: Fix netsrvmgr to accept eth0 & wlan0 - if ("wlan0" == interface) - strncpy(iarmData.setInterface, "WIFI", INTERFACE_SIZE); - else if ("eth0" == interface) - strncpy(iarmData.setInterface, "ETHERNET", INTERFACE_SIZE); - else - { - rc = Core::ERROR_BAD_REQUEST; - return rc; - } - - if (IARM_RESULT_SUCCESS == IARM_Bus_Call (IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_setDefaultInterface, (void *)&iarmData, sizeof(iarmData))) - { - NMLOG_INFO ("Call to %s for %s success", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_setDefaultInterface); - rc = Core::ERROR_NONE; - } - else - { - NMLOG_ERROR ("Call to %s for %s failed", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_setDefaultInterface); - } - return rc; - } - - uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool& enable /* @in */) +#endif + uint32_t NetworkManagerImplementation::SetInterfaceState(const string& interface/* @in */, const bool enable /* @in */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; @@ -734,6 +625,10 @@ namespace WPEFramework if (IARM_RESULT_SUCCESS == IARM_Bus_Call (IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_setInterfaceEnabled, (void *)&iarmData, sizeof(iarmData))) { NMLOG_INFO ("Call to %s for %s success", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_setInterfaceEnabled); + if(interface == "wlan0" && _instance != NULL) + _instance->m_wlanEnabled.store(enable); + else if(interface == "eth0" && _instance != NULL) + _instance->m_ethEnabled.store(enable); rc = Core::ERROR_NONE; } else @@ -763,7 +658,7 @@ namespace WPEFramework if (IARM_RESULT_SUCCESS == IARM_Bus_Call (IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isInterfaceEnabled, (void *)&iarmData, sizeof(iarmData))) { - NMLOG_TRACE("Call to %s for %s success", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isInterfaceEnabled); + NMLOG_DEBUG("Call to %s for %s success", IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_isInterfaceEnabled); isEnabled = iarmData.isInterfaceEnabled; rc = Core::ERROR_NONE; } @@ -804,49 +699,126 @@ namespace WPEFramework /* @brief Get IP Address Of the Interface */ - uint32_t NetworkManagerImplementation::GetIPSettings(const string& interface /* @in */, const string& ipversion /* @in */, IPAddressInfo& result /* @out */) + uint32_t NetworkManagerImplementation::GetIPSettings(string& interface /* @inout */, const string &ipversion /* @in */, IPAddress& address /* @out */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; IARM_BUS_NetSrvMgr_Iface_Settings_t iarmData = { 0 }; + string ipversionStr; + + if(interface.empty()) + { + interface = getDefaultInterface(); + } + if(ipversion.empty()) + { + ipversionStr = "IPv4"; + } + else + { + ipversionStr = ipversion; + } + if ("wlan0" == interface) + { + if("IPv4" == ipversionStr && !m_wlanIPv4Address.ipaddress.empty()) + { + address = m_wlanIPv4Address; + return Core::ERROR_NONE; + } + else if("IPv6" == ipversionStr && !m_wlanIPv6Address.ipaddress.empty()) + { + address = m_wlanIPv6Address; + return Core::ERROR_NONE; + } + } + else if ("eth0" == interface) + { + if("IPv4" == ipversionStr && !m_ethIPv4Address.ipaddress.empty()) + { + address = m_ethIPv4Address; + return Core::ERROR_NONE; + } + else if("IPv6" == ipversionStr && !m_ethIPv6Address.ipaddress.empty()) + { + address = m_ethIPv6Address; + return Core::ERROR_NONE; + } + } /* Netsrvmgr returns eth0 & wlan0 as primary interface but when we want to set., we must set ETHERNET or WIFI*/ //TODO: Fix netsrvmgr to accept eth0 & wlan0 if ("wlan0" == interface) strncpy(iarmData.interface, "WIFI", INTERFACE_SIZE); else if ("eth0" == interface) strncpy(iarmData.interface, "ETHERNET", INTERFACE_SIZE); + else if (!interface.empty()) + { + NMLOG_ERROR("Given interface (%s) is NOT supported", interface.c_str()); + return Core::ERROR_NOT_SUPPORTED; + } + + if (("IPv4" == ipversion) || ("IPv6" == ipversion)) + sprintf(iarmData.ipversion,"%s", ipversion.c_str()); - strncpy(iarmData.ipversion, ipversion.c_str(), 16); iarmData.isSupported = true; - NMLOG_INFO("NetworkManagerImplementation::GetIPSettings - Before Calling IARM"); + if (IARM_RESULT_SUCCESS == IARM_Bus_Call (IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETSRVMGR_API_getIPSettings, (void *)&iarmData, sizeof(iarmData))) { - NMLOG_INFO("NetworkManagerImplementation::GetIPSettings - IARM Success.. Filling the data"); - result.m_ipAddrType = string(iarmData.ipversion); - result.m_autoConfig = iarmData.autoconfig; - result.m_dhcpServer = string(iarmData.dhcpserver,MAX_IP_ADDRESS_LEN - 1); - result.m_v6LinkLocal = ""; - result.m_ipAddress = string(iarmData.ipaddress,MAX_IP_ADDRESS_LEN - 1); - if (0 == strcasecmp("ipv4", iarmData.ipversion)) - result.m_prefix = NetmaskToPrefix(iarmData.netmask); - else if (0 == strcasecmp("ipv6", iarmData.ipversion)) - result.m_prefix = std::atoi(iarmData.netmask); - result.m_gateway = string(iarmData.gateway,MAX_IP_ADDRESS_LEN - 1); - result.m_primaryDns = string(iarmData.primarydns,MAX_IP_ADDRESS_LEN - 1); - result.m_secondaryDns = string(iarmData.secondarydns,MAX_IP_ADDRESS_LEN - 1); - NMLOG_INFO("NetworkManagerImplementation::GetIPSettings - IARM Success.. Filled the data"); - rc = Core::ERROR_NONE; + if(iarmData.errCode == NETWORK_IPADDRESS_ACQUIRED) + { + address.autoconfig = iarmData.autoconfig; + address.dhcpserver = string(iarmData.dhcpserver); + address.ula = string(""); + address.ipaddress = string(iarmData.ipaddress); + address.gateway = string(iarmData.gateway); + address.primarydns = string(iarmData.primarydns); + address.secondarydns = string(iarmData.secondarydns); + if (0 == strcasecmp("ipv4", iarmData.ipversion)) + { + address.ipversion = string ("IPv4"); + address.prefix = NetmaskToPrefix(iarmData.netmask); + if("eth0" == interface) + m_ethIPv4Address = address; + else if("wlan0" == interface) + m_wlanIPv4Address = address; + } + else if (0 == strcasecmp("ipv6", iarmData.ipversion)) + { + address.ipversion = string ("IPv6"); + address.prefix = std::atoi(iarmData.netmask); + if("eth0" == interface) + m_ethIPv6Address = address; + else if("wlan0" == interface) + m_wlanIPv6Address = address; + } + + rc = Core::ERROR_NONE; + /* Return the default interface information */ + if (interface.empty()) + { + string tmpInterface = string(iarmData.interface); + if ("ETHERNET" == tmpInterface) + interface = "eth0"; + else if ("WIFI" == tmpInterface) + interface = "wlan0"; + else + rc = Core::ERROR_BAD_REQUEST; + } + } + else + { + NMLOG_WARNING("IP Address is not ready to use yet; err = %d", iarmData.errCode); + } } else { - NMLOG_ERROR("NetworkManagerImplementation::GetIPSettings - Calling IARM Failed"); + NMLOG_ERROR("GetIPSettings - Calling IARM Failed"); } return rc; } -#define CIDR_NETMASK_IP_LEN 32 -const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { +const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN+1] = { + "0.0.0.0", "128.0.0.0", "192.0.0.0", "224.0.0.0", @@ -882,10 +854,14 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { }; /* @brief Set IP Address Of the Interface */ - uint32_t NetworkManagerImplementation::SetIPSettings(const string& interface /* @in */, const string &ipversion /* @in */, const IPAddressInfo& address /* @in */) + uint32_t NetworkManagerImplementation::SetIPSettings(const string& interface /* @in */, const IPAddress& address /* @in */) { uint32_t rc = Core::ERROR_NONE; - if (0 == strcasecmp("ipv4", ipversion.c_str())) + if (("IPv4" != address.ipversion) && ("IPv6" != address.ipversion)) + { + return Core::ERROR_BAD_REQUEST; + } + if ("IPv4" == address.ipversion) { IARM_BUS_NetSrvMgr_Iface_Settings_t iarmData = {0}; /* Netsrvmgr returns eth0 & wlan0 as primary interface but when we want to set., we must set ETHERNET or WIFI*/ @@ -899,27 +875,28 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { rc = Core::ERROR_BAD_REQUEST; return rc; } + /* IP version */ - strncpy(iarmData.ipversion, ipversion.c_str(), 16); + sprintf(iarmData.ipversion, "IPv4"); - if (!address.m_autoConfig) + if (!address.autoconfig) { //Lets validate the prefix Address - if (address.m_prefix >= 32) + if (address.prefix > CIDR_NETMASK_IP_LEN) { rc = Core::ERROR_BAD_REQUEST; } else { - string netmask = CIDR_PREFIXES[address.m_prefix]; + string netmask = CIDR_PREFIXES[address.prefix]; //Lets validate the IP Address struct in_addr tmpIPAddress, tmpGWAddress, tmpNetmask; struct in_addr bcastAddr1, bcastAddr2; - if (inet_pton(AF_INET, address.m_ipAddress.c_str(), &tmpIPAddress) == 1 && + if (inet_pton(AF_INET, address.ipaddress.c_str(), &tmpIPAddress) == 1 && inet_pton(AF_INET, netmask.c_str(), &tmpNetmask) == 1 && - inet_pton(AF_INET, address.m_gateway.c_str(), &tmpGWAddress) == 1) + inet_pton(AF_INET, address.gateway.c_str(), &tmpGWAddress) == 1) { bcastAddr1.s_addr = tmpIPAddress.s_addr | ~tmpNetmask.s_addr; bcastAddr2.s_addr = tmpGWAddress.s_addr | ~tmpNetmask.s_addr; @@ -953,17 +930,17 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { if (Core::ERROR_NONE == rc) { - strncpy(iarmData.ipaddress, address.m_ipAddress.c_str(), 16); + strncpy(iarmData.ipaddress, address.ipaddress.c_str(), 16); strncpy(iarmData.netmask, netmask.c_str(), 16); - strncpy(iarmData.gateway, address.m_gateway.c_str(), 16); - strncpy(iarmData.primarydns, address.m_primaryDns.c_str(), 16); - strncpy(iarmData.secondarydns, address.m_secondaryDns.c_str(), 16); + strncpy(iarmData.gateway, address.gateway.c_str(), 16); + strncpy(iarmData.primarydns, address.primarydns.c_str(), 16); + strncpy(iarmData.secondarydns, address.secondarydns.c_str(), 16); } } } else { - iarmData.autoconfig = address.m_autoConfig; + iarmData.autoconfig = address.autoconfig; } if (Core::ERROR_NONE == rc) { @@ -988,15 +965,39 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { return rc; } - uint32_t NetworkManagerImplementation::StartWiFiScan(const WiFiFrequency frequency /* @in */) + uint32_t NetworkManagerImplementation::StartWiFiScan(IStringIterator* const frequencies /* @in */, IStringIterator* const ssids/* @in */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_SsidList_Param_t param; + IARM_Bus_WiFiSrvMgr_SsidList_Param_t param{}; IARM_Result_t retVal = IARM_RESULT_SUCCESS; + //Cleared the Existing Store filterred SSID list + m_filterVectorsLock.Lock(); + m_filterSsidslist.clear(); + m_filterFrequencies.clear(); + if(ssids) + { + string ssidlist{}; + while (ssids->Next(ssidlist) == true) + { + m_filterSsidslist.push_back(ssidlist.c_str()); + NMLOG_DEBUG("%s added to SSID filtering", ssidlist.c_str()); + } + } + + if (frequencies) + { + string frequencyList{}; + while (frequencies->Next(frequencyList) == true) + { + m_filterFrequencies.push_back(frequencyList.c_str()); + NMLOG_DEBUG("%s added to Frequency filtering", frequencyList.c_str()); + } + } + m_filterVectorsLock.Unlock(); + memset(¶m, 0, sizeof(param)); - (void) frequency; retVal = IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsync, (void *)¶m, sizeof(IARM_Bus_WiFiSrvMgr_SsidList_Param_t)); @@ -1015,7 +1016,7 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_stopProgressiveWifiScanning, (void*) ¶m, sizeof(IARM_Bus_WiFiSrvMgr_Param_t))) @@ -1035,7 +1036,7 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; IARM_Result_t retVal = IARM_RESULT_SUCCESS; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); @@ -1050,6 +1051,9 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { NMLOG_INFO ("GetKnownSSIDs Success"); ssids = Core::Service::Create(ssidList); + if(ssids == nullptr) { + return Core::ERROR_GENERAL; + } rc = Core::ERROR_NONE; } else @@ -1063,12 +1067,12 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); - strncpy(param.data.connect.ssid, ssid.m_ssid.c_str(), SSID_SIZE - 1); - strncpy(param.data.connect.passphrase, ssid.m_passphrase.c_str(), PASSPHRASE_BUFF - 1); - param.data.connect.security_mode = (SsidSecurity) ssid.m_securityMode; + strncpy(param.data.connect.ssid, ssid.ssid.c_str(), SSID_SIZE - 1); + strncpy(param.data.connect.passphrase, ssid.passphrase.c_str(), PASSPHRASE_BUFF - 1); + param.data.connect.security_mode = (SsidSecurity) mapToLegacySecurityMode(ssid.security); IARM_Result_t retVal = IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_saveSSID, (void *)¶m, sizeof(param)); if((retVal == IARM_RESULT_SUCCESS) && param.status) @@ -1083,11 +1087,18 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { return rc; } + uint32_t NetworkManagerImplementation::ConnectToKnownSSID(const string& ssid /* @in */) + { + LOG_ENTRY_FUNCTION(); + NMLOG_INFO ("netsrvmgr does not support multiple known SSIDs; ConnectToKnownSSID is unavailable."); + return Core::ERROR_UNAVAILABLE; + } + uint32_t NetworkManagerImplementation::RemoveKnownSSID(const string& ssid /* @in */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); /* Currently RDK-NM supports only one saved SSID. So when you say clear, it jsut clears it. No need to pass input at this point in time. @@ -1113,23 +1124,27 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; IARM_Result_t retVal = IARM_RESULT_SUCCESS; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); - if(ssid.m_ssid.length() || ssid.m_passphrase.length()) - { - ssid.m_ssid.copy(param.data.connect.ssid, sizeof(param.data.connect.ssid) - 1); - ssid.m_passphrase.copy(param.data.connect.passphrase, sizeof(param.data.connect.passphrase) - 1); - param.data.connect.security_mode = (SsidSecurity)ssid.m_securityMode; - if(!ssid.m_identity.empty()) - ssid.m_identity.copy(param.data.connect.eapIdentity, sizeof(param.data.connect.eapIdentity) - 1); - if(!ssid.m_caCert.empty()) - ssid.m_caCert.copy(param.data.connect.carootcert, sizeof(param.data.connect.carootcert) - 1); - if(!ssid.m_clientCert.empty()) - ssid.m_clientCert.copy(param.data.connect.clientcert, sizeof(param.data.connect.clientcert) - 1); - if(!ssid.m_privateKey.empty()) - ssid.m_privateKey.copy(param.data.connect.privatekey, sizeof(param.data.connect.privatekey) - 1); - param.data.connect.persistSSIDInfo = ssid.m_persistSSIDInfo; + if(ssid.ssid.length() || ssid.passphrase.length()) + { + ssid.ssid.copy(param.data.connect.ssid, sizeof(param.data.connect.ssid) - 1); + ssid.passphrase.copy(param.data.connect.passphrase, sizeof(param.data.connect.passphrase) - 1); + param.data.connect.security_mode = (SsidSecurity)mapToLegacySecurityMode(ssid.security); + if(!ssid.eap_identity.empty()) + ssid.eap_identity.copy(param.data.connect.eapIdentity, sizeof(param.data.connect.eapIdentity) - 1); + if(!ssid.ca_cert.empty()) + ssid.ca_cert.copy(param.data.connect.carootcert, sizeof(param.data.connect.carootcert) - 1); + if(!ssid.client_cert.empty()) + ssid.client_cert.copy(param.data.connect.clientcert, sizeof(param.data.connect.clientcert) - 1); + if(!ssid.private_key.empty()) + ssid.private_key.copy(param.data.connect.privatekey, sizeof(param.data.connect.privatekey) - 1); + if(!ssid.private_key_passwd.empty()) + { + ssid.private_key_passwd.copy(param.data.connect.privatekeypasswd, sizeof(param.data.connect.privatekeypasswd) - 1); + } + param.data.connect.persistSSIDInfo = ssid.persist; } retVal = IARM_Bus_Call( IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_connect, (void *)¶m, sizeof(param)); @@ -1151,7 +1166,7 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { LOG_ENTRY_FUNCTION(); IARM_Result_t retVal = IARM_RESULT_SUCCESS; uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); retVal = IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_disconnectSSID, (void *)¶m, sizeof(param)); @@ -1167,12 +1182,26 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { return rc; } + uint32_t NetworkManagerImplementation::EthernetDeactivate(void) + { + /* No-op on RDK platform */ + NMLOG_INFO("EthernetDeactivate: no-op on RDK platform"); + return Core::ERROR_UNAVAILABLE; + } + + uint32_t NetworkManagerImplementation::ReacquireDHCPLease(const string& iface) + { + /* No-op on RDK platform */ + NMLOG_INFO("ReacquireDHCPLease: no-op on RDK platform (iface=%s)", iface.c_str()); + return Core::ERROR_UNAVAILABLE; + } + uint32_t NetworkManagerImplementation::GetConnectedSSID(WiFiSSIDInfo& ssidInfo /* @out */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; IARM_Result_t retVal = IARM_RESULT_SUCCESS; - IARM_Bus_WiFiSrvMgr_Param_t param; + IARM_Bus_WiFiSrvMgr_Param_t param{}; memset(¶m, 0, sizeof(param)); @@ -1183,13 +1212,26 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { { auto &connectedSsid = param.data.getConnectedSSID; - ssidInfo.m_ssid = string(connectedSsid.ssid); - ssidInfo.m_bssid = string(connectedSsid.bssid); - ssidInfo.m_securityMode = (WIFISecurityMode) connectedSsid.securityMode; - ssidInfo.m_signalStrength = to_string(connectedSsid.signalStrength); - ssidInfo.m_frequency = static_cast(connectedSsid.frequency)/1000.0; - ssidInfo.m_rate = to_string(connectedSsid.rate); - ssidInfo.m_noise = to_string(connectedSsid.noise); + ssidInfo.ssid = string(connectedSsid.ssid); + ssidInfo.bssid = string(connectedSsid.bssid); + ssidInfo.security = (WIFISecurityMode)mapToNewSecurityMode(connectedSsid.securityMode); + ssidInfo.strength = (int)connectedSsid.signalStrength; + ssidInfo.rate = (int)connectedSsid.rate; + + if(((int) connectedSsid.noise >= 0) || ((int) connectedSsid.noise < DEFAULT_NOISE)) + { + NMLOG_DEBUG ("Received Noise (%f) from wifi driver is not valid; so clamping it", connectedSsid.noise); + if (connectedSsid.noise >= 0) { + ssidInfo.noise = 0; + } + else if (connectedSsid.noise < DEFAULT_NOISE) { + ssidInfo.noise = DEFAULT_NOISE; + } + } + else + ssidInfo.noise = (int)connectedSsid.noise; + + ssidInfo.frequency = ((double)connectedSsid.frequency/1000); NMLOG_INFO ("GetConnectedSSID Success"); rc = Core::ERROR_NONE; @@ -1201,65 +1243,70 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { return rc; } - uint32_t NetworkManagerImplementation::GetWiFiSignalStrength(string& ssid /* @out */, string& signalStrength /* @out */, WiFiSignalQuality& quality /* @out */) +#if 0 + uint32_t NetworkManagerImplementation::GetWiFiSignalQuality(string& ssid /* @out */, string& strength /* @out */, WiFiSignalQuality& quality /* @out */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; WiFiSSIDInfo ssidInfo{}; - float signalStrengthOut = 0.0f; + float rssi = 0.0f; + float noise = 0.0f; + float floatStrength = 0.0f; + unsigned int strengthOut = 0; if (Core::ERROR_NONE == GetConnectedSSID(ssidInfo)) { - ssid = ssidInfo.m_ssid; - signalStrength = ssidInfo.m_signalStrength; - - if (!signalStrength.empty()) - { - signalStrengthOut = std::stof(signalStrength.c_str()); - NMLOG_INFO ("WiFiSignalStrength in dB = %s",signalStrengthOut); - } - - if (signalStrengthOut == 0) - { + ssid = ssidInfo.ssid; + if (!ssidInfo.strength.empty()) + rssi = std::stof(ssidInfo.strength.c_str()); + if (!ssidInfo.noise.empty()) + noise = std::stof(ssidInfo.noise.c_str()); + floatStrength = (rssi - noise); + if (floatStrength < 0) + floatStrength = 0.0; + + strengthOut = static_cast(floatStrength); + NMLOG_INFO ("WiFiSignalQuality in dB = %u",strengthOut); + + if (strengthOut == 0) + { quality = WIFI_SIGNAL_DISCONNECTED; - signalStrength = "0"; - } - else if (signalStrengthOut >= signalStrengthThresholdExcellent && signalStrengthOut < 0) - { - quality = WIFI_SIGNAL_EXCELLENT; - signalStrength = "100"; - } - else if (signalStrengthOut >= signalStrengthThresholdGood && signalStrengthOut < signalStrengthThresholdExcellent) - { - quality = WIFI_SIGNAL_GOOD; - signalStrength = "75"; - } - else if (signalStrengthOut >= signalStrengthThresholdFair && signalStrengthOut < signalStrengthThresholdGood) - { + strength = "0"; + } + else if (strengthOut > 0 && strengthOut < NM_WIFI_SNR_THRESHOLD_FAIR) + { + quality = WIFI_SIGNAL_WEAK; + } + else if (strengthOut > NM_WIFI_SNR_THRESHOLD_FAIR && strengthOut < NM_WIFI_SNR_THRESHOLD_GOOD) + { quality = WIFI_SIGNAL_FAIR; - signalStrength = "50"; - } + } + else if (strengthOut > NM_WIFI_SNR_THRESHOLD_GOOD && strengthOut < NM_WIFI_SNR_THRESHOLD_EXCELLENT) + { + quality = WIFI_SIGNAL_GOOD; + } else - { - quality = WIFI_SIGNAL_WEAK; - signalStrength = "25"; - } + { + quality = WIFI_SIGNAL_EXCELLENT; + } - NMLOG_INFO ("GetWiFiSignalStrength success"); + strength = std::to_string(strengthOut); + NMLOG_INFO ("GetWiFiSignalQuality success"); rc = Core::ERROR_NONE; } else { - NMLOG_ERROR ("GetWiFiSignalStrength failed"); + NMLOG_ERROR ("GetWiFiSignalQuality failed"); } return rc; } +#endif uint32_t NetworkManagerImplementation::StartWPS(const WiFiWPS& method /* @in */, const string& wps_pin /* @in */) { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Bus_WiFiSrvMgr_WPS_Parameters_t wps_parameters; + IARM_Bus_WiFiSrvMgr_WPS_Parameters_t wps_parameters{}; if (method == WIFI_WPS_PBC) { wps_parameters.pbc = true; @@ -1274,15 +1321,22 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { snprintf(wps_parameters.pin, sizeof(wps_parameters.pin), "xxxxxxxx"); wps_parameters.pbc = false; } + wps_parameters.status = false; if (IARM_RESULT_SUCCESS == IARM_Bus_Call(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_API_initiateWPSPairing2, (void *)&wps_parameters, sizeof(wps_parameters))) { - NMLOG_INFO ("StartWPS is success"); - rc = Core::ERROR_NONE; + if (wps_parameters.status) + { + NMLOG_INFO ("StartWPS is success"); + rc = Core::ERROR_NONE; + } + else + NMLOG_ERROR ("StartWPS: Failed"); } - else + + if (Core::ERROR_NONE != rc) { - NMLOG_ERROR ("StartWPS: Failed"); + NMLOG_ERROR ("StartWPS: Failed"); } return rc; @@ -1301,7 +1355,7 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { } else { - NMLOG_ERROR ("StopWPS: Failed"); + NMLOG_ERROR ("StopWPS: Failed"); } return rc; @@ -1311,7 +1365,6 @@ const string CIDR_PREFIXES[CIDR_NETMASK_IP_LEN] = { { LOG_ENTRY_FUNCTION(); uint32_t rc = Core::ERROR_RPC_CALL_FAILED; - IARM_Result_t retVal = IARM_RESULT_SUCCESS; IARM_Bus_WiFiSrvMgr_Param_t param; memset(¶m, 0, sizeof(param)); diff --git a/plugin/rdk/NetworkManagerRDKProxy.h b/plugin/rdk/NetworkManagerRDKProxy.h new file mode 100644 index 000000000..4a678d0b3 --- /dev/null +++ b/plugin/rdk/NetworkManagerRDKProxy.h @@ -0,0 +1,365 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#define IARM_BUS_NM_SRV_MGR_NAME "NET_SRV_MGR" + +#define MAX_IP_ADDRESS_LEN 46 +#define NETSRVMGR_INTERFACES_MAX 16 +#define MAX_ENDPOINTS 5 +#define MAX_ENDPOINT_SIZE 512 +#define MAX_URI_LEN 512 +#define MAX_HOST_NAME_LEN 128 +#define DEFAULT_PING_PACKETS 15 +#define CIDR_NETMASK_IP_LEN 32 +#define INTERFACE_SIZE 10 +#define INTERFACE_LIST 50 +#define MAX_IP_FAMILY_SIZE 10 + + +#define BUFF_LENGTH_64 65 +#define BUFF_LENGTH_256 257 +#define BUFF_MAX 1025 +#define BUFF_MAC 18 +#define BUFF_MIN 17 +#define BUFF_LENGTH_4 4 +#define SSID_SIZE 33 +#define BSSID_BUFF 20 +#define BUFF_LENGTH_24 24 +#define PASSPHRASE_BUFF 385 +#define MAX_SSIDLIST_BUF (48*1024) +#define MAX_FILE_PATH_LEN 4096 + +typedef enum _NetworkManager_EventId_t { + IARM_BUS_NETWORK_MANAGER_EVENT_SET_INTERFACE_ENABLED=50, + IARM_BUS_NETWORK_MANAGER_EVENT_SET_INTERFACE_CONTROL_PERSISTENCE, + IARM_BUS_NETWORK_MANAGER_EVENT_WIFI_INTERFACE_STATE, + IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, + IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, + IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, + IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE, + IARM_BUS_NETWORK_MANAGER_EVENT_INTERNET_CONNECTION_CHANGED, + IARM_BUS_NETWORK_MANAGER_MAX, +} IARM_Bus_NetworkManager_EventId_t; + +typedef enum _IARM_Bus_NMgr_WiFi_EventId_t { + IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged = 1, + IARM_BUS_WIFI_MGR_EVENT_onError, + IARM_BUS_WIFI_MGR_EVENT_onSSIDsChanged, + IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, + IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDsIncr, + IARM_BUS_WIFI_MGR_EVENT_onWIFILNFStateChanged, + IARM_BUS_WIFI_MGR_EVENT_MAX, /*!< Maximum event id*/ +} IARM_Bus_NMgr_WiFi_EventId_t; + + +typedef struct { + char name[16]; + char mac[20]; + unsigned int flags; +} NetSrvMgr_Interface_t; + +typedef struct { + unsigned char size; + NetSrvMgr_Interface_t interfaces[NETSRVMGR_INTERFACES_MAX]; +} IARM_BUS_NetSrvMgr_InterfaceList_t; + +typedef enum _NetworkManager_GetIPSettings_ErrorCode_t +{ + NETWORK_IPADDRESS_ACQUIRED, + NETWORK_IPADDRESS_NOTFOUND, + NETWORK_NO_ROUTE_INTERFACE, + NETWORK_NO_DEFAULT_ROUTE, + NETWORK_DNS_NOT_CONFIGURED, + NETWORK_INVALID_IPADDRESS, +} NetworkManager_GetIPSettings_ErrorCode_t; + +typedef struct { + char interface[16]; + char ipversion[16]; + bool autoconfig; + char ipaddress[MAX_IP_ADDRESS_LEN]; + char netmask[MAX_IP_ADDRESS_LEN]; + char gateway[MAX_IP_ADDRESS_LEN]; + char dhcpserver[MAX_IP_ADDRESS_LEN]; + char primarydns[MAX_IP_ADDRESS_LEN]; + char secondarydns[MAX_IP_ADDRESS_LEN]; + bool isSupported; + NetworkManager_GetIPSettings_ErrorCode_t errCode; +} IARM_BUS_NetSrvMgr_Iface_Settings_t; + +typedef struct { + char interface[16]; + char gateway[MAX_IP_ADDRESS_LEN]; +} IARM_BUS_NetSrvMgr_DefaultRoute_t; + +typedef struct { + char interface[16]; + bool status; +} IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t; + +typedef IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t IARM_BUS_NetSrvMgr_Iface_EventInterfaceEnabledStatus_t; +typedef IARM_BUS_NetSrvMgr_Iface_EventInterfaceStatus_t IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t; + +typedef struct { + char interface[16]; + char ip_address[MAX_IP_ADDRESS_LEN]; + bool is_ipv6; + bool acquired; +} IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t; + +typedef struct { + char oldInterface[16]; + char newInterface[16]; +} IARM_BUS_NetSrvMgr_Iface_EventDefaultInterface_t; + +typedef struct +{ + char server[MAX_HOST_NAME_LEN]; + uint16_t port; + bool ipv6; + char interface[16]; + uint16_t bind_timeout; + uint16_t cache_timeout; + bool sync; + char public_ip[MAX_IP_ADDRESS_LEN]; +} IARM_BUS_NetSrvMgr_Iface_StunRequest_t; + +typedef struct +{ + bool disableConnectivityTest; +} IARM_BUS_NetSrvMgr_configurePNI_t; + + + +/*! Supported values are NONE - 0, WPA - 1, WEP - 2*/ +typedef enum _SsidSecurity +{ + NET_WIFI_SECURITY_NONE = 0, + NET_WIFI_SECURITY_WEP_64, + NET_WIFI_SECURITY_WEP_128, + NET_WIFI_SECURITY_WPA_PSK_TKIP, + NET_WIFI_SECURITY_WPA_PSK_AES, + NET_WIFI_SECURITY_WPA2_PSK_TKIP, + NET_WIFI_SECURITY_WPA2_PSK_AES, + NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP, + NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES, + NET_WIFI_SECURITY_WPA_WPA2_PSK, + NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE, + NET_WIFI_SECURITY_WPA3_PSK_AES, + NET_WIFI_SECURITY_WPA3_SAE, + NET_WIFI_SECURITY_NOT_SUPPORTED = 99, +} SsidSecurity; + + +/*! Event states associated with WiFi connection */ +typedef enum _WiFiStatusCode_t { + WIFI_UNINSTALLED, /**< The device was in an installed state, and was uninstalled */ + WIFI_DISABLED, /**< The device is installed (or was just installed) and has not yet been enabled */ + WIFI_DISCONNECTED, /**< The device is not connected to a network */ + WIFI_PAIRING, /**< The device is not connected to a network, but not yet connecting to a network */ + WIFI_CONNECTING, /**< The device is attempting to connect to a network */ + WIFI_CONNECTED, /**< The device is successfully connected to a network */ + WIFI_FAILED /**< The device has encountered an unrecoverable error with the wifi adapter */ +} WiFiStatusCode_t; + +/*! Error code: A recoverable, unexpected error occurred, + * as defined by one of the following values */ +typedef enum _WiFiErrorCode_t { + WIFI_SSID_CHANGED, /**< The SSID of the network changed */ + WIFI_CONNECTION_LOST, /**< The connection to the network was lost */ + WIFI_CONNECTION_FAILED, /**< The connection failed for an unknown reason */ + WIFI_CONNECTION_INTERRUPTED, /**< The connection was interrupted */ + WIFI_INVALID_CREDENTIALS, /**< The connection failed due to invalid credentials */ + WIFI_NO_SSID, /**< The SSID does not exist */ + WIFI_UNKNOWN, /**< Any other error */ + WIFI_AUTH_FAILED /**< The connection failed due to auth failure */ +} WiFiErrorCode_t; + +typedef enum _WiFiLNFState_t { + LNF_SSID_CONNECTED, /**< Connected to the LNF network */ + LNF_SSID_CONNECT_FAILED, /**< failed connect to the LNF network */ + LNF_ENDPOINT_BACKOFF_TIME, /**< lnf in last requested backoff time */ + LNF_ENDPOINT_CONNECTED, /**< lnf connected to end point and get the LFAT */ + LNF_RECEIVED_PRIVATE_SSID_INFO, /**< lnf process Got private cridential*/ + LNF_SWITCHING_TO_PRIVATE, /**< lnf connected to private*/ + LNF_SSID_DISCONNECTED, /**< disconnected form lnf ssid */ + LNF_ERROR /**< common lnf error replay from lnf library */ +} WiFiLNFState_t; + +typedef struct _IARM_Bus_WiFiSrvMgr_WPS_Parameters_t +{ + bool pbc; + char pin[9]; + bool status; +} IARM_Bus_WiFiSrvMgr_WPS_Parameters_t; + + +/*! LNF states */ +typedef enum _WiFiLNFStatusCode_t { + LNF_UNITIALIZED, /**< Network manager hasn't started the LNF process */ + LNF_IN_PROGRESS, /**< Network manager has started LNF, and waiting for operation to complete */ + CONNECTED_LNF, /**< Connected to the LNF network */ + CONNECTED_PRIVATE, /** Connected to a network that is not LNF */ + DISCONNECTED_NO_LNF_GATEWAY_DETECTED, /**< unable to connect to LNF network */ + DISCONNECTED_GET_LFAT_FAILED, /**< client wasn't able to acquire an LFAT */ + DISCONNECTED_CANT_CONNECT_TO_PRIVATE // client could obtain LFAT, but couldn't connect to private network */ + /**< The device has encountered an unrecoverable error with the wifi adapter */ +} WiFiLNFStatusCode_t; + + +typedef struct _setWiFiAdapter +{ + bool enable; +} setWiFiAdapter; + +typedef struct _WiFiConnection +{ + char ssid[SSID_SIZE]; + char bssid[BSSID_BUFF]; + char security[BUFF_LENGTH_64]; + char passphrase[PASSPHRASE_BUFF]; + SsidSecurity security_mode; + char security_WEPKey[PASSPHRASE_BUFF]; + char security_PSK[PASSPHRASE_BUFF]; + char eapIdentity[BUFF_LENGTH_256]; + char carootcert[MAX_FILE_PATH_LEN]; + char clientcert[MAX_FILE_PATH_LEN]; + char privatekey[MAX_FILE_PATH_LEN]; + char privatekeypasswd[PASSPHRASE_BUFF]; + bool persistSSIDInfo; +} WiFiConnection; + +typedef struct _WiFiConnectedSSIDInfo +{ + char ssid[SSID_SIZE]; /**< The name of connected SSID. */ + char bssid[BSSID_BUFF]; /**< The the Basic Service Set ID (mac address). */ + char band[BUFF_MIN]; /**< The frequency band at which the client is conneted to. */ + int securityMode; /**< Current WiFi Security Mode used for connection. */ + int frequency; /**< The Frequency wt which the client is connected to. */ + float rate; /**< The Physical data rate in Mbps */ + float noise; /**< The average noise strength in dBm. */ + float signalStrength; /**< The RSSI value in dBm. */ + +} WiFiConnectedSSIDInfo_t; + +typedef struct _WiFiPairedSSIDInfo +{ + char ssid[SSID_SIZE]; /**< The name of connected SSID. */ + char bssid[BSSID_BUFF]; /**< The the Basic Service Set ID (mac address). */ + char security[BUFF_LENGTH_64]; /**< security of AP */ + SsidSecurity secMode; +} WiFiPairedSSIDInfo_t; + + +typedef enum _WiFiConnectionTypeCode_t { + WIFI_CON_UNKNOWN, + WIFI_CON_WPS, + WIFI_CON_MANUAL, + WIFI_CON_LNF, + WIFI_CON_PRIVATE, + WIFI_CON_MAX, +} WiFiConnectionTypeCode_t; + + +typedef struct _IARM_Bus_WiFiSrvMgr_Param_t { + union { + WiFiLNFStatusCode_t wifiLNFStatus; + WiFiStatusCode_t wifiStatus; + setWiFiAdapter setwifiadapter; + WiFiConnection connect; + WiFiConnection saveSSID; + WiFiConnection clearSSID; + WiFiConnectedSSIDInfo_t getConnectedSSID; + WiFiPairedSSIDInfo_t getPairedSSIDInfo; + WiFiConnectionTypeCode_t connectionType; + struct getPairedSSID { + char ssid[SSID_SIZE]; + } getPairedSSID; + bool isPaired; + } data; + bool status; +} IARM_Bus_WiFiSrvMgr_Param_t; + +typedef struct _IARM_BUS_NetSrvMgr_Iface_EventData_t { + union { + char activeIface[INTERFACE_SIZE]; + char allNetworkInterfaces[INTERFACE_LIST]; + char setInterface[INTERFACE_SIZE]; + char activeIfaceIpaddr[MAX_IP_ADDRESS_LEN]; + }; + char interfaceCount; + bool isInterfaceEnabled; + bool persist; + char ipfamily[MAX_IP_FAMILY_SIZE]; +} IARM_BUS_NetSrvMgr_Iface_EventData_t; + +typedef struct _IARM_BUS_WiFiSrvMgr_EventData_t { + union { + struct _WIFI_STATECHANGE_DATA { + WiFiStatusCode_t state; + } wifiStateChange; + struct _WIFI_ERROR { + WiFiErrorCode_t code; + } wifiError; + struct _WIFI_SSID_LIST { + char ssid_list[MAX_SSIDLIST_BUF]; + bool more_data; + } wifiSSIDList; + struct _WIFI_LNF_STATE { + WiFiLNFState_t state; + float backOffTime; + }wifilnfState; + } data; +} IARM_BUS_WiFiSrvMgr_EventData_t; + +typedef struct _wifiSsidData_t { + char jdata[MAX_SSIDLIST_BUF]; /**< Buffer containing the serialized data. */ + size_t jdataLen; /**< Length of the data buffer. */ +} wifiSsidData_t; + +typedef struct _IARM_Bus_WiFiSrvMgr_SsidList_Param_t { + wifiSsidData_t curSsids; + bool status; +} IARM_Bus_WiFiSrvMgr_SsidList_Param_t; + + +#define IARM_BUS_NETSRVMGR_API_getInterfaceList "getInterfaceList" +#define IARM_BUS_NETSRVMGR_API_getDefaultInterface "getDefaultInterface" +#define IARM_BUS_NETSRVMGR_API_setDefaultInterface "setDefaultInterface" +#define IARM_BUS_NETSRVMGR_API_isInterfaceEnabled "isInterfaceEnabled" +#define IARM_BUS_NETSRVMGR_API_setInterfaceEnabled "setInterfaceEnabled" +#define IARM_BUS_NETSRVMGR_API_setIPSettings "setIPSettings" +#define IARM_BUS_NETSRVMGR_API_getIPSettings "getIPSettings" +#define IARM_BUS_NETSRVMGR_API_isAvailable "isAvailable" +#define IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsync "getAvailableSSIDsAsync" /**< Retrieve array of strings representing SSIDs */ +#define IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsyncIncr "getAvailableSSIDsAsyncIncr" /**< Retrieve array of strings representing SSIDs in an incremental way */ +#define IARM_BUS_WIFI_MGR_API_stopProgressiveWifiScanning "stopProgressiveWifiScanning" /**< Stop any in-progress wifi progressive scanning thread */ +#define IARM_BUS_WIFI_MGR_API_initiateWPSPairing2 "initiateWPSPairing2" /**< Initiate connection via WPS via either Push Button or PIN */ +#define IARM_BUS_WIFI_MGR_API_cancelWPSPairing "cancelWPSPairing" /**< Cancel in-progress WPS */ +#define IARM_BUS_WIFI_MGR_API_getConnectedSSID "getConnectedSSID" /**< Return properties of the currently connected SSID */ +#define IARM_BUS_WIFI_MGR_API_saveSSID "saveSSID" /**< Save SSID and passphrase */ +#define IARM_BUS_WIFI_MGR_API_clearSSID "clearSSID" /**< Clear given SSID */ +#define IARM_BUS_WIFI_MGR_API_connect "connect" /**< Connect with given or saved SSID and passphrase */ +#define IARM_BUS_WIFI_MGR_API_disconnectSSID "disconnectSSID" /**< Disconnect from current SSID */ +#define IARM_BUS_WIFI_MGR_API_getCurrentState "getCurrentState" /**< Retrieve current state */ + diff --git a/tests/l1Test/CMakeLists.txt b/tests/l1Test/CMakeLists.txt new file mode 100644 index 000000000..ec7b8c37e --- /dev/null +++ b/tests/l1Test/CMakeLists.txt @@ -0,0 +1,91 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message ("building l1 test") + +find_package(CURL) +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin/rdk) +include_directories(${PROJECT_SOURCE_DIR}/plugin) + +if(ENABLE_ROUTER_DISCOVERY_TOOL) + set(NM_ROUTER_DISCOVERY_L1_TEST "upnpcli_l1_test") + + add_executable(${NM_ROUTER_DISCOVERY_L1_TEST} + ${CMAKE_SOURCE_DIR}/tests/l1Test/l1_test_routediscovery.cpp + ${CMAKE_SOURCE_DIR}/tools/upnp/UpnpDiscoveryManager.cpp + ) + + target_link_libraries(${NM_ROUTER_DISCOVERY_L1_TEST} PRIVATE + gmock_main + ${GLIB_LIBRARIES} + ${LIBSOUP_LIBRARIES} + ${GUPNP_LIBRARIES} + ${GSSDP_LIBRARIES} + ) + + set_target_properties(${NM_ROUTER_DISCOVERY_L1_TEST} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + ) + + target_include_directories(${NM_ROUTER_DISCOVERY_L1_TEST} PRIVATE + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include + ${PROJECT_SOURCE_DIR}/tools/upnp + ${GUPNP_INCLUDE_DIRS} + ${GLIB_INCLUDE_DIRS} + ${LIBSOUP_INCLUDE_DIRS} + ${GSSDP_INCLUDE_DIRS} + ) + + install(TARGETS ${NM_ROUTER_DISCOVERY_L1_TEST} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +endif() + +set(NM_CLASS_L1_TEST "class_l1_test") + +add_executable(${NM_CLASS_L1_TEST} + ${CMAKE_SOURCE_DIR}/tests/l1Test/l1_test_stunclient.cpp + ${CMAKE_SOURCE_DIR}/tests/l1Test/l1_test_connectivity.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerConnectivity.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerStunClient.cpp +) + +target_link_libraries(${NM_CLASS_L1_TEST} PRIVATE + gmock_main + ${CURL_LIBRARIES} + ${NAMESPACE}Core::${NAMESPACE}Core +) + +set_target_properties(${NM_CLASS_L1_TEST} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES +) + +target_include_directories(${NM_CLASS_L1_TEST} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include +) + +install(TARGETS ${NM_CLASS_L1_TEST} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/tests/l1Test/l1_test_connectivity.cpp b/tests/l1Test/l1_test_connectivity.cpp new file mode 100644 index 000000000..5ce589559 --- /dev/null +++ b/tests/l1Test/l1_test_connectivity.cpp @@ -0,0 +1,67 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include "NetworkManagerImplementation.h" +#include "NetworkManagerConnectivity.h" +#include +#include + +using namespace std; +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +namespace WPEFramework +{ + namespace Plugin + { + NetworkManagerImplementation* _instance = nullptr; + void NetworkManagerImplementation::ReportInternetStatusChange(const InternetStatus prevState, const InternetStatus currState, const string interface, const string& reason) + { + return; + } + } +} + +class ConnectivityMonitorTest : public ::testing::Test { +protected: + WPEFramework::Plugin::ConnectivityMonitor cm; +}; + +TEST_F(ConnectivityMonitorTest, StartConnectivityMonitor_Success) { + bool result = cm.startConnectivityMonitor(); + EXPECT_TRUE(result); + result = cm.startConnectivityMonitor(); + EXPECT_TRUE(result); +} + +TEST_F(ConnectivityMonitorTest, SetEndpoints_Valid) { + std::vector endpoints = {"https://github.com/rdkcentral", "https://github.com/rdkcentral/rdkservices"}; + cm.setConnectivityMonitorEndpoints(endpoints); + EXPECT_EQ(cm.getConnectivityMonitorEndpoints(), endpoints); +} + +TEST_F(ConnectivityMonitorTest, SetEndpoints_InvalidShortEndpoints) { + std::vector endpoints = {"ab", "htt", "xyz"}; + cm.setConnectivityMonitorEndpoints(endpoints); + EXPECT_TRUE(cm.getConnectivityMonitorEndpoints().empty()); +} + +TEST_F(ConnectivityMonitorTest, SetEndpoints_DuplicateEndpoints) { + std::vector endpoints = {"https://github.com", "https://github.com"}; + cm.setConnectivityMonitorEndpoints(endpoints); + EXPECT_EQ((int)cm.getConnectivityMonitorEndpoints().size(), 2); +} diff --git a/tests/l1Test/l1_test_routediscovery.cpp b/tests/l1Test/l1_test_routediscovery.cpp new file mode 100644 index 000000000..49b817165 --- /dev/null +++ b/tests/l1Test/l1_test_routediscovery.cpp @@ -0,0 +1,100 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include + +using ::testing::_; +using ::testing::Return; +using ::testing::Mock; + +class UpnpDiscoveryManagerTest : public ::testing::Test { +protected: + UpnpDiscoveryManager upnpDiscover; + GMainLoop* mainLoop; + void SetUp() override { + mainLoop = g_main_loop_new(nullptr, FALSE); + } +}; + +std::string getInterfaceWithDefaultRoute() { + std::string command = "ip -4 route show | grep default | awk '{print $5}'"; + std::array buffer; + std::string result; + + std::unique_ptr pipe(popen(command.c_str(), "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + + // Remove trailing newline character + if (!result.empty() && result.back() == '\n') { + result.pop_back(); + } + return result; +} + +TEST_F(UpnpDiscoveryManagerTest, FindGatewayDeviceTest) +{ + // Invalid interface name + EXPECT_EQ(false, upnpDiscover.findGatewayDevice("123")); + + // Get router details on actual interface + std::string interfaceName = getInterfaceWithDefaultRoute(); + if (!interfaceName.empty()) { + std::cout << "Interface with default route: " << interfaceName << std::endl; + } else { + std::cout << "No default route interface found." << std::endl; + } + + // Valid interface name + EXPECT_EQ(true, upnpDiscover.findGatewayDevice(interfaceName)); +} + +TEST_F(UpnpDiscoveryManagerTest, routerDiscovery) +{ + auto threadHandler = [&]() { + std::this_thread::sleep_for(std::chrono::seconds(10)); + std::cout << "Thread finished sleeping." << std::endl; + if (this->mainLoop) + { + g_main_loop_quit(this->mainLoop); + g_main_loop_unref(this->mainLoop); + } + }; + // Get router details on actual interface + std::string interfaceName = getInterfaceWithDefaultRoute(); + if (!interfaceName.empty()) { + std::cout << "Interface with default route: " << interfaceName << std::endl; + } else { + std::cout << "No default route interface found." << std::endl; + } + mainLoop = g_main_loop_new(NULL, FALSE); + std::thread workerThread(threadHandler); + // Get the router details, mostly will timeout in corporate network + EXPECT_EQ(true, upnpDiscover.findGatewayDevice(interfaceName)); + g_main_loop_run(mainLoop); + workerThread.join(); +} diff --git a/tests/l1Test/l1_test_stunclient.cpp b/tests/l1Test/l1_test_stunclient.cpp new file mode 100644 index 000000000..82de8406b --- /dev/null +++ b/tests/l1Test/l1_test_stunclient.cpp @@ -0,0 +1,55 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include "NetworkManagerStunClient.h" + +using namespace std; +using namespace stun; + +class AddressTest : public ::testing::Test { +protected: + stun::attributes::address ad; +}; + +class ClientTest : public ::testing::Test { +protected: + stun::client _client; +}; + +TEST_F(ClientTest, BindSuccess) { + stun::bind_result result; + bool success = _client.bind("https://github.com/", 3478, "eth0", stun::protocol::af_inet, 5000, 10000, result); + EXPECT_FALSE(success); + EXPECT_FALSE(result.is_valid()); +} + +TEST_F(ClientTest, BindFailure) { + stun::bind_result result; + bool success = _client.bind("http//tata.com", 3478, "eth0", stun::protocol::af_inet, 5000, 10000, result); + EXPECT_FALSE(success); + EXPECT_FALSE(result.is_valid()); +} + +TEST_F(ClientTest, BindWithInvalidInterface) { + stun::bind_result result; + bool success = _client.bind("https://github.com/", 3478, "invalid_interface", stun::protocol::af_inet, 5000, 10000, result); + EXPECT_FALSE(success); + EXPECT_FALSE(result.is_valid()); +} diff --git a/tests/l2Test/CMakeLists.txt b/tests/l2Test/CMakeLists.txt new file mode 100644 index 000000000..74c150bf7 --- /dev/null +++ b/tests/l2Test/CMakeLists.txt @@ -0,0 +1,43 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2023 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +cmake_minimum_required(VERSION 3.3) +message ("building Networkmanager plugin l2 tests") + +find_package(CURL) +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip +) + +FetchContent_MakeAvailable(googletest) + +if(ENABLE_LEGACY_PLUGINS) + add_subdirectory(legacy) +endif() + +if(ENABLE_GNOME_NETWORKMANAGER) + add_subdirectory(libnm) +else() + add_subdirectory(rdk) +endif() diff --git a/tests/l2Test/legacy/CMakeLists.txt b/tests/l2Test/legacy/CMakeLists.txt new file mode 100644 index 000000000..ae1688612 --- /dev/null +++ b/tests/l2Test/legacy/CMakeLists.txt @@ -0,0 +1,89 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message ("building l2 legacy plugin test") + +set(NM_LEGACY_WIFI_UT "legacywifi_tests") +set(NM_LEGACY_NETWORK_UT "legacynetwork_tests") + +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) + +add_executable(${NM_LEGACY_WIFI_UT} + ${CMAKE_SOURCE_DIR}/tests/l2Test/legacy/l2_test_LegacyPlugin_WiFiManagerAPIs.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/thunder/Module.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/legacy/LegacyWiFiManagerAPIs.cpp +) + +add_executable(${NM_LEGACY_NETWORK_UT} + ${CMAKE_SOURCE_DIR}/tests/l2Test/legacy/l2_test_LegacyPlugin_NetworkAPIs.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/thunder/Module.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/legacy/LegacyNetworkAPIs.cpp +) + +set_target_properties(${NM_LEGACY_WIFI_UT} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES +) +set_target_properties(${NM_LEGACY_NETWORK_UT} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES +) + +target_compile_options(${NM_LEGACY_WIFI_UT} PRIVATE -Wall -include ${CMAKE_SOURCE_DIR}/interface/INetworkManager.h) +target_compile_options(${NM_LEGACY_NETWORK_UT} PRIVATE -Wall -include ${CMAKE_SOURCE_DIR}/interface/INetworkManager.h) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin) +include_directories(${PROJECT_SOURCE_DIR}/legacy) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks/thunder) + +target_include_directories(${NM_LEGACY_WIFI_UT} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include +) + +target_include_directories(${NM_LEGACY_NETWORK_UT} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + +target_link_libraries(${NM_LEGACY_WIFI_UT} PRIVATE + gmock_main + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Core::${NAMESPACE}Core +) + +target_link_libraries(${NM_LEGACY_NETWORK_UT} PRIVATE + gmock_main + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Core::${NAMESPACE}Core +) + +install(TARGETS ${NM_LEGACY_WIFI_UT} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(TARGETS ${NM_LEGACY_NETWORK_UT} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/tests/l2Test/legacy/l2_test_LegacyPlugin_NetworkAPIs.cpp b/tests/l2Test/legacy/l2_test_LegacyPlugin_NetworkAPIs.cpp new file mode 100644 index 000000000..1eee1dcdc --- /dev/null +++ b/tests/l2Test/legacy/l2_test_LegacyPlugin_NetworkAPIs.cpp @@ -0,0 +1,941 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include +#include "ServiceMock.h" +#include "FactoriesImplementation.h" +#include "NetworkManagerLogger.h" +#include "LegacyNetworkAPIs.h" +#include "ThunderPortability.h" +#include "INetworkManager.h" +#include "INetworkManagerMock.h" +#include "LegacyNetworkMock.h" +#include "IStringIteratorMock.h" +#include "mockauthservices.h" +#include "InterfaceIteratorMock.h" + +using namespace std; +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using IStringIterator = RPC::IIteratorType; +using ::testing::NiceMock; + +class NetworkTest : public ::testing::Test{ +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + Core::JSONRPC::Handler& handlerV2; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + ServiceMock *m_service; + bool m_subsIfaceStateChange; + bool m_subsActIfaceChange; + bool m_subsIPAddrChange; + bool m_subsInternetChange; + + NetworkTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , handlerV2(*(plugin->GetHandler(2))) + , INIT_CONX(1, 0) + , m_subsIfaceStateChange(true) + , m_subsActIfaceChange(true) + , m_subsIPAddrChange(true) + , m_subsInternetChange(true) + { + ServiceMock* service = new ServiceMock(); + WPEFramework::PluginHost::IAuthenticate* mock_security_agent = new MockIAuthenticate(); + ServiceMock* mockShell = new ServiceMock(); + + EXPECT_CALL(*service, AddRef()).Times(1); + EXPECT_CALL(*service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("SecurityAgent"))); + return static_cast(mock_security_agent); + })) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + m_subsIfaceStateChange = true; + m_subsActIfaceChange = true; + m_subsIPAddrChange = true; + m_subsInternetChange = true; + return static_cast(mockShell); + })); + m_service = service; + EXPECT_CALL(*mockShell, State()) + .WillOnce(::testing::Return(PluginHost::IShell::state::UNAVAILABLE)) + .WillOnce(::testing::Return(PluginHost::IShell::state::UNAVAILABLE)) + .WillRepeatedly(::testing::Return(PluginHost::IShell::state::ACTIVATED)); + EXPECT_EQ(string{}, plugin->Initialize(service)); + delete mockShell; + delete mock_security_agent; + } + virtual ~NetworkTest() override + { + plugin->Deinitialize(m_service); + m_service->Release(); + delete m_service; + } +}; + +TEST_F(NetworkTest, getInterfaces) +{ + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + NiceMock mockIterator; + Exchange::INetworkManager::InterfaceDetails entry1; + entry1.type = Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_ETHERNET; + entry1.mac = "00:11:22:33:44:55"; + entry1.enabled = true; + entry1.connected = false; + + Exchange::INetworkManager::InterfaceDetails entry2; + entry2.type = Exchange::INetworkManager::InterfaceType::INTERFACE_TYPE_WIFI; + entry2.mac = "66:77:88:99:AA:BB"; + entry2.enabled = false; + entry2.connected = true; + + // Set up Next() to return two entries, then false + EXPECT_CALL(mockIterator, Next(testing::_)) + .WillOnce(testing::DoAll(testing::SetArgReferee<0>(entry1), testing::Return(true))) + .WillOnce(testing::DoAll(testing::SetArgReferee<0>(entry2), testing::Return(true))) + .WillOnce(testing::Return(false)); + EXPECT_CALL(mockIterator, Release()).Times(1); + NiceMock service; + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, GetAvailableInterfaces(::testing::_)) + .WillOnce(testing::DoAll( + ::testing::Invoke([&mockIterator](WPEFramework::Exchange::INetworkManager::IInterfaceDetailsIterator*& iterator) -> uint32_t { + iterator = &mockIterator; + return 0; + }))); + EXPECT_CALL(*mockNetworkManager, Release()).Times(1); + JsonObject parameters, JsonResponse; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getInterfaces"), _T("{}"), response)); + std::string expectedResponse = "{\"interfaces\":[{\"interface\":\"ETHERNET\",\"macAddress\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":false},{\"interface\":\"WIFI\",\"macAddress\":\"66:77:88:99:AA:BB\",\"enabled\":false,\"connected\":true}],\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + delete mockNetworkManager; +} + +TEST_F(NetworkTest, RegisteredMethods) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getStbIp"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getInterfaces"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("setStunEndPoint"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("isInterfaceEnabled"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("setInterfaceEnabled"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getDefaultInterface"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("isConnectedToInternet"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getPublicIP"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getSTBIPFamily"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("setConnectivityTestEndpoints"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getCaptivePortalURI"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("startConnectivityMonitoring"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("stopConnectivityMonitoring"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("setDefaultInterface"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("setIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handlerV2.Exists(_T("getIPSettings"))); +} + +TEST_F(NetworkTest, setStunEndpoint) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + string endpoint = "stun.example.com"; + uint32_t port = 3478; + uint32_t bindTimeout = 10; + uint32_t cacheTimeout = 30; + + JsonObject parameters; + parameters["server"] = endpoint; + parameters["port"] = port; + parameters["timeout"] = bindTimeout; + parameters["cache_timeout"] = cacheTimeout; + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, SetStunEndpoint(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([&](string const endpoint, const uint32_t port, const uint32_t timeout, const uint32_t cacheLifetime ) -> uint32_t { + return 0; + })); + EXPECT_CALL(*mockNetworkManager, Release()).Times(1); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setStunEndPoint"), _T("{}"), response)); + EXPECT_EQ(response, "{\"success\":true}"); + delete mockNetworkManager; +} + +TEST_F(NetworkTest, setInterfaceEnabled){ + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + string interface = "ETHERNET"; + bool enabled = true; + + JsonObject jsonParameters; + jsonParameters["interface"] = interface; + jsonParameters["enabled"] = enabled; + string parameters; + jsonParameters.ToString(parameters); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, SetInterfaceState(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setInterfaceEnabled"), _T(parameters), response)); + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getDefaultInterface) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + string interface = "eth0"; + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, GetPrimaryInterface(::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& interface) -> uint32_t { + interface = "eth0"; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getDefaultInterface"), _T("{}"), response)); + EXPECT_EQ(response, "{\"interface\":\"ETHERNET\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, setDefaultInterface) { + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setDefaultInterface"), _T("{}"), response)); + EXPECT_EQ(response, "{\"success\":true}"); +} + +TEST_F(NetworkTest, setIPSettings) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + jsonParameters["autoconfig"] = false; + jsonParameters["ipaddr"] = "192.168.1.100"; + jsonParameters["gateway"] = "192.168.1.1"; + jsonParameters["primarydns"] = "8.8.8.8"; + jsonParameters["secondarydns"] = "8.8.4.4"; + jsonParameters["netmask"] = "255.255.255.0"; + + string parameters; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, SetIPSettings(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setIPSettings"), _T(parameters), response)); + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getIPSettings) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + WPEFramework::Exchange::INetworkManager::IPAddress address{}; + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.primarydns = "75.75.75.76"; + address.secondarydns = "75.75.76.76"; + address.dhcpserver = "192.168.0.1"; + address.gateway = "192.168.0.1"; + address.ipaddress = "192.168.0.11"; + address.ipversion = "IPv4"; + address.prefix = 24; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getIPSettings"), _T(parameters), response)); + EXPECT_EQ(response, "{\"primarydns\":\"75.75.75.76\",\"gateway\":\"192.168.0.1\",\"secondarydns\":\"75.75.76.76\",\"dhcpserver\":\"192.168.0.1\",\"netmask\":\"255.255.255.0\",\"ipaddr\":\"192.168.0.11\",\"autoconfig\":false,\"ipversion\":\"IPv4\",\"interface\":\"WIFI\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getIPSettingsIPv6) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv6"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + WPEFramework::Exchange::INetworkManager::IPAddress address{}; + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.ipaddress = "2001:0db8:85a3:0000:0000:8a2e:0370:7334"; + address.ipversion = "IPv6"; + address.prefix = 64; + address.primarydns = "2001:4860:4860::8888"; + address.gateway = "fe80::1"; + address.secondarydns = "2001:4860:4860::8844"; + address.dhcpserver = "fe80::2"; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getIPSettings"), _T(parameters), response)); + EXPECT_EQ(response, "{\"primarydns\":\"2001:4860:4860::8888\",\"gateway\":\"fe80::1\",\"secondarydns\":\"2001:4860:4860::8844\",\"dhcpserver\":\"fe80::2\",\"netmask\":64,\"ipaddr\":\"2001:0db8:85a3:0000:0000:8a2e:0370:7334\",\"autoconfig\":false,\"ipversion\":\"IPv6\",\"interface\":\"WIFI\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getIPSettingsErrorEmptyString) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + WPEFramework::Exchange::INetworkManager::IPAddress address{}; + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.ipaddress = ""; + address.ipversion = "IPv4"; + address.prefix = 34; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getIPSettings"), _T(parameters), response)); + EXPECT_EQ(response, "{\"success\":false}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getIPSettings2) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + WPEFramework::Exchange::INetworkManager::IPAddress address{}; + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.primarydns = "75.75.75.76"; + address.secondarydns = "75.75.76.76"; + address.dhcpserver = "192.168.0.1"; + address.gateway = "192.168.0.1"; + address.ipaddress = "192.168.0.11"; + address.ipversion = "IPv4"; + address.prefix = 24; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handlerV2.Invoke(connection, _T("getIPSettings"), _T(parameters), response)); + string expectedResponse = "{\"interface\":\"WIFI\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddr\":\"192.168.0.11\",\"netmask\":\"255.255.255.0\",\"dhcpserver\":\"192.168.0.1\",\"gateway\":\"192.168.0.1\",\"primarydns\":\"75.75.75.76\",\"secondarydns\":\"75.75.76.76\",\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, isConnectedToInternet) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, IsConnectedToInternet(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , string&, WPEFramework::Exchange::INetworkManager::InternetStatus& result, string&) -> uint32_t + { + result = WPEFramework::Exchange::INetworkManager::InternetStatus::INTERNET_FULLY_CONNECTED; + return Core::ERROR_NONE; + })); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("isConnectedToInternet"), _T(parameters), response)); + EXPECT_EQ(response, "{\"ipversion\":\"IPv4\",\"connectedToInternet\":true,\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getInternetConnectionState) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, IsConnectedToInternet(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const string&, const string&, Exchange::INetworkManager::InternetStatus& status, string&) { + status = Exchange::INetworkManager::InternetStatus::INTERNET_CAPTIVE_PORTAL; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, GetCaptivePortalURI(::testing::_)) + .Times(1) + .WillOnce(::testing::DoAll( + ::testing::SaveArg<0>(&response), + ::testing::Return(Core::ERROR_NONE))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getInternetConnectionState"), _T(parameters), response)); + EXPECT_EQ(response, "{\"ipversion\":\"IPv4\",\"state\":2,\"URI\":\"\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, doPing) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + std::string traceResult = "{" + "\"success\": true," + "\"tripStdDev\": \"0.741 ms\"," + "\"tripAvg\": \"16.702\"," + "\"tripMin\": \"15.747\"," + "\"tripMax\": \"17.564\"," + "\"packetLoss\": \"0\"," + "\"endpoint\": \"8.8.8.8\"," + "\"packetsReceived\": 5," + "\"packetsTransmitted\": 5," + "\"target\": \"8.8.8.8\"" + "}"; + EXPECT_CALL(*mockNetworkManager, Ping(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const string, const string, const uint32_t, const uint16_t, const string, string& result) -> uint32_t + { + result = traceResult; + return Core::ERROR_NONE; + })); + + JsonObject parametersJson; + parametersJson["endpoint"] = "8.8.8.8"; + parametersJson["packets"] = 5; + parametersJson["ipversion"] = "IPv4"; + parametersJson["guid"] = "..."; + string parameters; + parametersJson.ToString(parameters); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("ping"), _T(parameters), response)); + string expectedResponse = "{\"packetsTransmitted\":5,\"packetsReceived\":5,\"packetLoss\":\"0\",\"target\":\"8.8.8.8\",\"tripMax\":\"17.564\",\"tripMin\":\"15.747\",\"tripAvg\":\"16.702\",\"tripStdDev\":\"0.741 ms\",\"endpoint\":\"8.8.8.8\",\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, doTrace) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + std::string traceResult = "{" + "\"results\": \"[\\\"traceroute to 8.8.8.8 (8.8.8.8), 6 hops max, 52 byte packets \\\",\\\"" + "1 gateway (10.46.5.1) 0.448 ms\\\",\\\"" + "2 10.46.0.240 (10.46.0.240) 3.117 ms\\\",\\\"" + "3 *\\\",\\\"" + "4 *\\\",\\\"" + "5 *\\\",\\\"" + "6 *\\\"]\"," + "\"endpoint\": \"8.8.8.8\"," + "\"success\": true" + "}"; + EXPECT_CALL(*mockNetworkManager, Trace(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const string, const string, const uint32_t, const string, string& response1) -> uint32_t + { + response1 = traceResult; + return Core::ERROR_NONE; + })); + + JsonObject parametersJson; + parametersJson["endpoint"] = "8.8.8.8"; + parametersJson["ipversion"] = "IPv4"; + parametersJson["packets"] = 1; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("trace"), _T(parametersStr), response)); + // We expect the response to contain success and results fields + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"results\":") != std::string::npos); + EXPECT_TRUE(response.find("\"endpoint\":\"8.8.8.8\"") != std::string::npos); + EXPECT_TRUE(response.find("\"target\":\"8.8.8.8\"") != std::string::npos); + EXPECT_TRUE(response.find("6 hops max, 52 byte packets") != std::string::npos); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getPublicIP) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, GetPublicIP(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string&, string&, string& ipaddress) { + ipaddress = "69.136.49.95"; + return Core::ERROR_NONE; + })); + + + JsonObject parametersJson; + parametersJson["ipv6"] = true; + parametersJson["iface"] = "WIFI"; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getPublicIP"), _T(parametersStr), response)); + EXPECT_EQ(response, "{\"public_ip\":\"69.136.49.95\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, isInterfaceEnabled) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, GetInterfaceState(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::DoAll( + ::testing::Return(Core::ERROR_NONE))); + + JsonObject parametersJson; + parametersJson["interface"] = "WIFI"; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("isInterfaceEnabled"), _T(parametersStr), response)); + EXPECT_EQ(response, "{\"enabled\":false,\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, isInterfaceEnabledErrorInvalidInterface) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + JsonObject parametersJson; + parametersJson["interface"] = "INVALID"; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_BAD_REQUEST, handler.Invoke(connection, _T("isInterfaceEnabled"), _T(parametersStr), response)); + EXPECT_EQ(response, string{}); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, setConnectivityTestEndpoints) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, SetConnectivityTestEndpoints(::testing::_)) + .Times(1) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + + JsonArray array; + array.Add("http://example.com"); + array.Add("http://example2.com"); + array.Add("http://example3.com"); + + JsonObject parametersJson; + parametersJson["endpoints"] = array; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setConnectivityTestEndpoints"), _T(parametersStr), response)); + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, setConnectivityTestEndpointsErrorInvalidType) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + JsonArray array; + array.Add("http://example.com"); + array.Add(1234); + array.Add("http://example3.com"); + + JsonObject parametersJson; + parametersJson["endpoints"] = array; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setConnectivityTestEndpoints"), _T(parametersStr), response)); + EXPECT_EQ(response, "{\"success\":false}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, setConnectivityTestEndpointsErrorEmptyArray) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + JsonArray array; + + JsonObject parametersJson; + string parametersStr; + parametersJson.ToString(parametersStr); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("setConnectivityTestEndpoints"), _T(parametersStr), response)); + EXPECT_EQ(response, "{\"success\":false}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, startConnectivityMonitoring) { + string parameters; + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("startConnectivityMonitoring"), _T(parameters), response)); + EXPECT_EQ(response, "{\"success\":true}"); +} + +TEST_F(NetworkTest, getCaptivePortalURI) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + string parameters; + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + EXPECT_CALL(*mockNetworkManager, GetCaptivePortalURI(::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& uri) { + uri = "http://10.0.0.1/captiveportal.jst"; + return Core::ERROR_NONE; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCaptivePortalURI"), _T(parameters), response)); + EXPECT_EQ(response, "{\"uri\":\"http:\\/\\/10.0.0.1\\/captiveportal.jst\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, stopConnectivityMonitoring) { + string parameters; + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("stopConnectivityMonitoring"), _T(parameters), response)); + EXPECT_EQ(response, "{\"success\":true}"); +} + +TEST_F(NetworkTest, getStbIp) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.ipaddress = "192.168.0.1"; + address.ipversion = "IPv4"; + address.prefix = 24; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getStbIp"), _T(parameters), response)); + EXPECT_EQ(response, "{\"ip\":\"192.168.0.1\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, getSTBIPFamily) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + JsonObject jsonParameters; + jsonParameters["interface"] = "WIFI"; + jsonParameters["ipversion"] = "IPv4"; + string parameters; + string response; + jsonParameters.ToString(parameters); + + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, GetIPSettings(::testing::_, ::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](string& , const string&, WPEFramework::Exchange::INetworkManager::IPAddress& address) -> uint32_t { + address.ipaddress = "192.168.0.1"; + address.ipversion = "IPv4"; + address.prefix = 24; + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getSTBIPFamily"), _T({}), response)); + EXPECT_EQ(response, "{\"ip\":\"192.168.0.1\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(NetworkTest, ReportonInterfaceStateChangeAdded) { + StubNetwork network; + JsonObject parameters; + parameters["status"] = "INTERFACE_ADDED"; + parameters["interface"] = "ETHERNET"; + network.onInterfaceStateChange(parameters); +} + +TEST_F(NetworkTest, ReportonInterfaceStateChangeRemoved) { + StubNetwork network; + JsonObject parameters; + parameters["status"] = "INTERFACE_REMOVED"; + parameters["interface"] = "eth0"; + network.onInterfaceStateChange(parameters); +} + +TEST_F(NetworkTest, ReportonInterfaceStateChangeLinkUp) { + StubNetwork network; + JsonObject parameters; + parameters["status"] = "INTERFACE_LINK_UP"; + parameters["interface"] = "wlan0"; + network.onInterfaceStateChange(parameters); +} + +TEST_F(NetworkTest, ReportonInterfaceStateChangeLinkDown) { + StubNetwork network; + JsonObject parameters; + parameters["status"] = "INTERFACE_LINK_DOWN"; + parameters["interface"] = "eth0"; + network.onInterfaceStateChange(parameters); +} + +TEST_F(NetworkTest, ReportonActiveInterfaceChange) { + StubNetwork network; + JsonObject parameters; + parameters["prevActiveInterface"] = "eth0"; + parameters["currentActiveInterface"] = "wlan0"; + network.onActiveInterfaceChange(parameters); +} + +TEST_F(NetworkTest, ReportonIPAddressChange_IPv4) { + StubNetwork network; + JsonObject parameters; + parameters["interface"] = "eth0"; + parameters["ipversion"] = "IPv4"; + parameters["ipaddress"] = "192.168.1.100"; + network.onIPAddressChange(parameters); +} + +TEST_F(NetworkTest, ReportonIPAddressChange_IPv6) { + StubNetwork network; + JsonObject parameters; + parameters["interface"] = "wlan0"; + parameters["ipversion"] = "IPv6"; + parameters["ipaddress"] = "fe80::1"; + + network.onIPAddressChange(parameters); +} + +TEST_F(NetworkTest, ReportonInternetStatusChange) { + StubNetwork network; + JsonObject parameters; + parameters["state"] = "CONNECTED"; + parameters["status"] = "OK"; + network.onInternetStatusChange(parameters); +} + +TEST_F(NetworkTest, Information) { + StubNetwork network; + network.Information(); +} diff --git a/tests/l2Test/legacy/l2_test_LegacyPlugin_WiFiManagerAPIs.cpp b/tests/l2Test/legacy/l2_test_LegacyPlugin_WiFiManagerAPIs.cpp new file mode 100644 index 000000000..ddb81495f --- /dev/null +++ b/tests/l2Test/legacy/l2_test_LegacyPlugin_WiFiManagerAPIs.cpp @@ -0,0 +1,898 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include +#include +#include "ServiceMock.h" +#include "FactoriesImplementation.h" +#include "NetworkManagerLogger.h" +#include "LegacyWiFiManagerAPIs.h" +#include "ThunderPortability.h" +#include "INetworkManagerMock.h" +#include "LegacyWiFiManagerMock.h" +#include "IStringIteratorMock.h" +#include "mockauthservices.h" + +using namespace std; +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using ::testing::NiceMock; + +#define WPA_SUPPLICANT_CONF "/opt/secure/wifi/wpa_supplicant.conf/wpa_supplicant.conf" + +Plugin::WiFiManager* _gWiFiInstance = nullptr; +class WiFiManagerTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + Core::JSONRPC::Handler& handlerV2; + DECL_CORE_JSONRPC_CONX connection; + ServiceMock *m_service; + Core::JSONRPC::Message message; + string response; + + WiFiManagerTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , handlerV2(*(plugin->GetHandler(2))) + , INIT_CONX(1, 0) + { + ServiceMock* service = new ServiceMock(); + WPEFramework::PluginHost::IAuthenticate* mock_security_agent = new MockIAuthenticate(); + ServiceMock* mockShell = new ServiceMock(); + + EXPECT_CALL(*service, AddRef()).Times(1); + EXPECT_CALL(*service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("SecurityAgent"))); + return static_cast(mock_security_agent); + })) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockShell); + })); + m_service = service; + EXPECT_CALL(*mockShell, State()) + .WillOnce(::testing::Return(PluginHost::IShell::state::UNAVAILABLE)) + .WillOnce(::testing::Return(PluginHost::IShell::state::UNAVAILABLE)) + .WillRepeatedly(::testing::Return(PluginHost::IShell::state::ACTIVATED)); + EXPECT_EQ(string{}, plugin->Initialize(service)); + delete mockShell; + delete mock_security_agent; + } + + virtual ~WiFiManagerTest() override + { + plugin->Deinitialize(m_service); + m_service->Release(); + delete m_service; + } +}; + +TEST_F(WiFiManagerTest, TestedAPIsShouldExist) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("startScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("stopScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("connect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("disconnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("initiateWPSPairing"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("cancelWPSPairing"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("saveSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("clearSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getPairedSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getPairedSSIDInfo"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("isPaired"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getCurrentState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("getConnectedSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("retrieveSSID"))); +} + +TEST_F(WiFiManagerTest, cancelWPSPairing) +{ + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, StopWPS()) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("cancelWPSPairing"), _T("{}"), response)); + EXPECT_EQ(response, "{\"result\":\"\",\"success\":true}"); + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, clearSSID) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, RemoveKnownSSID(::testing::_)) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("clearSSID"), _T("{}"), response)); + EXPECT_EQ(response, "{\"result\":0,\"success\":true}"); + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, connect) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, WiFiConnect(::testing::_)) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Create a sample parameters object + JsonObject jsonParameters; + jsonParameters["ssid"] = "my-ssid"; + jsonParameters["passphrase"] = "my-passphrase"; + jsonParameters["securityMode"] = 1; // Assuming 1 is the value for WPA2 + + string parameters; + jsonParameters.ToString(parameters); + // Call the connect method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("connect"), parameters, response)); + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getConnectedSSID) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + ssidInfo.ssid = "my-ssid"; + ssidInfo.bssid = "00:11:22:33:44:55"; + ssidInfo.rate = 100; + ssidInfo.noise = -50; + ssidInfo.security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; + ssidInfo.strength = 50; + ssidInfo.frequency = 2.4; + EXPECT_CALL(*mockNetworkManager, GetConnectedSSID(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(ssidInfo), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getConnectedSSID method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getConnectedSSID"), _T("{}"), response)); + + std::string expectedResponse = "{\"ssid\":\"my-ssid\",\"bssid\":\"00:11:22:33:44:55\",\"rate\":\"100\",\"noise\":\"-50\",\"security\":6,\"signalStrength\":\"50\",\"frequency\":\"2.4\",\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getConnectedSSIDSAE) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + ssidInfo.ssid = "my-ssid"; + ssidInfo.bssid = "00:11:22:33:44:55"; + ssidInfo.rate = 100; + ssidInfo.noise = -50; + ssidInfo.security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_SAE; + ssidInfo.strength = 50; + ssidInfo.frequency = 2.4; + EXPECT_CALL(*mockNetworkManager, GetConnectedSSID(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(ssidInfo), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getConnectedSSID method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getConnectedSSID"), _T("{}"), response)); + + std::string expectedResponse = "{\"ssid\":\"my-ssid\",\"bssid\":\"00:11:22:33:44:55\",\"rate\":\"100\",\"noise\":\"-50\",\"security\":14,\"signalStrength\":\"50\",\"frequency\":\"2.4\",\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getConnectedSSIDEAP) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + ssidInfo.ssid = "my-ssid"; + ssidInfo.bssid = "00:11:22:33:44:55"; + ssidInfo.rate = 100; + ssidInfo.noise = -50; + ssidInfo.security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_EAP; + ssidInfo.strength = 50; + ssidInfo.frequency = 2.4; + EXPECT_CALL(*mockNetworkManager, GetConnectedSSID(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(ssidInfo), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getConnectedSSID method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getConnectedSSID"), _T("{}"), response)); + + std::string expectedResponse = "{\"ssid\":\"my-ssid\",\"bssid\":\"00:11:22:33:44:55\",\"rate\":\"100\",\"noise\":\"-50\",\"security\":12,\"signalStrength\":\"50\",\"frequency\":\"2.4\",\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getCurrentState) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED; + EXPECT_CALL(*mockNetworkManager, GetWifiState(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(state), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getCurrentState method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCurrentState"), _T("{}"), response)); + + std::string expectedResponse = "{\"state\":5,\"success\":true}"; + // Verify the response + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getCurrentStateFailed1) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WiFiState::WIFI_STATE_ERROR; + EXPECT_CALL(*mockNetworkManager, GetWifiState(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(state), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getCurrentState method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCurrentState"), _T("{}"), response)); + + std::string expectedResponse = "{\"state\":6,\"success\":true}"; + // Verify the response + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getCurrentStateFailed2) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTION_INTERRUPTED; + EXPECT_CALL(*mockNetworkManager, GetWifiState(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(state), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getCurrentState method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCurrentState"), _T("{}"), response)); + + std::string expectedResponse = "{\"state\":2,\"success\":true}"; + // Verify the response + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getCurrentStateDisconnected) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WiFiState::WIFI_STATE_DISCONNECTED; + EXPECT_CALL(*mockNetworkManager, GetWifiState(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(state), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getCurrentState method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCurrentState"), _T("{}"), response)); + + std::string expectedResponse = "{\"state\":2,\"success\":true}"; + // Verify the response + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getCurrentStateConnected) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiState state = Exchange::INetworkManager::WiFiState::WIFI_STATE_CONNECTED; + EXPECT_CALL(*mockNetworkManager, GetWifiState(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(state), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getCurrentState method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getCurrentState"), _T("{}"), response)); + + std::string expectedResponse = "{\"state\":5,\"success\":true}"; + // Verify the response + EXPECT_EQ(response, expectedResponse); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getPairedSSIDInfo) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiSSIDInfo ssidInfo{}; + ssidInfo.ssid = "my-ssid"; + ssidInfo.bssid = "00:11:22:33:44:55"; + EXPECT_CALL(*mockNetworkManager, GetConnectedSSID(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(ssidInfo), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the getPairedSSIDInfo method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getPairedSSIDInfo"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"ssid\":\"my-ssid\",\"bssid\":\"00:11:22:33:44:55\",\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, getSupportedSecurityModes) { + // Call the getSupportedSecurityModes method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getSupportedSecurityModes"), _T("{}"), response)); + + // Verify the response + std::string expectedResponse = "{\"security_modes\":{\"NET_WIFI_SECURITY_NONE\":0,\"NET_WIFI_SECURITY_WEP_64\":1,\"NET_WIFI_SECURITY_WEP_128\":2,\"NET_WIFI_SECURITY_WPA_PSK_TKIP\":3,\"NET_WIFI_SECURITY_WPA_PSK_AES\":4,\"NET_WIFI_SECURITY_WPA2_PSK_TKIP\":5,\"NET_WIFI_SECURITY_WPA2_PSK_AES\":6,\"NET_WIFI_SECURITY_WPA_ENTERPRISE_TKIP\":7,\"NET_WIFI_SECURITY_WPA_ENTERPRISE_AES\":8,\"NET_WIFI_SECURITY_WPA2_ENTERPRISE_TKIP\":9,\"NET_WIFI_SECURITY_WPA2_ENTERPRISE_AES\":10,\"NET_WIFI_SECURITY_WPA_WPA2_PSK\":11,\"NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE\":12,\"NET_WIFI_SECURITY_WPA3_PSK_AES\":13,\"NET_WIFI_SECURITY_WPA3_SAE\":14,\"NET_WIFI_SECURITY_NOT_SUPPORTED\":99},\"success\":true}"; + EXPECT_EQ(response, expectedResponse); + +} + +TEST_F(WiFiManagerTest, saveSSID) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + Exchange::INetworkManager::WiFiConnectTo ssid{}; + ssid.ssid = "my-ssid"; + ssid.passphrase = "my-passphrase"; + ssid.security = Exchange::INetworkManager::WIFISecurityMode::WIFI_SECURITY_WPA_PSK; + EXPECT_CALL(*mockNetworkManager, AddToKnownSSIDs(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Create a sample parameters object + JsonObject jsonParameters; + jsonParameters["ssid"] = "my-ssid"; + jsonParameters["passphrase"] = "my-passphrase"; + jsonParameters["security"] = 1; // Assuming 1 is the value for WPA2 + + string parameters; + jsonParameters.ToString(parameters); + // Call the saveSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("saveSSID"), parameters, response)); + + // Verify the response + EXPECT_EQ(response, "{\"result\":0,\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, disconnect) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, WiFiDisconnect()) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the disconnect method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("disconnect"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"result\":0,\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, initiateWPSPairing) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, StartWPS(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Create a sample parameters object + JsonObject jsonParameters; + jsonParameters["method"] = static_cast(Exchange::INetworkManager::WiFiWPS::WIFI_WPS_PIN); + jsonParameters["pin"] = "my-pin"; + + string parameters; + jsonParameters.ToString(parameters); + // Call the initiateWPSPairing method + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("initiateWPSPairing"), parameters, response)); + + // Verify the response + EXPECT_EQ(response, "{\"result\":0,\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, startScan) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, StartWiFiScan(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Create a sample parameters object + JsonObject jsonParameters; + jsonParameters["frequency"] = "2.4GHz"; + jsonParameters["ssid"] = "test"; + + // Call the startScan method + std::string parameters; + jsonParameters.ToString(parameters); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("startScan"), parameters, response)); + + // Verify the response + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, stopScan) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, StopWiFiScan()) + .WillOnce(::testing::Return(Core::ERROR_NONE)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the stopScan method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("stopScan"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"success\":true}"); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, stopScan_Error) { + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + EXPECT_CALL(*mockNetworkManager, StopWiFiScan()) + .WillOnce(::testing::Return(Core::ERROR_GENERAL)); + EXPECT_CALL(*mockNetworkManager, Release()) + .Times(1); + + // Call the stopScan method + std::string response; + EXPECT_EQ(Core::ERROR_GENERAL, handler.Invoke(connection, _T("stopScan"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, string{}); + + delete mockNetworkManager; +} + +TEST_F(WiFiManagerTest, onWiFiStateChange1) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_SSID_CHANGED); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange2) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_CONNECTION_LOST); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange3) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_CONNECTION_FAILED); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange4) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_CONNECTION_INTERRUPTED); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange5) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_INVALID_CREDENTIALS); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange6) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_SSID_NOT_FOUND); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange7) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_ERROR); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiStateChange8) { + StubWiFi wifi; + JsonObject parameters; + parameters["state"] = static_cast(Exchange::INetworkManager::WIFI_STATE_AUTHENTICATION_FAILED); + wifi.onWiFiStateChange(parameters); +} + +TEST_F(WiFiManagerTest, onAvailableSSIDs) { + StubWiFi wifi; + JsonObject parameters; + JsonArray ssids; + JsonObject ssid1; + ssid1["ssid"] = "test"; + ssid1["security"] = 2; + ssid1["signalStrength"] = "-27.000"; + ssid1["frequency"] = "2.4"; + ssids.Add(ssid1); + parameters["ssids"] = ssids; + + wifi.onAvailableSSIDs(parameters); +} + +TEST_F(WiFiManagerTest, onWiFiSignalQualityChange) { + StubWiFi wifi; + JsonObject parameters; + parameters["signalStrength"] = "27.00"; + parameters["strength"] = "Excellent"; + + wifi.onWiFiSignalQualityChange(parameters); +} + +TEST_F(WiFiManagerTest, Information) { + StubWiFi wifi; + wifi.Information(); +} + +TEST_F(WiFiManagerTest, getPairedSSID) { + // Create a mock network manager object + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + // Set up the mock network manager to return a mock string iterator + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + // Set up the mock string iterator to return a string + MockStringIterator* mockStringIterator = new MockStringIterator(); + EXPECT_CALL(*mockNetworkManager, GetKnownSSIDs(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(mockStringIterator), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockStringIterator, Next(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>("my-ssid"), + ::testing::Return(true))); + + // Call the getPairedSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("getPairedSSID"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"ssid\":\"my-ssid\",\"success\":true}"); + + // Clean up + delete mockNetworkManager; + delete mockStringIterator; +} + +TEST_F(WiFiManagerTest, isPaired) { + // Create a mock network manager object + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + // Set up the mock network manager to return a mock string iterator + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + // Set up the mock string iterator to return a string + MockStringIterator* mockStringIterator = new MockStringIterator(); + EXPECT_CALL(*mockNetworkManager, GetKnownSSIDs(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>(mockStringIterator), + ::testing::Return(Core::ERROR_NONE))); + EXPECT_CALL(*mockStringIterator, Next(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::SetArgReferee<0>("my-ssid"), + ::testing::Return(true))); + + // Call the getPairedSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("isPaired"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"result\":1,\"success\":true}"); + + // Clean up + delete mockNetworkManager; + delete mockStringIterator; +} + +TEST_F(WiFiManagerTest, isPairedNoSSID) { + // Create a mock network manager object + MockINetworkManager* mockNetworkManager = new MockINetworkManager(); + + // Set up the mock network manager to return a mock string iterator + EXPECT_CALL(*m_service, QueryInterfaceByCallsign(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const string& name) -> void* { + EXPECT_EQ(name, string(_T("org.rdk.NetworkManager"))); + return static_cast(mockNetworkManager); + })); + + // Set up the mock string iterator to return a string + EXPECT_CALL(*mockNetworkManager, GetKnownSSIDs(::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Return(Core::ERROR_NONE))); + + // Call the getPairedSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("isPaired"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"result\":0,\"success\":true}"); + + // Clean up + delete mockNetworkManager; +} + +// Create a sample configuration file +std::string configFileContent = "ssid=\"my-ssid\"\n" +"key_mgmt=PSK\n" +"psk=\"my-passphrase\"\n"; + +void wpaSupplicantConfCreate(std::string configFileContent) +{ + std::string filePath = "/opt/secure/wifi/wpa_supplicant.conf"; + // Create the parent directories if they don't exist + size_t pos = filePath.find_last_of('/'); + if (pos != std::string::npos) { + std::string dirPath = filePath.substr(0, pos); + std::string cmd = "mkdir -p " + dirPath; + FILE* pipe = popen(cmd.c_str(), "w"); + if (!pipe) { + // Handle error + } + pclose(pipe); + } + std::ofstream configFileOut(filePath, std::ios_base::out | std::ios_base::app); + // Check if the file is open + if (configFileOut.is_open()) { + // File is open, write to it + configFileOut << configFileContent; + configFileOut.close(); + + // Print the content of the file + std::ifstream configFileRead(filePath); + if (configFileRead.is_open()) { + std::cout << configFileRead.rdbuf(); + configFileRead.close(); + } else { + std::cerr << "Unable to open file for reading." << std::endl; + } + } else { + std::cerr << "Unable to open file for writing." << std::endl; + } +} + +TEST_F(WiFiManagerTest, retrieveSSIDWPAPSK) { + std::string configFileContent = "ssid=\"my-ssid\"\n" + "key_mgmt=PSK\n" + "psk=\"my-passphrase\"\n"; + + wpaSupplicantConfCreate(configFileContent); + // Call the retrieveSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("retrieveSSID"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"ssid\":\"my-ssid\",\"securityMode\":6,\"passphrase\":\"my-passphrase\",\"success\":true}"); + + // Remove the configuration file + std::remove("/opt/secure/wifi/wpa_supplicant.conf"); +} + +TEST_F(WiFiManagerTest, retrieveSSIDSecurityNone) { + std::string configFileContent = "ssid=\"my-ssid\"\n" + "key_mgmt=NONE\n" + "psk=\"my-passphrase\"\n"; + + wpaSupplicantConfCreate(configFileContent); + // Call the retrieveSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("retrieveSSID"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"ssid\":\"my-ssid\",\"securityMode\":0,\"passphrase\":\"my-passphrase\",\"success\":true}"); + + // Remove the configuration file + std::remove("/opt/secure/wifi/wpa_supplicant.conf"); +} + +TEST_F(WiFiManagerTest, retrieveSSIDSecuritySAE) { + std::string configFileContent = "ssid=\"my-ssid\"\n" + "key_mgmt=SAE\n" + "psk=\"my-passphrase\"\n"; + + wpaSupplicantConfCreate(configFileContent); + // Call the retrieveSSID method + std::string response; + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("retrieveSSID"), _T("{}"), response)); + + // Verify the response + EXPECT_EQ(response, "{\"ssid\":\"my-ssid\",\"securityMode\":14,\"passphrase\":\"my-passphrase\",\"success\":true}"); + + // Remove the configuration file + std::remove("/opt/secure/wifi/wpa_supplicant.conf"); +} diff --git a/tests/l2Test/libnm/CMakeLists.txt b/tests/l2Test/libnm/CMakeLists.txt new file mode 100644 index 000000000..d05bf0a2f --- /dev/null +++ b/tests/l2Test/libnm/CMakeLists.txt @@ -0,0 +1,199 @@ +############################################################################# +# Copyright 2024 RDK Management +# Licensed under the Apache License, Version 2.0 +############################################################################# +message ("building libnm test") + +set(NM_LIBNM_PROXY_L2_TEST "libnm_proxy_l2_test") + +find_package(CURL) +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(LIBNM REQUIRED libnm) +pkg_check_modules(GIO REQUIRED gio-2.0) + +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/609281088cfefc76f9d0ce82e1ff6c30cc3591e5.zip +) +FetchContent_MakeAvailable(googletest) + +message("networkmanager building with libnm") +add_executable(${NM_LIBNM_PROXY_L2_TEST} + ${CMAKE_SOURCE_DIR}/tests/l2Test/libnm/l2_test_libnmproxyEvent.cpp + ${CMAKE_SOURCE_DIR}/tests/l2Test/libnm/l2_test_libnmproxyWifi.cpp + ${CMAKE_SOURCE_DIR}/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp + ${CMAKE_SOURCE_DIR}/tests/l2Test/libnm/l2_test_libnmproxy.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/thunder/Module.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/Iarm.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/Wraps.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/CurlWraps.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/LibnmWraps.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/GLibWraps.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManager.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerJsonRpc.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerImplementation.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerConnectivity.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerStunClient.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerPowerClient.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeProxy.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeWIFI.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeEvents.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeUtils.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerSecretAgent.cpp + ${PROXY_STUB_SOURCES} +) + +if(USE_CONNECTIVITYCHECKMGR) + target_sources(${NM_LIBNM_PROXY_L2_TEST} PRIVATE + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerConnectivityClient.cpp + ) +endif() + +set_target_properties(${NM_LIBNM_PROXY_L2_TEST} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES +) + +target_compile_options(${NM_LIBNM_PROXY_L2_TEST} PRIVATE -Wall -include ${CMAKE_SOURCE_DIR}/interface/INetworkManager.h) + +# Add a definition to handle the NM_DEVICE macro in test +target_compile_definitions(${NM_LIBNM_PROXY_L2_TEST} PRIVATE + NM_DEVICE_CAST_WORKAROUND=1 +) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin) +include_directories(${PROJECT_SOURCE_DIR}/plugin/gnome) +include_directories(${PROJECT_SOURCE_DIR}/legacy) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks/thunder) + +target_include_directories(${NM_LIBNM_PROXY_L2_TEST} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${GLIB_INCLUDE_DIRS} + ${LIBNM_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS} + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include +) + +target_link_options(${NM_LIBNM_PROXY_L2_TEST} PUBLIC + -g + -Wall + -Wl,-wrap,system + -Wl,-wrap,popen + -Wl,-wrap,syslog + -Wl,-wrap,pclose + -Wl,-wrap,getmntent + -Wl,-wrap,setmntent + -Wl,-wrap,v_secure_popen + -Wl,-wrap,v_secure_pclose + -Wl,-wrap,v_secure_system + -Wl,-wrap,nm_device_get_iface + -Wl,-wrap,nm_active_connection_get_id + -Wl,-wrap,nm_active_connection_get_connection_type + -Wl,-wrap,nm_device_get_state_reason + -Wl,-wrap,nm_ip_address_get_family + -Wl,-wrap,nm_client_get_state + -Wl,-wrap,nm_client_get_nm_running + -Wl,-wrap,nm_client_get_primary_connection + -Wl,-wrap,nm_active_connection_get_connection + -Wl,-wrap,nm_connection_get_interface_name + -Wl,-wrap,nm_client_get_device_by_iface + -Wl,-wrap,nm_device_get_state + -Wl,-wrap,nm_client_get_devices + -Wl,-wrap,nm_client_new + -Wl,-wrap,nm_device_get_hw_address + -Wl,-wrap,nm_client_get_active_connections + -Wl,-wrap,nm_connection_get_setting_connection + -Wl,-wrap,nm_setting_connection_get_interface_name + -Wl,-wrap,nm_active_connection_get_ip4_config + -Wl,-wrap,nm_active_connection_get_ip6_config + -Wl,-wrap,nm_active_connection_get_dhcp4_config + -Wl,-wrap,nm_active_connection_get_dhcp6_config + -Wl,-wrap,nm_connection_get_setting_ip4_config + -Wl,-wrap,nm_connection_get_setting_ip6_config + -Wl,-wrap,nm_setting_ip_config_get_method + -Wl,-wrap,nm_ip_config_get_addresses + -Wl,-wrap,nm_ip_address_get_address + -Wl,-wrap,nm_ip_address_get_prefix + -Wl,-wrap,nm_ip_config_get_gateway + -Wl,-wrap,nm_ip_config_get_nameservers + -Wl,-wrap,nm_dhcp_config_get_one_option + -Wl,-wrap,nm_device_disconnect_async + -Wl,-wrap,nm_device_disconnect_finish + -Wl,-wrap,g_main_loop_is_running + -Wl,--wrap=g_signal_connect_data + -Wl,-wrap,nm_access_point_get_flags + -Wl,-wrap,nm_access_point_get_wpa_flags + -Wl,-wrap,nm_access_point_get_rsn_flags + -Wl,-wrap,nm_access_point_get_ssid + -Wl,-wrap,nm_access_point_get_bssid + -Wl,-wrap,nm_access_point_get_frequency + -Wl,-wrap,nm_access_point_get_mode + -Wl,-wrap,nm_access_point_get_max_bitrate + -Wl,-wrap,nm_access_point_get_strength + -Wl,-wrap,nm_device_wifi_get_active_access_point + -Wl,-wrap,nm_device_wifi_request_scan_async + -Wl,-wrap,nm_device_wifi_request_scan_options_async + -Wl,-wrap,nm_device_wifi_request_scan_finish + -Wl,-wrap,nm_client_activate_connection_async + -Wl,-wrap,nm_connection_get_connection_type + -Wl,-wrap,nm_connection_get_id + -Wl,-wrap,nm_connection_to_dbus + -Wl,-wrap,nm_client_get_connections + -Wl,-wrap,nm_device_set_autoconnect + -Wl,-wrap,nm_device_wifi_get_last_scan + -Wl,-wrap,nm_client_activate_connection_finish + -Wl,-wrap,nm_client_add_and_activate_connection_async + -Wl,-wrap,nm_remote_connection_update2_finish + -Wl,-wrap,nm_remote_connection_update2 + -Wl,-wrap,nm_client_add_connection2 + -Wl,-wrap,nm_client_add_connection2_finish + -Wl,-wrap,nm_client_add_and_activate_connection_finish + -Wl,-wrap,nm_device_wifi_get_access_points + -Wl,-wrap,nm_device_get_available_connections + -Wl,-wrap,nm_object_get_path + -Wl,-wrap,nm_client_dbus_set_property + -Wl,-wrap,nm_client_dbus_set_property_finish + -Wl,-wrap,nm_device_get_active_connection + -Wl,-wrap,nm_remote_connection_commit_changes + -Wl,-wrap,nm_setting_ip_config_get_dhcp_hostname + -Wl,-wrap,nm_setting_ip_config_get_dhcp_send_hostname + -Wl,-wrap,nm_connection_add_setting + -Wl,-wrap,nm_setting_wireless_get_ssid + -Wl,-wrap,nm_connection_get_setting_wireless + -Wl,-wrap,nm_remote_connection_delete + -Wl,-wrap,nm_remote_connection_delete_finish + -Wl,-wrap,nm_access_point_connection_valid + -Wl,-wrap,curl_multi_perform + -Wl,-wrap,curl_multi_info_read + -Wl,-wrap,curl_multi_poll + -Wl,-wrap,nm_device_get_ip4_config +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + +target_link_libraries(${NM_LIBNM_PROXY_L2_TEST} PRIVATE + gmock_main + ${GLIB_LIBRARIES} + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${CURL_LIBRARIES} + ${LIBNM_LIBRARIES} + ${GIO_LIBRARIES} +) + +if(USE_CONNECTIVITYCHECKMGR) + target_link_libraries(${NM_LIBNM_PROXY_L2_TEST} PRIVATE ${RFCAPI_LIBRARY}) + if(RFCAPI_INCLUDE_DIR) + target_include_directories(${NM_LIBNM_PROXY_L2_TEST} PRIVATE ${RFCAPI_INCLUDE_DIR}) + endif() +endif() + +install(TARGETS ${NM_LIBNM_PROXY_L2_TEST} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/tests/l2Test/libnm/l2_test_libnmproxy.cpp b/tests/l2Test/libnm/l2_test_libnmproxy.cpp new file mode 100644 index 000000000..883f24db8 --- /dev/null +++ b/tests/l2Test/libnm/l2_test_libnmproxy.cpp @@ -0,0 +1,1379 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FactoriesImplementation.h" +#include "WrapsMock.h" +#include "LibnmWrapsMock.h" +#include "GLibWrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" +#include "NetworkManagerGnomeEvents.h" +#include + +using namespace WPEFramework; +using ::testing::NiceMock; + +namespace WPEFramework { namespace Plugin { extern NetworkManagerImplementation* _instance; } } + +class NetworkManagerTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + NMClient* gNmClient = NULL; + + WrapsImplMock *p_wrapsImplMock = nullptr; + GLibWrapsImplMock *p_gLibWrapsImplMock = nullptr; + LibnmWrapsImplMock *p_libnmWrapsImplMock = nullptr; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + NetworkManagerTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + + p_libnmWrapsImplMock = new NiceMock ; + LibnmWraps::setImpl(p_libnmWrapsImplMock); + + p_gLibWrapsImplMock = new NiceMock ; + GLibWraps::setImpl(p_gLibWrapsImplMock); + + p_wrapsImplMock = new NiceMock ; + Wraps::setImpl(p_wrapsImplMock); + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + " \"autostart\":false," + " \"configuration\":{" + " \"root\":{" + " \"outofprocess\":true," + " \"locator\":\"libWPEFrameworkNetworkManagerImpl.so\"" + " }," + " \"connectivity\":{" + " \"endpoint_1\":\"http://clients3.google.com/generate_204\"," + " \"interval\":60" + " }," + " \"stun\":{" + " \"endpoint\":\"stun.l.google.com\"," + " \"port\":19302," + " \"interval\":30" + " }," + " \"loglevel\":4" + " }" + "}" + )); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + response = plugin->Initialize(&service); + EXPECT_EQ(string(""), response); + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); + } + + virtual void SetUp() override + { + struct stat buffer; + if (stat("/etc/device.properties", &buffer) != 0) { + std::ofstream file("/etc/device.properties"); + if (file.is_open()) { + file << "ETHERNET_INTERFACE=eth0\nWIFI_INTERFACE=wlan0\n"; + file.close(); + } + else { + std::cerr << "Failed to create /etc/device.properties file." << std::endl; + } + } + + /* Interface-state cache is a process-global static; reset the known + interfaces so each test starts from a deterministic empty state. */ + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("eth0"); + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("wlan0"); + } + + virtual ~NetworkManagerTest() override + { + plugin->Deinitialize(&service); + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + Wraps::setImpl(nullptr); + if (p_wrapsImplMock != nullptr) + { + delete p_wrapsImplMock; + p_wrapsImplMock = nullptr; + } + + LibnmWraps::setImpl(nullptr); + if (p_libnmWrapsImplMock != nullptr) + { + delete p_libnmWrapsImplMock; + p_libnmWrapsImplMock = nullptr; + } + + GLibWraps::setImpl(nullptr); + if (p_gLibWrapsImplMock != nullptr) + { + delete p_gLibWrapsImplMock; + p_gLibWrapsImplMock = nullptr; + } + } +}; + +TEST_F(NetworkManagerTest, RegisteredMethods) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetAvailableInterfaces"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetPrimaryInterface"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("AddToKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("RemoveKnownSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiConnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiDisconnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetConnectedSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWifiState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWiFiSignalQuality"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetSupportedSecurityModes"))); +} + +TEST_F(NetworkManagerTest, SetLogLevel) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("nmcli general logging level TRACE domains PLATFORM,DEVICE,WIFI,ETHER,DNS,DHCP4,DHCP6,DEVICE,SETTINGS,DISPATCH")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + return tempFile; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"level\":4}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetHostName_null) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetHostName_ClientCon_Null) +{ + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test_host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetHostName_iface_null) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return(nullptr)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test_host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + //g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerTest, SetHostName_iface_unknown) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return("unknown")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test_host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerTest, SetHostName_iface_wlan0_alradyset) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip4_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100173))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip6_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100174))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_ip_config_get_dhcp_hostname(::testing::_)) + .WillOnce(::testing::Return("test_host")) + .WillOnce(::testing::Return("test_host")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_ip_config_get_dhcp_send_hostname(::testing::_)) + .WillOnce(::testing::Return(TRUE)) + .WillOnce(::testing::Return(TRUE)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test_host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerTest, SetHostName_iface_wlan0) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip4_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip6_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return("test_wlan")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_commit_changes(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke( + [](NMRemoteConnection *connection, gboolean save_to_disk, GCancellable *cancellable, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test_host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerTest, GetPrimaryInterface_empty) +{ + string interface{}; + Core::ProxyType NetworkManagerImpl2 = Core::ProxyType::Create(); + NetworkManagerImpl2->m_ethEnabled = false; + NetworkManagerImpl2->m_wlanEnabled = false; + uint32_t result = NetworkManagerImpl2->GetPrimaryInterface(interface); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(interface, ""); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_Failed) +{ + /* wlan0 not present in the event cache -> state unknown -> failure */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_WifiEth) +{ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_ACTIVATED, "66:77:88:99:AA:BB"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_UNMANAGED, "00:11:22:33:44:55"); // disabled + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"enabled\":true,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_EQ(response, _T("{\"enabled\":false,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_WifiEthNotFound) +{ + /* Only eth0 known to the cache; querying wlan0 -> not found -> failure */ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_emptyInterface) +{ + /* Empty interface name is rejected before any cache lookup */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_unknown) +{ + Core::ProxyType NetworkManagerImpl2 = Core::ProxyType::Create(); + Exchange::INetworkManager* interface = static_cast(NetworkManagerImpl2->QueryInterface(Exchange::INetworkManager::ID)); + ASSERT_TRUE(interface != nullptr); + + bool isEnabled; + std::string ifaceName = "wlan1"; + uint32_t result = interface->GetInterfaceState(ifaceName, isEnabled); + EXPECT_EQ(result, Core::ERROR_GENERAL); + EXPECT_EQ(isEnabled, false); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces_emptyCache) +{ + /* No interfaces reported by NM yet -> empty list is a valid (successful) result */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("eth0") == std::string::npos); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces_Enabled) +{ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_UNAVAILABLE, "66:77:88:99:AA:BB"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + EXPECT_TRUE(response.find("wlan0") != std::string::npos); + EXPECT_TRUE(response.find("eth0") != std::string::npos); + + std::string expectedResponse = + _T("{\"interfaces\":[") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":true},") + _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":true,\"connected\":false}") + _T("],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces_disabled) +{ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_UNMANAGED, "00:11:22:33:44:55"); + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("wlan0", NM_DEVICE_STATE_UNMANAGED, "66:77:88:99:AA:BB"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + EXPECT_TRUE(response.find("wlan0") != std::string::npos); + EXPECT_TRUE(response.find("eth0") != std::string::npos); + + std::string expectedResponse = + _T("{\"interfaces\":[") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":false,\"connected\":false},") + _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"66:77:88:99:AA:BB\",\"enabled\":false,\"connected\":false}") + _T("],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces_onlyEthernet) +{ + /* Only eth0 reported by NM; wlan0 (absent from cache) is omitted from the list */ + Plugin::GnomeNetworkManagerEvents::updateInterfaceStateCache("eth0", NM_DEVICE_STATE_ACTIVATED, "00:11:22:33:44:55"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); + + std::string expectedResponse = + _T("{\"interfaces\":[") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"00:11:22:33:44:55\",\"enabled\":true,\"connected\":true}") + _T("],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetIPSettings_unknown_iface) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth1\"}"), response)); + EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_emptyCache) +{ + /* With no cache populated, GetIPSettings should still succeed but return no IP data */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"interface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv4\"") != std::string::npos); + /* No ipaddress key when cache is empty */ + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_interface_Empty) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"\"}"), response)); + EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_GetPrimary_failed) +{ + Core::ProxyType NetworkManagerImpl2 = Core::ProxyType::Create(); + string interface; const string ipversion; Exchange::INetworkManager::IPAddress address; + EXPECT_EQ(Core::ERROR_GENERAL, NetworkManagerImpl2->GetIPSettings(interface, ipversion, address)); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv4_fromCache) +{ + /* Populate IPv4 cache for eth0, then verify GetIPSettings returns cached data */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.globalAddresses["192.168.1.2"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + cache.gateway = "192.168.1.1"; + cache.primarydns = "8.8.8.8"; + cache.secondarydns = "8.8.4.4"; + cache.dhcpserver = "192.168.1.11"; + cache.autoconfig = true; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"interface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv4\"") != std::string::npos); + EXPECT_TRUE(response.find("\"autoconfig\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.2\"") != std::string::npos); + EXPECT_TRUE(response.find("\"prefix\":24") != std::string::npos); + EXPECT_TRUE(response.find("\"gateway\":\"192.168.1.1\"") != std::string::npos); + EXPECT_TRUE(response.find("\"primarydns\":\"8.8.8.8\"") != std::string::npos); + EXPECT_TRUE(response.find("\"secondarydns\":\"8.8.4.4\"") != std::string::npos); + EXPECT_TRUE(response.find("\"dhcpserver\":\"192.168.1.11\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv4_autoconfig) +{ + /* Populate IPv4 cache with autoconfig=true but no IP address */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + std::string expectedResponse = + _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv4_staticConfig) +{ + /* Populate IPv4 cache with autoconfig=false (static config) */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = false; + cache.globalAddresses["192.168.1.100"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + cache.gateway = "192.168.1.1"; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"autoconfig\":false") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.100\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_wlan0_fromCache) +{ + /* Populate IPv4 cache for wlan0 */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["10.0.0.5"] = Plugin::GlobalAddressInfo(8, Plugin::ADDR_GLOBAL); + cache.gateway = "10.0.0.1"; + cache.primarydns = "1.1.1.1"; + Plugin::_instance->swapIpCache("wlan0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"interface\":\"wlan0\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"10.0.0.5\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv4_config_valid) +{ + /* Populate full IPv4 cache for eth0 and verify all fields */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["192.168.1.2"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + cache.gateway = "192.168.1.0"; + cache.primarydns = "8.8.8.8"; + cache.secondarydns = "8.8.4.4"; + cache.dhcpserver = "192.168.1.11"; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"secondarydns\":\"8.8.4.4\"") != std::string::npos); + EXPECT_TRUE(response.find("\"primarydns\":\"8.8.8.8\"") != std::string::npos); + EXPECT_TRUE(response.find("\"interface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.2\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ula\":\"\"") != std::string::npos); + EXPECT_TRUE(response.find("\"dhcpserver\":\"192.168.1.11\"") != std::string::npos); + EXPECT_TRUE(response.find("\"gateway\":\"192.168.1.0\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv6_config_valid) +{ + /* Populate full IPv6 cache for eth0 and verify all fields */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["2001:db8:1:2:3:4:5:6"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + cache.uniqueLocalAddresses.insert("fd12::1234:5678:abcd:ef01"); + cache.gateway = "2001:4860:4860::1"; + cache.primarydns = "2001:4860:4860::8888"; + cache.secondarydns = "2001:4860:4860::8844"; + cache.dhcpserver = "2001:db8::1"; + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv6\"}"), response)); + + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv6\"") != std::string::npos); + EXPECT_TRUE(response.find("\"secondarydns\":\"2001:4860:4860::8844\"") != std::string::npos); + EXPECT_TRUE(response.find("\"primarydns\":\"2001:4860:4860::8888\"") != std::string::npos); + EXPECT_TRUE(response.find("\"interface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8:1:2:3:4:5:6\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ula\":\"fd12::1234:5678:abcd:ef01\"") != std::string::npos); + EXPECT_TRUE(response.find("\"prefix\":64") != std::string::npos); + EXPECT_TRUE(response.find("\"dhcpserver\":\"2001:db8::1\"") != std::string::npos); + EXPECT_TRUE(response.find("\"gateway\":\"2001:4860:4860::1\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv6_mac_based_fallback) +{ + /* When all global addresses are MAC-based, toIPAddress should use the MAC-based one */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["2001:db8::aabb:ccff:fedd:eeff"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL_MAC_BASED); + cache.gateway = "fe80::1"; + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8::aabb:ccff:fedd:eeff\"") != std::string::npos); + EXPECT_TRUE(response.find("\"prefix\":64") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv6_prefer_non_mac_global) +{ + /* ADDR_GLOBAL should be preferred over ADDR_GLOBAL_MAC_BASED */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + /* Insert MAC-based first to ensure it's not selected by insertion order */ + cache.globalAddresses["2001:db8::aabb:ccff:fedd:eeff"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL_MAC_BASED); + cache.globalAddresses["2001:db8::1234:5678"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8::1234:5678\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv6_only_cached_request_ipv4) +{ + /* Only IPv6 cached, but IPv4 requested — should return success with no IP */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["2001:db8::1"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv4\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv4\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_cache_invalidated) +{ + /* Cache exists but valid=false — should return success with no IP data */ + Plugin::IpFamilyCache cache; + cache.valid = false; + cache.globalAddresses["192.168.1.5"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipversion_case_insensitive) +{ + /* ipversion "ipv6" (lowercase) should be treated as IPv6 */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.globalAddresses["2001:db8::99"] = Plugin::GlobalAddressInfo(128, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"ipv6\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8::99\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_ipv6_ula_only) +{ + /* Cache has only ULA addresses, no global — ipaddress is empty so the + JSON-RPC layer (NetworkManagerJsonRpc.cpp) skips all address fields + including ula, gateway, etc. Only interface/ipversion/autoconfig/success + are returned. */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.autoconfig = true; + cache.uniqueLocalAddresses.insert("fd00::1234:abcd"); + cache.gateway = "fe80::1"; + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + /* No global address → ipaddress empty → JSON-RPC omits address detail fields */ + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); + EXPECT_TRUE(response.find("\"ula\"") == std::string::npos); + EXPECT_TRUE(response.find("\"gateway\"") == std::string::npos); + EXPECT_TRUE(response.find("\"autoconfig\":true") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_swapIpCache_returns_old_keys) +{ + /* Verify swapIpCache returns the old global address keys */ + Plugin::IpFamilyCache cache1; + cache1.valid = true; + cache1.globalAddresses["192.168.1.10"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + cache1.globalAddresses["192.168.1.20"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache1)); + + /* Now swap with a new cache and check old keys are returned */ + Plugin::IpFamilyCache cache2; + cache2.valid = true; + cache2.globalAddresses["10.0.0.1"] = Plugin::GlobalAddressInfo(8, Plugin::ADDR_GLOBAL); + std::set oldKeys = Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache2)); + + EXPECT_EQ(oldKeys.size(), 2u); + EXPECT_TRUE(oldKeys.count("192.168.1.10") == 1); + EXPECT_TRUE(oldKeys.count("192.168.1.20") == 1); + + /* Verify the new cache is now active */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"10.0.0.1\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_separate_ipv4_ipv6_caches) +{ + /* IPv4 and IPv6 caches for the same interface should be independent */ + Plugin::IpFamilyCache cache4; + cache4.valid = true; + cache4.autoconfig = true; + cache4.globalAddresses["192.168.1.50"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache4)); + + Plugin::IpFamilyCache cache6; + cache6.valid = true; + cache6.autoconfig = true; + cache6.globalAddresses["2001:db8::50"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + cache6.uniqueLocalAddresses.insert("fd12::50"); + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache6)); + + /* Query IPv4 */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv4\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.50\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv4\"") != std::string::npos); + + /* Query IPv6 */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\", \"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8::50\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\":\"IPv6\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ula\":\"fd12::50\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, GetIPSettings_cache_cleared) +{ + /* Populate cache, then swap with empty/invalid cache — simulates disconnect */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.globalAddresses["192.168.1.99"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + /* Verify it's there */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.99\"") != std::string::npos); + + /* Clear cache by swapping with default (valid=false) */ + Plugin::IpFamilyCache empty; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(empty)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +/* ──────────────────────────────────────────────────────────────────────────── + * Utility function tests — isIPv4LinkLocal, isIPv6LinkLocal, isIPv6ULA, + * isIPv6MacBased (which exercises parseMac internally). + * ──────────────────────────────────────────────────────────────────────────── */ + +TEST_F(NetworkManagerTest, isIPv4LinkLocal_true_for_169_254) +{ + EXPECT_TRUE(Plugin::isIPv4LinkLocal("169.254.0.1")); + EXPECT_TRUE(Plugin::isIPv4LinkLocal("169.254.255.255")); + EXPECT_TRUE(Plugin::isIPv4LinkLocal("169.254.100.50")); +} + +TEST_F(NetworkManagerTest, isIPv4LinkLocal_false_for_non_link_local) +{ + EXPECT_FALSE(Plugin::isIPv4LinkLocal("192.168.1.1")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("10.0.0.1")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("169.253.255.255")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("169.255.0.1")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("0.0.0.0")); +} + +TEST_F(NetworkManagerTest, isIPv4LinkLocal_false_for_invalid_input) +{ + EXPECT_FALSE(Plugin::isIPv4LinkLocal("")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("not_an_ip")); + EXPECT_FALSE(Plugin::isIPv4LinkLocal("fe80::1")); +} + +TEST_F(NetworkManagerTest, isIPv6LinkLocal_true_for_fe80) +{ + EXPECT_TRUE(Plugin::isIPv6LinkLocal("fe80::1")); + EXPECT_TRUE(Plugin::isIPv6LinkLocal("fe80::abcd:1234:5678:9abc")); + EXPECT_TRUE(Plugin::isIPv6LinkLocal("fe80::ffff:ffff:ffff:ffff")); +} + +TEST_F(NetworkManagerTest, isIPv6LinkLocal_false_for_non_link_local) +{ + EXPECT_FALSE(Plugin::isIPv6LinkLocal("2001:db8::1")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("fd00::1")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("::1")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("fc00::1")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("fec0::1")); +} + +TEST_F(NetworkManagerTest, isIPv6LinkLocal_false_for_invalid_input) +{ + EXPECT_FALSE(Plugin::isIPv6LinkLocal("")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("not_an_ip")); + EXPECT_FALSE(Plugin::isIPv6LinkLocal("169.254.1.1")); +} + +TEST_F(NetworkManagerTest, isIPv6ULA_true_for_fc_fd) +{ + EXPECT_TRUE(Plugin::isIPv6ULA("fc00::1")); + EXPECT_TRUE(Plugin::isIPv6ULA("fd00::1")); + EXPECT_TRUE(Plugin::isIPv6ULA("fd12:3456:789a::1")); + EXPECT_TRUE(Plugin::isIPv6ULA("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")); +} + +TEST_F(NetworkManagerTest, isIPv6ULA_false_for_non_ula) +{ + EXPECT_FALSE(Plugin::isIPv6ULA("2001:db8::1")); + EXPECT_FALSE(Plugin::isIPv6ULA("fe80::1")); + EXPECT_FALSE(Plugin::isIPv6ULA("::1")); + EXPECT_FALSE(Plugin::isIPv6ULA("fb00::1")); + EXPECT_FALSE(Plugin::isIPv6ULA("fe00::1")); +} + +TEST_F(NetworkManagerTest, isIPv6ULA_false_for_invalid_input) +{ + EXPECT_FALSE(Plugin::isIPv6ULA("")); + EXPECT_FALSE(Plugin::isIPv6ULA("garbage")); + EXPECT_FALSE(Plugin::isIPv6ULA("192.168.1.1")); +} + +TEST_F(NetworkManagerTest, isIPv6MacBased_true_for_eui64_colon_mac) +{ + /* MAC AA:BB:CC:DD:EE:FF → EUI-64: A8:BB:CC:FF:FE:DD:EE:FF + (byte 0: 0xAA ^ 0x02 = 0xA8, insert FF:FE in middle) */ + EXPECT_TRUE(Plugin::isIPv6MacBased("2001:db8::a8bb:ccff:fedd:eeff", "AA:BB:CC:DD:EE:FF")); +} + +TEST_F(NetworkManagerTest, isIPv6MacBased_true_for_eui64_plain_mac) +{ + /* Same MAC in plain hex format */ + EXPECT_TRUE(Plugin::isIPv6MacBased("2001:db8::a8bb:ccff:fedd:eeff", "aabbccddeeff")); +} + +TEST_F(NetworkManagerTest, isIPv6MacBased_false_for_non_eui64) +{ + /* Address that doesn't match the MAC's EUI-64 */ + EXPECT_FALSE(Plugin::isIPv6MacBased("2001:db8::1234:5678:9abc:def0", "AA:BB:CC:DD:EE:FF")); +} + +TEST_F(NetworkManagerTest, isIPv6MacBased_false_for_privacy_address) +{ + /* Privacy extension address — random interface ID, not MAC-based */ + EXPECT_FALSE(Plugin::isIPv6MacBased("2001:db8::4f2a:8c91:e3d7:b560", "00:11:22:33:44:55")); +} + +TEST_F(NetworkManagerTest, isIPv6MacBased_false_for_invalid_inputs) +{ + EXPECT_FALSE(Plugin::isIPv6MacBased("", "AA:BB:CC:DD:EE:FF")); + EXPECT_FALSE(Plugin::isIPv6MacBased("2001:db8::1", "")); + EXPECT_FALSE(Plugin::isIPv6MacBased("not_ipv6", "AA:BB:CC:DD:EE:FF")); + EXPECT_FALSE(Plugin::isIPv6MacBased("2001:db8::1", "not_a_mac")); + EXPECT_FALSE(Plugin::isIPv6MacBased("192.168.1.1", "AA:BB:CC:DD:EE:FF")); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_deviceFailed_wlan0) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"eth0\",\"enabled\":true}"), response)); + EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_wlan0_already_diconnected) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"wlan0\",\"enabled\":false}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_wlan0_activated) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "WiFi scanning failed: %s", "Network interface is busy or unavailable"); + return FALSE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"wlan0\",\"enabled\":false}"), response)); + EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_wlan0_deactivated_ErroCode) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CANCELLED, "WiFi scanning failed: %s", "Network interface is busy or unavailable"); + return FALSE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"wlan0\",\"enabled\":false}"), response)); + EXPECT_TRUE(response.find("\"success\":false") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_wlan0_deactivated_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"wlan0\",\"enabled\":false}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_wlan0_enable_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(static_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"wlan0\",\"enabled\":true}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_eth0_enable_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(static_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), _T("{\"interface\":\"eth0\",\"enabled\":true}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + + +TEST_F(NetworkManagerTest, SetIPSettings_interface_empty) +{ + // Test setting static IP configuration + std::string request = _T("{\"interface\":\"\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_device_NULL) +{ + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + // Test setting static IP configuration + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_Connection_NULL) +{ + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778392))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_settingsNull) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778392))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_Connection) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778392))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778394))); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_Connection_eth0) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778392))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x778394))); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_connection_get_interface_name(::testing::_)) + .WillOnce(::testing::Return("eth0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip4_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100173))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_ip_config_get_method(::testing::_)) + .WillOnce(::testing::Return("auto")); + + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_static_wlan0_activeNull) +{ + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_active_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + std::string request = _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(dummyActiveConn); +} + +TEST_F(NetworkManagerTest, SetIPSettings_static_wlan0_active_remoteNull) +{ + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_active_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyActiveConn))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_active_connection_get_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + std::string request = _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(dummyActiveConn); +} + +TEST_F(NetworkManagerTest, SetIPSettings_static_wlan0_autoConfTrue) +{ + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + NMRemoteConnection *dummyRemoteConn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_active_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyActiveConn))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_active_connection_get_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyRemoteConn))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip4_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100173))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_ip_config_get_method(::testing::_)) + .WillOnce(::testing::Return("auto")); + + std::string request = _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(dummyActiveConn); + g_object_unref(dummyRemoteConn); +} + +TEST_F(NetworkManagerTest, SetIPSettings_static_wlan0_autoConffalse) +{ + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + NMRemoteConnection *dummyRemoteConn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_active_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyActiveConn))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_active_connection_get_connection(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyRemoteConn))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_ip4_config(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_commit_changes(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke( + [](NMRemoteConnection *connection, gboolean save_to_disk, GCancellable *cancellable, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "connection delete failed"); + return false; + })); + + std::string request = _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(dummyActiveConn); + g_object_unref(dummyRemoteConn); +} diff --git a/tests/l2Test/libnm/l2_test_libnmproxyEvent.cpp b/tests/l2Test/libnm/l2_test_libnmproxyEvent.cpp new file mode 100644 index 000000000..e9eed28d1 --- /dev/null +++ b/tests/l2Test/libnm/l2_test_libnmproxyEvent.cpp @@ -0,0 +1,643 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FactoriesImplementation.h" +#include "WrapsMock.h" +#include "CurlWrapsMock.h" +#include "LibnmWrapsMock.h" +#include "GLibWrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerGnomeEvents.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" +#include + +using namespace WPEFramework; +using ::testing::NiceMock; + +namespace WPEFramework { namespace Plugin { extern NetworkManagerImplementation* _instance; } } + +class NetworkManagerEventTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + + /* every event callback */ + GCallback nmClientRunCb = NULL; + GCallback nmStateChange = NULL; + + WrapsImplMock *p_wrapsImplMock = nullptr; + CurlWrapsImplMock *p_curlWrapsImplMock = nullptr; + GLibWrapsImplMock *p_gLibWrapsImplMock = nullptr; + LibnmWrapsImplMock *p_libnmWrapsImplMock = nullptr; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + NetworkManagerEventTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + + p_libnmWrapsImplMock = new NiceMock ; + LibnmWraps::setImpl(p_libnmWrapsImplMock); + + p_gLibWrapsImplMock = new NiceMock ; + GLibWraps::setImpl(p_gLibWrapsImplMock); + + p_wrapsImplMock = new NiceMock ; + Wraps::setImpl(p_wrapsImplMock); + + p_curlWrapsImplMock = new NiceMock ; + CurlWraps::setImpl(p_curlWrapsImplMock); + ON_CALL(*p_curlWrapsImplMock, curl_multi_perform(::testing::_, ::testing::_)) + .WillByDefault(::testing::DoAll( + ::testing::SetArgPointee<1>(0), + ::testing::Return(CURLM_OK))); + ON_CALL(*p_curlWrapsImplMock, curl_multi_info_read(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(nullptr)); + ON_CALL(*p_curlWrapsImplMock, curl_multi_poll(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(CURLM_OK)); + + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + "}" + )); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_signal_connect_data(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Invoke( + [&](gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) -> gulong { + + if(detailed_signal == NULL) + printf("g_signal_connect_data: NULL\n"); + else + printf("g_signal_connect_data - signal: %s\n", detailed_signal); + return 0; + })); + + EXPECT_CALL(*p_gLibWrapsImplMock, signal_handlers_disconnect_by_data(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(0)); + + EXPECT_CALL(*p_gLibWrapsImplMock, signal_handlers_disconnect_by_func(::testing::_, ::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(0)); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + EXPECT_EQ(string(""), (response = plugin->Initialize(&service))); + sleep(1); // Allow some time for initialization + } + + virtual void SetUp() override + { + } + + virtual ~NetworkManagerEventTest() override + { + plugin->Deinitialize(&service); + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + Wraps::setImpl(nullptr); + if (p_wrapsImplMock != nullptr) + { + delete p_wrapsImplMock; + p_wrapsImplMock = nullptr; + } + + CurlWraps::setImpl(nullptr); + if (p_curlWrapsImplMock != nullptr) + { + delete p_curlWrapsImplMock; + p_curlWrapsImplMock = nullptr; + } + + LibnmWraps::setImpl(nullptr); + if (p_libnmWrapsImplMock != nullptr) + { + delete p_libnmWrapsImplMock; + p_libnmWrapsImplMock = nullptr; + } + + GLibWraps::setImpl(nullptr); + if (p_gLibWrapsImplMock != nullptr) + { + delete p_gLibWrapsImplMock; + p_gLibWrapsImplMock = nullptr; + } + + printf("~NetworkManagerEventTest\n"); + } +}; + +TEST_F(NetworkManagerEventTest, onInterfaceStateChangeCb) +{ + // Testing all interface state change events for Ethernet interface + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ADDED, "eth0"); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_UP, "eth0"); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_LINK_DOWN, "eth0"); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_ACQUIRING_IP, "eth0"); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_REMOVED, "eth0"); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onInterfaceStateChangeCb(Exchange::INetworkManager::INTERFACE_DISABLED, "eth0"); +} + +TEST_F(NetworkManagerEventTest, onAvailableSSIDsCb) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(0x833332)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + // Create a fake SSID GBytes object + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("00:11:22:33:44:55")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))); // 85% signal strength + + g_ptr_array_free(fakeDevices, TRUE); + + WPEFramework::Plugin::GnomeNetworkManagerEvents::onAvailableSSIDsCb(nullptr, nullptr, nullptr); + NMDeviceWifi *deviceWifiDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::onAvailableSSIDsCb(deviceWifiDummy, nullptr, nullptr); + + g_object_unref(deviceDummy); + g_object_unref(deviceWifiDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); +} + +TEST_F(NetworkManagerEventTest, onWIFIStateChanged) +{ + WPEFramework::Plugin::GnomeNetworkManagerEvents::onWIFIStateChanged(1); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb) +{ + NMDevice *ethDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNKNOWN)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_PREPARE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CONFIG)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("eth0")) + .WillOnce(::testing::Return("eth0")) + .WillOnce(::testing::Return("eth0")) + .WillOnce(::testing::Return("eth0")) + .WillOnce(::testing::Return("eth0")) + .WillOnce(::testing::Return("wlan0")) + .WillOnce(::testing::Return("wlan0")) + .WillOnce(::testing::Return("wlan0")) + .WillOnce(::testing::Return("wlan0")) + .WillOnce(::testing::Return("wlan0")) + .WillOnce(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SSID_NOT_FOUND)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SUPPLICANT_TIMEOUT)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT)); + + // Test with nullptr and with mock device + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(nullptr, nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(ethDummyDevice, nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(ethDummyDevice, nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(ethDummyDevice, nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(ethDummyDevice, nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(ethDummyDevice, nullptr, nullptr); + + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); + g_object_unref(ethDummyDevice); + g_object_unref(wifiDummyDevice); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_activated) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_disconnected) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_unmanaged) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_prepare) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_PREPARE)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_config) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_CONFIG)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_ip_config) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CONFIG)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_ip_check) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CHECK)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_secondaries) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_SECONDARIES)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_deactivating) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DEACTIVATING)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_failed) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_FAILED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_need_auth) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_NEED_AUTH)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_unknown) +{ + NMDevice *wifiDummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNKNOWN)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(wifiDummyDevice), nullptr, nullptr); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_eth0_unmanaged) +{ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(DummyDevice), nullptr, nullptr); + g_object_unref(DummyDevice); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_eth0_disconnected) +{ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(DummyDevice), nullptr, nullptr); + g_object_unref(DummyDevice); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_eth0_prepare) +{ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_PREPARE)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(DummyDevice), nullptr, nullptr); + g_object_unref(DummyDevice); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_eth0_ipconfig) +{ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CONFIG)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(DummyDevice), nullptr, nullptr); + g_object_unref(DummyDevice); +} + +TEST_F(NetworkManagerEventTest, deviceStateChangeCb_eth0_activated) +{ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillOnce(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(DummyDevice, nullptr, nullptr); + g_object_unref(DummyDevice); +} + +/* ──────────────────────────────────────────────────────────────────────────── + * Cache-clearing tests — verify that disconnect events clear the IP cache. + * These test observable behavior (cache state after disconnect) rather than + * internal NM API call sequences, so they remain stable across refactors. + * ──────────────────────────────────────────────────────────────────────────── */ + +TEST_F(NetworkManagerEventTest, disconnect_clears_ipv4_cache_eth0) +{ + /* Pre-populate the IPv4 cache for eth0 */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.globalAddresses["192.168.1.50"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + cache.gateway = "192.168.1.1"; + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache)); + + /* Verify cache is populated */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"192.168.1.50\"") != std::string::npos); + + /* Trigger eth0 disconnect — refreshIpFamilyCache runs with skipRead=true, + swapping an empty cache and clearing the old addresses. */ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(DummyDevice, nullptr, nullptr); + g_object_unref(DummyDevice); + + /* Cache should now be empty — GetIPSettings returns success but no address fields */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +TEST_F(NetworkManagerEventTest, disconnect_clears_ipv6_cache_wlan0) +{ + /* Pre-populate the IPv6 cache for wlan0 */ + Plugin::IpFamilyCache cache; + cache.valid = true; + cache.globalAddresses["2001:db8::1"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + cache.gateway = "fe80::1"; + Plugin::_instance->swapIpCache("wlan0", "IPv6", std::move(cache)); + + /* Verify cache is populated */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\":\"2001:db8::1\"") != std::string::npos); + + /* Trigger wlan0 disconnect */ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(reinterpret_cast(DummyDevice), nullptr, nullptr); + g_object_unref(DummyDevice); + + /* Cache should now be empty */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} + +TEST_F(NetworkManagerEventTest, disconnect_clears_both_ip_family_caches) +{ + /* Pre-populate both IPv4 and IPv6 caches for eth0 */ + Plugin::IpFamilyCache cache4; + cache4.valid = true; + cache4.globalAddresses["192.168.1.50"] = Plugin::GlobalAddressInfo(24, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv4", std::move(cache4)); + + Plugin::IpFamilyCache cache6; + cache6.valid = true; + cache6.globalAddresses["2001:db8::99"] = Plugin::GlobalAddressInfo(64, Plugin::ADDR_GLOBAL); + Plugin::_instance->swapIpCache("eth0", "IPv6", std::move(cache6)); + + /* Trigger disconnect — refreshIpFamilyCache is called for BOTH families */ + NMDevice *DummyDevice = static_cast(g_object_new(NM_TYPE_DEVICE_ETHERNET, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("eth0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state_reason(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_REASON_NONE)); + WPEFramework::Plugin::GnomeNetworkManagerEvents::deviceStateChangeCb(DummyDevice, nullptr, nullptr); + g_object_unref(DummyDevice); + + /* Both families should be cleared */ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv6\"}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\"") == std::string::npos); +} diff --git a/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp b/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp new file mode 100644 index 000000000..52d2a1399 --- /dev/null +++ b/tests/l2Test/libnm/l2_test_libnmproxyInit.cpp @@ -0,0 +1,179 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include + +#include "FactoriesImplementation.h" +#include "LibnmWrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" +#include "NetworkManagerGnomeEvents.h" +#include + +using namespace WPEFramework; +using ::testing::NiceMock; + +class NetworkManagerInitTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + + LibnmWrapsImplMock *p_libnmWrapsImplMock = nullptr; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + NetworkManagerInitTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + + p_libnmWrapsImplMock = new NiceMock ; + LibnmWraps::setImpl(p_libnmWrapsImplMock); + + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + " \"autostart\":false," + " \"configuration\":{" + " \"root\":{" + " \"outofprocess\":true," + " \"locator\":\"libWPEFrameworkNetworkManagerImpl.so\"" + " }," + " \"connectivity\":{" + " \"endpoint_1\":\"http://clients3.google.com/generate_204\"," + " \"interval\":60" + " }," + " \"stun\":{" + " \"endpoint\":\"stun.l.google.com\"," + " \"port\":19302," + " \"interval\":30" + " }," + " \"loglevel\":4" + " }" + "}" + )); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_new(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(nullptr)); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + response = plugin->Initialize(&service); + EXPECT_EQ(string(""), response); + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); // Set log level to DEBUG_LEVEL + } + + virtual void SetUp() override + { + /* Interface-state cache is a process-global static shared across suites; + clear the known interfaces so platform_init failure yields an empty cache. */ + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("eth0"); + Plugin::GnomeNetworkManagerEvents::removeInterfaceStateCache("wlan0"); + } + + virtual ~NetworkManagerInitTest() override + { + plugin->Deinitialize(&service); + + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + LibnmWraps::setImpl(nullptr); + if (p_libnmWrapsImplMock != nullptr) + { + delete p_libnmWrapsImplMock; + p_libnmWrapsImplMock = nullptr; + } + } +}; + +TEST_F(NetworkManagerInitTest, platformInit) +{ + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + /* platform_init failed (no NMClient) -> event cache never populated -> empty list, still success */ + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("eth0") == std::string::npos); + EXPECT_TRUE(response.find("wlan0") == std::string::npos); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPrimaryInterface"), _T(""), response)); + EXPECT_EQ(response, _T("{\"interface\":\"\",\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetHostname"), _T("{\"hostname\":\"test-host\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":4,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetSupportedSecurityModes"), _T(""), response)); + EXPECT_EQ(response, _T("{\"security\":{\"NONE\":0,\"WPA_PSK\":1,\"SAE\":2,\"EAP\":3},\"success\":true}")); +} \ No newline at end of file diff --git a/tests/l2Test/libnm/l2_test_libnmproxyWifi.cpp b/tests/l2Test/libnm/l2_test_libnmproxyWifi.cpp new file mode 100644 index 000000000..c0086a003 --- /dev/null +++ b/tests/l2Test/libnm/l2_test_libnmproxyWifi.cpp @@ -0,0 +1,2357 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "FactoriesImplementation.h" +#include "WrapsMock.h" +#include "LibnmWrapsMock.h" +#include "GLibWrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" +#include + +using namespace WPEFramework; +using ::testing::NiceMock; + +class NetworkManagerWifiTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + NMClient* gNmClient = NULL; + + WrapsImplMock *p_wrapsImplMock = nullptr; + LibnmWrapsImplMock *p_libnmWrapsImplMock = nullptr; + GLibWrapsImplMock *p_gLibWrapsImplMock = nullptr; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + NetworkManagerWifiTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + + p_libnmWrapsImplMock = new NiceMock ; + LibnmWraps::setImpl(p_libnmWrapsImplMock); + + p_gLibWrapsImplMock = new NiceMock ; + GLibWraps::setImpl(p_gLibWrapsImplMock); + + p_wrapsImplMock = new NiceMock ; + Wraps::setImpl(p_wrapsImplMock); + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + " \"autostart\":false," + " \"configuration\":{" + " \"root\":{" + " \"outofprocess\":true," + " \"locator\":\"libWPEFrameworkNetworkManagerImpl.so\"" + " }," + " \"connectivity\":{" + " \"endpoint_1\":\"http://clients3.google.com/generate_204\"," + " \"interval\":60" + " }," + " \"stun\":{" + " \"endpoint\":\"stun.l.google.com\"," + " \"port\":19302," + " \"interval\":30" + " }," + " \"loglevel\":4" + " }" + "}" + )); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x100178))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + response = plugin->Initialize(&service); + EXPECT_EQ(string(""), response); + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); + usleep(1000); // wait for the plugin to initialize + } + + virtual void SetUp() override + { + struct stat buffer; + if (stat("/etc/device.properties", &buffer) != 0) { + std::ofstream file("/etc/device.properties"); + if (file.is_open()) { + file << "ETHERNET_INTERFACE=eth0\nWIFI_INTERFACE=wlan0\n"; + file.close(); + } + else { + std::cerr << "Failed to create /etc/device.properties file." << std::endl; + } + } + } + + virtual ~NetworkManagerWifiTest() override + { + plugin->Deinitialize(&service); + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + Wraps::setImpl(nullptr); + if (p_wrapsImplMock != nullptr) + { + delete p_wrapsImplMock; + p_wrapsImplMock = nullptr; + } + + LibnmWraps::setImpl(nullptr); + if (p_libnmWrapsImplMock != nullptr) + { + delete p_libnmWrapsImplMock; + p_libnmWrapsImplMock = nullptr; + } + + GLibWraps::setImpl(nullptr); + if (p_gLibWrapsImplMock != nullptr) + { + delete p_gLibWrapsImplMock; + p_gLibWrapsImplMock = nullptr; + } + } +}; + +TEST_F(NetworkManagerWifiTest, RegisteredMethods) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetAvailableInterfaces"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetPrimaryInterface"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("AddToKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("RemoveKnownSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("ConnectToKnownSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiConnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiDisconnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetConnectedSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWifiState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWiFiSignalQuality"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetSupportedSecurityModes"))); +} + +TEST_F(NetworkManagerWifiTest, get_wifi_device_devices_null) +{ + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerWifiTest, WiFiAlreadyDisconnected) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiAlreadyConnected_failed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GAsyncResult *result, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CANCELLED, "WiFi scanning failed: %s", "Network interface is busy or unavailable"); + return FALSE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiAlreadyConnected_failed_errorCode) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("NULL"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GAsyncResult *result, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CLOSED, "WiFi scanning failed: %s", "Network interface is busy or unavailable"); + return FALSE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiAlreadyConnected_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_deviceError) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_ApNotConnected) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"\",\"bssid\":\"\",\"security\":0,\"strength\":0,\"frequency\":0,\"rate\":0,\"noise\":0,\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_ApNotNull) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CHECK)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_Connected_psk) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CHECK)); + + // Create a fake SSID GBytes object + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + + // Setup the Access Point mock calls + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x833332))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))); // 54 Mbps + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))); // 85% signal strength + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + + // Update expected response to match the mock data + EXPECT_EQ(response, _T("{\"ssid\":\"TestWiFiNetwork\",\"bssid\":\"AA:BB:CC:DD:EE:FF\",\"security\":2,\"strength\":-39,\"frequency\":2.462,\"rate\":54000,\"noise\":0,\"success\":true}")); + + // Free the GBytes object + g_bytes_unref(fake_ssid); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_Connected_EAP) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CHECK)); + + // Create a fake SSID GBytes object + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + + // Setup the Access Point mock calls + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x833332))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_802_1X))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_802_1X))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))); // 54 Mbps + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))); // 85% signal strength + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + + // Update expected response to match the mock data + EXPECT_EQ(response, _T("{\"ssid\":\"TestWiFiNetwork\",\"bssid\":\"AA:BB:CC:DD:EE:FF\",\"security\":3,\"strength\":-39,\"frequency\":2.462,\"rate\":54000,\"noise\":0,\"success\":true}")); + + // Free the GBytes object + g_bytes_unref(fake_ssid); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetConnectedSSID_Connected_ssidNull) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CHECK)); + + // Create a fake SSID GBytes object + const char* ssid_str = ""; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, 0); + + // Setup the Access Point mock calls + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x833332))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_NONE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_NONE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))); // 54 Mbps + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(20))); // 20% signal strength + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T(""), response)); + + // Update expected response to match the mock data + EXPECT_EQ(response, _T("{\"ssid\":\"\",\"bssid\":\"AA:BB:CC:DD:EE:FF\",\"security\":1,\"strength\":-78,\"frequency\":2.462,\"rate\":54000,\"noise\":0,\"success\":true}")); + + // Free the GBytes object + g_bytes_unref(fake_ssid); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_Disconnect) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_DISCONNECTED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":2,\"status\":\"WIFI_STATE_DISCONNECTED\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_Connected) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":5,\"status\":\"WIFI_STATE_CONNECTED\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_Pairing) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_PREPARE)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_CONFIG)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":3,\"status\":\"WIFI_STATE_PAIRING\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_unknown) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_NEED_AUTH)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNKNOWN)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":1,\"status\":\"WIFI_STATE_DISABLED\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_Connecting) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_NEED_AUTH)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_IP_CONFIG)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":4,\"status\":\"WIFI_STATE_CONNECTING\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetWifiState_Disabled) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T(""), response)); + EXPECT_EQ(response, _T("{\"state\":1,\"status\":\"WIFI_STATE_DISABLED\",\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_Failed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_with_SSIDList) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T("{\"ssid\":[\"Testssid_1\", \"Testssid_2\"]}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_with_Frequency) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T("{\"frequencies\":[\"2.4\",\"5\"], \"ssids\":[\"Testssid_1\", \"Testssid_2\"]}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_with_failed_with_error) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GAsyncResult *result, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "WiFi scanning failed: %s", "Network interface is busy or unavailable"); + return FALSE; + })); + + // Execute the test + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + // Cleanup + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); // Device must be active for scanning + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GAsyncResult *result, GError **error) -> gboolean { + return true; + })); + + // Execute the test + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + // Cleanup + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StartWiFiScan_with_option_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); // Device must be active for scanning + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillOnce(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_options_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(device); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + } + )); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_request_scan_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMDeviceWifi *device, GAsyncResult *result, GError **error) -> gboolean { + return true; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), _T("{\"ssids\":[\"Testssid_1\"]}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_long_ssid) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA32\",\"passphrase\": \"123454321\",\"security\":1, \"persist\":false }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_Wrong_bssid) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestSSID\", \"bssid\":\"00:11:22:33:44:OO\", \"passphrase\": \"123454321\",\"security\":1, \"persist\":false }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed_device_null) +{ + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(static_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed_noconnection) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed_noconnection_null) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + // g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed_noconnection_id) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + // g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed_noconnection_type_null) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return("TestConnection")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + // g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_failed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return("TestConnection")) + .WillOnce(::testing::Return("TestConnection")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_connection_type(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("802-11-wireless"))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GError **error) -> NMActiveConnection* { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "connection activation failed %s", "Network interface is busy or unavailable"); + return reinterpret_cast(NULL); + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient* client, NMConnection* connection, NMDevice* device, const char* specific_object, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_object_unref(conn1); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_activateKnownConnection_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_device_by_iface(::testing::_,::testing::_)) + .WillRepeatedly(::testing::Return(deviceDummy)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_set_autoconnect(::testing::_,::testing::_)) + .WillOnce(::testing::Return()); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return("TestConnection")) + .WillOnce(::testing::Return("TestConnection")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_connection_type(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("802-11-wireless"))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(dummyActiveConn)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient* client, NMConnection* connection, NMDevice* device, const char* specific_object, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_object_unref(conn1); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, Connect_To_Known_SSID) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + NMConnection *conn2 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + NMConnection *conn3 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + g_ptr_array_add(dummyConns, conn2); + g_ptr_array_add(dummyConns, conn3); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)) + .WillOnce(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))) + .WillOnce(::testing::Return("TestEthConnection")) + .WillOnce(::testing::Return("TestConnection")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_connection_type(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("802-11-wired"))) + .WillOnce(::testing::Return(reinterpret_cast("802-11-wireless"))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + NMActiveConnection *dummyActiveConn = static_cast(g_object_new(NM_TYPE_ACTIVE_CONNECTION, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Return(dummyActiveConn)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_activate_connection_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient* client, NMConnection* connection, NMDevice* device, const char* specific_object, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("ConnectToKnownSSID"), _T("{ \"ssid\":\"TestConnection\" }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("ConnectToKnownSSID"), _T("{ \"ssid\":\"TestConnection\" }"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_object_unref(conn1); + g_object_unref(conn2); + g_object_unref(conn3); + g_object_unref(dummyActiveConn); + g_ptr_array_free(dummyConns, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_same_ssid) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + // Setup the Access Point mock calls + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))); // 54 Mbps + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))); // 85% signal strength + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork\",\"passphrase\": \"123454321\",\"security\":2, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_bytes_unref(fake_ssid); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_Ap_list_null) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + // Setup the Access Point mock calls + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))); // Channel 11 (2.4GHz) + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))); // 54 Mbps + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))); // 85% signal strength + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_0\",\"passphrase\": \"123454321\",\"security\":1, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid); + g_object_unref(deviceDummy); + //g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_no_access_point) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + const char* ssid_str = "TestWiFiNetwork_1"; + GBytes* fake_ssid1 = g_bytes_new_static(ssid_str, strlen(ssid_str)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillRepeatedly(::testing::Return(fake_ssid1)); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + // // TODO MOck addToKnownSSIDs call + + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_2\",\"passphrase\": \"123454321\",\"security\":1, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid1); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_different_ssid) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + NMAccessPoint *dummyAp = static_cast(g_object_new(NM_TYPE_ACCESS_POINT, NULL)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:F2")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))) // Channel 11 (2.4GHz) + .WillOnce(::testing::Return(static_cast(2462))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))) // 54 Mbps + .WillOnce(::testing::Return(static_cast(54000))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))) // 85% signal strength + .WillOnce(::testing::Return(static_cast(85))); + + const char* ssid_str = "TestWiFiNetwork_1"; + GBytes* fake_ssid1 = g_bytes_new_static(ssid_str, strlen(ssid_str)); + const char* ssid_str2 = "TestWiFiNetwork_2"; + GBytes* fake_ssid2 = g_bytes_new_static(ssid_str2, strlen(ssid_str2)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid1)) + .WillOnce(::testing::Return(fake_ssid2)) + .WillOnce(::testing::Return(fake_ssid2)); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(dummyAp)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("NULL"))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_2\",\"passphrase\": \"123454\",\"security\":1, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid1); + g_bytes_unref(fake_ssid2); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_different_good_password) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + NMAccessPoint *dummyAp = static_cast(g_object_new(NM_TYPE_ACCESS_POINT, NULL)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:F2")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))) // Channel 11 (2.4GHz) + .WillOnce(::testing::Return(static_cast(2462))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))) // 54 Mbps + .WillOnce(::testing::Return(static_cast(54000))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))) // 85% signal strength + .WillOnce(::testing::Return(static_cast(85))); + + const char* ssid_str = "TestWiFiNetwork_1"; + GBytes* fake_ssid1 = g_bytes_new_static(ssid_str, strlen(ssid_str)); + const char* ssid_str2 = "TestWiFiNetwork_2"; + GBytes* fake_ssid2 = g_bytes_new_static(ssid_str2, strlen(ssid_str2)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid1)) + .WillOnce(::testing::Return(fake_ssid2)) + .WillOnce(::testing::Return(fake_ssid2)); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(dummyAp)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("NULL"))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_2\",\"passphrase\": \"123454\",\"security\":1, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid1); + g_bytes_unref(fake_ssid2); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_connection_update) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + NMAccessPoint *dummyAp = static_cast(g_object_new(NM_TYPE_ACCESS_POINT, NULL)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:F2")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))) // Channel 11 (2.4GHz) + .WillOnce(::testing::Return(static_cast(2462))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))) // 54 Mbps + .WillOnce(::testing::Return(static_cast(54000))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))) // 85% signal strength + .WillOnce(::testing::Return(static_cast(85))); + + const char* ssid_str = "TestWiFiNetwork_1"; + GBytes* fake_ssid1 = g_bytes_new_static(ssid_str, strlen(ssid_str)); + const char* ssid_str2 = "TestWiFiNetwork_2"; + GBytes* fake_ssid2 = g_bytes_new_static(ssid_str2, strlen(ssid_str2)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid1)) + .WillOnce(::testing::Return(fake_ssid2)) + .WillOnce(::testing::Return(fake_ssid2)); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(dummyAp)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + GVariant* fakeConnSettings = g_variant_new_boolean(TRUE); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_to_dbus(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(fakeConnSettings)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("NULL"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GAsyncResult *result, GError **error) -> GVariant* { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CANCELLED, "update connection activation failed"); + return NULL; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(connection); + callback(source_object, nullptr, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("TestWiFiNetwork_2"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_connection_valid(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(TRUE)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_2\",\"passphrase\": \"123499054\",\"security\":1, \"persist\":true}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid1); + g_bytes_unref(fake_ssid2); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, WiFiConnect_with_connection_not_valid) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + NMAccessPoint *dummyAp = static_cast(g_object_new(NM_TYPE_ACCESS_POINT, NULL)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_active_access_point(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(0x333223))); + + // AP property mocks + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_FLAGS_PRIVACY))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_wpa_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_rsn_flags(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_SAE))) + .WillOnce(::testing::Return(static_cast(NM_802_11_AP_SEC_KEY_MGMT_PSK))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_bssid(::testing::_)) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:FF")) + .WillOnce(::testing::Return("AA:BB:CC:DD:EE:F2")); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_frequency(::testing::_)) + .WillOnce(::testing::Return(static_cast(2462))) // Channel 11 (2.4GHz) + .WillOnce(::testing::Return(static_cast(2462))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_mode(::testing::_)) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))) + .WillOnce(::testing::Return(static_cast(NM_802_11_MODE_INFRA))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_max_bitrate(::testing::_)) + .WillOnce(::testing::Return(static_cast(54000))) // 54 Mbps + .WillOnce(::testing::Return(static_cast(54000))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_strength(::testing::_)) + .WillOnce(::testing::Return(static_cast(85))) // 85% signal strength + .WillOnce(::testing::Return(static_cast(85))); + + const char* ssid_str = "TestWiFiNetwork_1"; + GBytes* fake_ssid1 = g_bytes_new_static(ssid_str, strlen(ssid_str)); + const char* ssid_str2 = "TestWiFiNetwork_2"; + GBytes* fake_ssid2 = g_bytes_new_static(ssid_str2, strlen(ssid_str2)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid1)) + .WillOnce(::testing::Return(fake_ssid2)) + .WillOnce(::testing::Return(fake_ssid2)); + + GPtrArray* fakeAccessPoints = g_ptr_array_new(); + g_ptr_array_add(fakeAccessPoints, reinterpret_cast(dummyAp)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_wifi_get_access_points(::testing::_)) + .WillOnce(::testing::Return(fakeAccessPoints)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2_finish(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) -> NMRemoteConnection* { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "add connection activation failed %s", "Network interface is busy or unavailable"); + return reinterpret_cast(NULL); + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_delete(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GCancellable *cancellable, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "connection delete failed"); + return FALSE; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_object_get_path(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("NULL"))); + + GVariant* fakeConnSettings = g_variant_new_boolean(TRUE); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_to_dbus(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(fakeConnSettings)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("TestWiFiNetwork_2"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_access_point_connection_valid(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(false)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), _T("{ \"ssid\":\"TestWiFiNetwork_2\",\"passphrase\": \"123499054\",\"security\":1, \"persist\":false}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_bytes_unref(fake_ssid1); + g_bytes_unref(fake_ssid2); + g_object_unref(deviceDummy); + g_ptr_array_free(fakeAccessPoints, TRUE); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_deviceFailed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_UNMANAGED)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), _T("{ \"ssid\":\"test\",\"passphrase\": \"123454321\",\"security\":1, \"persist\":false }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_SmallPssword) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), _T("{ \"ssid\":\"test\",\"passphrase\": \"12345\",\"security\":1, \"persist\":false }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_sae_failed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2_finish(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) -> NMRemoteConnection* { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "add connection activation failed %s", "Network interface is busy or unavailable"); + return reinterpret_cast(NULL); + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), _T("{ \"ssid\":\"test\",\"passphrase\": \"5454543343\",\"security\":2, \"persist\":false }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_sae_update_success) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("test"))); + + GVariant* fakeConnSettings = g_variant_new_boolean(TRUE); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_to_dbus(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(fakeConnSettings)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GAsyncResult *result, GError **error) -> GVariant* { + GVariant* newVariant = g_variant_new_string("UpdatedVariant"); + return newVariant; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(connection); + callback(source_object, nullptr, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{" + " \"ssid\":\"test\"," + "\"passphrase\": \"5454543343\"," + "\"security\":2," + " \"persist\":false" + " }"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); + g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_sae_update_failed) +{ + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn = static_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn); + + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("test"))); + + GVariant* fakeConnSettings = g_variant_new_boolean(TRUE); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_to_dbus(::testing::_, ::testing::_)) + .WillOnce(::testing::Return(fakeConnSettings)); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2_finish(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GAsyncResult *result, GError **error) -> GVariant* { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_CANCELLED, "update connection activation failed"); + return NULL; + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_update2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(connection); + callback(source_object, nullptr, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{" + " \"ssid\":\"test\"," + "\"passphrase\": \"5454543343\"," + "\"security\":2," + " \"persist\":false" + " }"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); + g_object_unref(conn); + g_ptr_array_free(dummyConns, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_sae_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2_finish(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) -> NMRemoteConnection* { + return reinterpret_cast(0x22222); + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{" + " \"ssid\":\"test\"," + "\"passphrase\": \"5454543343\"," + "\"security\":2," + " \"persist\":false" + " }"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, AddToKnownSSIDs_eap_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_available_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_CALL(*p_gLibWrapsImplMock, g_main_loop_is_running(::testing::_)) + .WillRepeatedly(::testing::Return(true)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2_finish(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) -> NMRemoteConnection* { + return reinterpret_cast(0x22222); + })); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_add_connection2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + if (callback) { + GObject* source_object = G_OBJECT(client); + GAsyncResult* result = nullptr; + callback(source_object, result, user_data); + } + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{" + " \"ssid\":\"TestNetwork\"," + "\"passphrase\":\"TestPassword\"," + "\"security\":3," + "\"ca_cert\":\"test_ca_cert\"," + "\"client_cert\":\"test_client_cert\"," + "\"private_key\":\"test_private_key\"," + "\"private_key_passwd\":\"test_private_key_passwd\"," + "\"eap\":\"test_eap\"," + "\"eap_identity\":\"test_eap_identity\"," + "\"eap_password\":\"test_eap_password\"," + "\"eap_phase1\":\"test_eap_phase1\"," + "\"eap_phase2\":\"test_eap_phase2\"," + "\"persist\":true" + " }"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetKnownSSIDs_connectionFailed) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillRepeatedly(::testing::Return(nullptr)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetKnownSSIDs"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, GetKnownSSIDs_success) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + const char* ssid_str = "TestWiFiNetwork"; + GBytes* fake_ssid = g_bytes_new_static(ssid_str, strlen(ssid_str)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_setting_wireless_get_ssid(::testing::_)) + .WillOnce(::testing::Return(fake_ssid)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetKnownSSIDs"), _T(""), response)); + EXPECT_EQ(response, _T("{\"ssids\":[\"TestWiFiNetwork\"],\"success\":true}")); + + g_object_unref(deviceDummy); + g_object_unref(settingWireless); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillRepeatedly(::testing::Return(nullptr)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs_success_false) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("Test_wifi"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_delete(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GCancellable *cancellable, GError **error) -> gboolean { + *error = g_error_new(G_IO_ERROR, G_IO_ERROR_FAILED, "connection delete failed"); + return FALSE; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs_success_true) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("Test_wifi"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_delete(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GCancellable *cancellable, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs_conn_id_null) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(NULL))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs_success_ssid_specified_not_found) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("Test_wifi"))); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), + _T("{\"ssid\":\"TestNetwork\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, RemoveKnownSSIDs_success_ssid_specified_found) +{ + GPtrArray* fakeDevices = g_ptr_array_new(); + NMDevice *deviceDummy = static_cast(g_object_new(NM_TYPE_DEVICE_WIFI, NULL)); + g_ptr_array_add(fakeDevices, deviceDummy); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_devices(::testing::_)) + .WillRepeatedly(::testing::Return(fakeDevices)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_iface(::testing::_)) + .WillRepeatedly(::testing::Return("wlan0")); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_device_get_state(::testing::_)) + .WillRepeatedly(::testing::Return(NM_DEVICE_STATE_ACTIVATED)); + + GPtrArray* dummyConns = g_ptr_array_new(); + NMConnection *conn1 = reinterpret_cast(g_object_new(NM_TYPE_REMOTE_CONNECTION, NULL)); + g_ptr_array_add(dummyConns, conn1); + NMSettingWireless* settingWireless = reinterpret_cast(g_object_new(NM_TYPE_SETTING_WIRELESS, NULL)); + EXPECT_CALL(*p_libnmWrapsImplMock, nm_client_get_connections(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(dummyConns))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_setting_wireless(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast(settingWireless))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_connection_get_id(::testing::_)) + .WillOnce(::testing::Return(reinterpret_cast("TestNetwork"))); + + EXPECT_CALL(*p_libnmWrapsImplMock, nm_remote_connection_delete(::testing::_, ::testing::_, ::testing::_)) + .WillOnce(::testing::Invoke([](NMRemoteConnection *connection, GCancellable *cancellable, GError **error) -> gboolean { + return TRUE; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), + _T("{\"ssid\":\"TestNetwork\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + g_object_unref(deviceDummy); + g_ptr_array_free(fakeDevices, TRUE); +} + +TEST_F(NetworkManagerWifiTest, StopWPS) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StopWPS"), + _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerWifiTest, StartWPS) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), + _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), + _T(""), response)); +} diff --git a/tests/l2Test/rdk/CMakeLists.txt b/tests/l2Test/rdk/CMakeLists.txt new file mode 100644 index 000000000..8e8939608 --- /dev/null +++ b/tests/l2Test/rdk/CMakeLists.txt @@ -0,0 +1,108 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message ("building rdk proxy l2 test") +set(NM_RDK_PROXY_L2_TEST "rdkproxy_l2_test") + +find_package(CURL) +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) +find_package(${NAMESPACE}Plugins REQUIRED) + +add_executable(${NM_RDK_PROXY_L2_TEST} + ${CMAKE_SOURCE_DIR}/tests/l2Test/rdk/l2_test_rdkproxy.cpp + ${CMAKE_SOURCE_DIR}/tests/l2Test/rdk/l2_test_rdkproxyEvent.cpp + ${CMAKE_SOURCE_DIR}/tests/l2Test/rdk/l2_test_rdkproxyImpl.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/thunder/Module.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/Iarm.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/Wraps.cpp + ${CMAKE_SOURCE_DIR}/tests/mocks/CurlWraps.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManager.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerJsonRpc.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerImplementation.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerConnectivity.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerStunClient.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerPowerClient.cpp + ${CMAKE_SOURCE_DIR}/plugin/rdk/NetworkManagerRDKProxy.cpp + ${PROXY_STUB_SOURCES} +) + +if(USE_CONNECTIVITYCHECKMGR) + target_sources(${NM_RDK_PROXY_L2_TEST} PRIVATE + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerConnectivityClient.cpp + ) +endif() + +set_target_properties(${NM_RDK_PROXY_L2_TEST} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES +) + +target_compile_options(${NM_RDK_PROXY_L2_TEST} PRIVATE -Wall -include ${CMAKE_SOURCE_DIR}/interface/INetworkManager.h) + +include_directories(${PROJECT_SOURCE_DIR}/interface) +include_directories(${PROJECT_SOURCE_DIR}/plugin/rdk) +include_directories(${PROJECT_SOURCE_DIR}/plugin) +include_directories(${PROJECT_SOURCE_DIR}/legacy) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks) +include_directories(${PROJECT_SOURCE_DIR}/tests/mocks/thunder) +include_directories(${PROJECT_SOURCE_DIR}/../install/usr/include/rdk/iarmbus/) +include_directories(${PROJECT_SOURCE_DIR}/tools/upnp/) + +target_include_directories(${NM_RDK_PROXY_L2_TEST} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${gtest_SOURCE_DIR}/include + ${gtest_SOURCE_DIR}/../googlemock/include +) + + +target_link_options(${NM_RDK_PROXY_L2_TEST} PRIVATE + -g + -Wall + -Wl,-wrap,system + -Wl,-wrap,popen + -Wl,-wrap,syslog + -Wl,-wrap,pclose + -Wl,-wrap,getmntent + -Wl,-wrap,setmntent + -Wl,-wrap,v_secure_popen + -Wl,-wrap,v_secure_pclose + -Wl,-wrap,v_secure_system + -Wl,-wrap,curl_multi_perform + -Wl,-wrap,curl_multi_info_read + -Wl,-wrap,curl_multi_poll +) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") + +target_link_libraries(${NM_RDK_PROXY_L2_TEST} PRIVATE + gmock_main + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${CURL_LIBRARIES} +) + +if(USE_CONNECTIVITYCHECKMGR) + target_link_libraries(${NM_RDK_PROXY_L2_TEST} PRIVATE ${RFCAPI_LIBRARY}) + if(RFCAPI_INCLUDE_DIR) + target_include_directories(${NM_RDK_PROXY_L2_TEST} PRIVATE ${RFCAPI_INCLUDE_DIR}) + endif() +endif() + +install(TARGETS ${NM_RDK_PROXY_L2_TEST} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) diff --git a/tests/l2Test/rdk/l2_test_rdkproxy.cpp b/tests/l2Test/rdk/l2_test_rdkproxy.cpp new file mode 100644 index 000000000..0d13caceb --- /dev/null +++ b/tests/l2Test/rdk/l2_test_rdkproxy.cpp @@ -0,0 +1,1628 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include + +#include "FactoriesImplementation.h" +#include "IarmBusMock.h" +#include "WrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "IarmBusMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerRDKProxy.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" + +using namespace WPEFramework; +using ::testing::NiceMock; + +class NetworkManagerTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + + // Mock classes + IarmBusImplMock *p_iarmBusImplMock = nullptr; + WrapsImplMock *p_wrapsImplMock = nullptr; + IARM_EventHandler_t _nmEventHandler{}; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + NetworkManagerTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + p_iarmBusImplMock = new NiceMock ; + IarmBus::setImpl(p_iarmBusImplMock); + p_wrapsImplMock = new NiceMock ; + Wraps::setImpl(p_wrapsImplMock); + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + " \"autostart\":false," + " \"configuration\":{" + " \"root\":{" + " \"outofprocess\":true," + " \"locator\":\"libWPEFrameworkNetworkManagerImpl.so\"" + " }," + " \"connectivity\":{" + " \"endpoint_1\":\"http://clients3.google.com/generate_204\"," + " \"interval\":60" + " }," + " \"stun\":{" + " \"endpoint\":\"stun.l.google.com\"," + " \"port\":19302," + " \"interval\":30" + " }," + " \"loglevel\":3" + " }" + "}" + )); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + ON_CALL(*p_iarmBusImplMock, IARM_Bus_Init(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME))) + .WillByDefault(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + ON_CALL(*p_iarmBusImplMock, IARM_Bus_RegisterEventHandler(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) { + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onError)) { + _nmEventHandler = handler; + } + return IARM_RESULT_SUCCESS; + })); + ON_CALL(*p_iarmBusImplMock, IARM_Bus_Init(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME))) + .WillByDefault(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + response = plugin->Initialize(&service); + EXPECT_EQ(string(""), response); + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::INFO_LEVEL)); // Set log level to DEBUG_LEVEL + } + + virtual void SetUp() override + { + // remove any previous connectivity endpoints file + } + + virtual ~NetworkManagerTest() override + { + plugin->Deinitialize(&service); + + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + IarmBus::setImpl(nullptr); + if (p_iarmBusImplMock != nullptr) + { + delete p_iarmBusImplMock; + p_iarmBusImplMock = nullptr; + } + + Wraps::setImpl(nullptr); + if (p_wrapsImplMock != nullptr) + { + delete p_wrapsImplMock; + p_wrapsImplMock = nullptr; + } + } +}; + +TEST_F(NetworkManagerTest, RegisteredMethods) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetLogLevel"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetAvailableInterfaces"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetPrimaryInterface"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetInterfaceState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetIPSettings"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetStunEndpoint"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetStunEndpoint"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetConnectivityTestEndpoints"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("SetConnectivityTestEndpoints"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("IsConnectedToInternet"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetCaptivePortalURI"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetPublicIP"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("Ping"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("Trace"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWiFiScan"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("AddToKnownSSIDs"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("RemoveKnownSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiConnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("WiFiDisconnect"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetConnectedSSID"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StartWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("StopWPS"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWifiState"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetWiFiSignalQuality"))); + EXPECT_EQ(Core::ERROR_NONE, handler.Exists(_T("GetSupportedSecurityModes"))); +} + +TEST_F(NetworkManagerTest, LogLevel) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"level\":1}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":1,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"level\":2}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":2,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"level\":3}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":3,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":3,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"level\":4}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetLogLevel"), _T(""), response)); + EXPECT_EQ(response, _T("{\"level\":4,\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetLogLevel"), _T("{\"levelInvalid\":4}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces) +{ + IARM_BUS_NetSrvMgr_InterfaceList_t mockList = {}; + mockList.size = 2; + // eth0 + strcpy(mockList.interfaces[0].name, "eth0"); + strcpy(mockList.interfaces[0].mac, "AA:AA:AA:AA:AA:AA"); + mockList.interfaces[0].flags = IFF_UP | IFF_RUNNING; // enabled and connected + // wlan0 + strcpy(mockList.interfaces[1].name, "wlan0"); + strcpy(mockList.interfaces[1].mac, "BB:BB:BB:BB:BB:BB"); + mockList.interfaces[1].flags = IFF_UP; // enabled, not connected + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_getInterfaceList), + ::testing::NotNull(), sizeof(mockList))) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockList](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockList, sizeof(mockList)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T(""), response)); + std::string expectedResponse = + _T("{\"interfaces\":[") + _T("{\"type\":\"ETHERNET\",\"name\":\"eth0\",\"mac\":\"AA:AA:AA:AA:AA:AA\",\"enabled\":true,\"connected\":true},") + _T("{\"type\":\"WIFI\",\"name\":\"wlan0\",\"mac\":\"BB:BB:BB:BB:BB:BB\",\"enabled\":true,\"connected\":false}") + _T("],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetAvailableInterfaces_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_getInterfaceList), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetAvailableInterfaces"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetPrimaryInterface_BothInterfacesDown) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPrimaryInterface"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"interface\":\"\",\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetInterfaceState) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setInterfaceEnabled), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_BUS_NetSrvMgr_Iface_EventData_t* ifaceData = + static_cast(arg); + // Verify the parameters + EXPECT_TRUE(ifaceData->isInterfaceEnabled); + EXPECT_TRUE(ifaceData->persist); + EXPECT_STREQ(ifaceData->setInterface, "WIFI"); // RDKProxy converts wlan0 to WIFI + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), + _T("{\"interface\":\"wlan0\",\"enabled\":true}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setInterfaceEnabled), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_BUS_NetSrvMgr_Iface_EventData_t* ifaceData = + static_cast(arg); + // Verify the parameters + EXPECT_TRUE(ifaceData->isInterfaceEnabled); + EXPECT_TRUE(ifaceData->persist); + EXPECT_STREQ(ifaceData->setInterface, "ETHERNET"); // RDKProxy converts eth0 to ETHERNET + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), + _T("{\"interface\":\"eth0\",\"enabled\":true}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetInterfaceState_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setInterfaceEnabled), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetInterfaceState"), + _T("{\"interface\":\"wlan0\",\"enabled\":true}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_wifi) +{ + IARM_BUS_NetSrvMgr_Iface_EventData_t mockIfaceData = {}; + strcpy(mockIfaceData.setInterface, "WIFI"); // Interface is converted to WIFI for wlan0 + mockIfaceData.isInterfaceEnabled = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_isInterfaceEnabled), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockIfaceData](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockIfaceData, sizeof(mockIfaceData)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), + _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"enabled\":true,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetInterfaceState_eth0) +{ + IARM_BUS_NetSrvMgr_Iface_EventData_t mockIfaceData = {}; + strcpy(mockIfaceData.setInterface, "ETHERNET"); // Interface is converted to ETHERNET for eth0 + mockIfaceData.isInterfaceEnabled = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_isInterfaceEnabled), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockIfaceData](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockIfaceData, sizeof(mockIfaceData)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetInterfaceState"), + _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_EQ(response, _T("{\"enabled\":true,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetIPSettings) +{ + IARM_BUS_NetSrvMgr_Iface_Settings_t mockSettings = {}; + strcpy(mockSettings.interface, "eth0"); + strcpy(mockSettings.ipversion, "IPv4"); + mockSettings.autoconfig = true; + strcpy(mockSettings.ipaddress, "192.168.1.100"); + strcpy(mockSettings.netmask, "255.255.255.0"); + strcpy(mockSettings.gateway, "192.168.1.1"); + strcpy(mockSettings.primarydns, "8.8.8.8"); + strcpy(mockSettings.secondarydns, "8.8.4.4"); + mockSettings.isSupported = true; + mockSettings.errCode = NETWORK_IPADDRESS_ACQUIRED; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_getIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockSettings](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockSettings, sizeof(mockSettings)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), + _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\"}"), response)); + + // The response should include all IP settings in JSON format + std::string expectedResponse = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"ula\":\"\",\"dhcpserver\":\"\",\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\",\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetIPSettings_IPv6) +{ + IARM_BUS_NetSrvMgr_Iface_Settings_t mockSettings = {}; + strcpy(mockSettings.interface, "eth0"); + strcpy(mockSettings.ipversion, "IPv6"); + mockSettings.autoconfig = true; + strcpy(mockSettings.ipaddress, "2001:db8::1"); + strcpy(mockSettings.netmask, "64"); + strcpy(mockSettings.gateway, "2001:db8::fe"); + strcpy(mockSettings.primarydns, "2001:4860:4860::8888"); + strcpy(mockSettings.secondarydns, "2001:4860:4860::8844"); + mockSettings.isSupported = true; + mockSettings.errCode = NETWORK_IPADDRESS_ACQUIRED; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_getIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockSettings](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockSettings, sizeof(mockSettings)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), + _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv6\"}"), response)); + + // The response should include all IP settings in JSON format + std::string expectedResponse = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv6\",\"autoconfig\":true,\"ipaddress\":\"2001:db8::1\",\"prefix\":64,\"ula\":\"\",\"dhcpserver\":\"\",\"gateway\":\"2001:db8::fe\",\"primarydns\":\"2001:4860:4860::8888\",\"secondarydns\":\"2001:4860:4860::8844\",\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, GetIPSettings_NoInterfaceNoIpVersion) { + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetIPSettings"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_StaticIPv4) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_BUS_NetSrvMgr_Iface_Settings_t* settings = + static_cast(arg); + + // Verify the parameters + EXPECT_STREQ(settings->interface, "ETHERNET"); + EXPECT_STREQ(settings->ipversion, "IPv4"); + EXPECT_EQ(settings->autoconfig, false); + EXPECT_STREQ(settings->ipaddress, "192.168.1.100"); + EXPECT_STREQ(settings->netmask, "255.255.255.0"); + EXPECT_STREQ(settings->gateway, "192.168.1.1"); + EXPECT_STREQ(settings->primarydns, "8.8.8.8"); + EXPECT_STREQ(settings->secondarydns, "8.8.4.4"); + + return IARM_RESULT_SUCCESS; + }) + )); + + // Test setting static IP configuration + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_DHCP) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_BUS_NetSrvMgr_Iface_Settings_t* settings = + static_cast(arg); + + // Verify the parameters + EXPECT_STREQ(settings->interface, "ETHERNET"); + EXPECT_STREQ(settings->ipversion, "IPv4"); + EXPECT_EQ(settings->autoconfig, true); + + return IARM_RESULT_SUCCESS; + }) + )); + + // Test setting DHCP configuration + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":true}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_WiFi) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_BUS_NetSrvMgr_Iface_Settings_t* settings = + static_cast(arg); + + // Verify the parameters for WiFi interface + EXPECT_STREQ(settings->interface, "WIFI"); + EXPECT_STREQ(settings->ipversion, "IPv4"); + EXPECT_EQ(settings->autoconfig, false); + EXPECT_STREQ(settings->ipaddress, "192.168.1.100"); + EXPECT_STREQ(settings->netmask, "255.255.255.0"); + EXPECT_STREQ(settings->gateway, "192.168.1.1"); + + return IARM_RESULT_SUCCESS; + }) + )); + + std::string request = _T("{\"interface\":\"wlan0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_InvalidInterface) +{ + // Test with an invalid interface name + std::string request = _T("{\"interface\":\"invalid0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_InvalidIPVersion) +{ + // Test with an invalid IP version + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv5\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_IPv6NotSupported) +{ + // Test with IPv6, which is marked as not supported in the implementation + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv6\",\"autoconfig\":false,\"ipaddress\":\"2001:db8::1\",\"prefix\":64,\"gateway\":\"2001:db8::1:1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_InvalidPrefix) +{ + // Test with an invalid prefix value (greater than 32 for IPv4) + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":33,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_InvalidIPFormat) +{ + // Test with an invalid IP address format + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_SameIPAndGateway) +{ + // Test when IP and gateway are the same (should fail validation) + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.1\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_DifferentBroadcastDomain) +{ + // Test when IP and gateway are not in the same broadcast domain + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.2.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_IPEqualsBroadcast) +{ + // Test when IP equals broadcast address + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.255\",\"prefix\":24,\"gateway\":\"192.168.1.1\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_GatewayEqualsBroadcast) +{ + // Test when gateway equals broadcast address + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.255\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, SetIPSettings_APICallFailed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_setIPSettings), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + std::string request = _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\",\"autoconfig\":false,\"ipaddress\":\"192.168.1.100\",\"prefix\":24,\"gateway\":\"192.168.1.1\",\"primarydns\":\"8.8.8.8\",\"secondarydns\":\"8.8.4.4\"}"); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetIPSettings"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, StartWiFiScan_Success) +{ + IARM_Bus_WiFiSrvMgr_SsidList_Param_t mockParam = {}; + mockParam.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsync), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), + _T("{\"frequency\":[\"2.4\"]}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StartWiFiScan_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getAvailableSSIDsAsync), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWiFiScan"), + _T("{\"frequency\":[\"2.4\"]}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, StopWiFiScan_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_stopProgressiveWifiScanning), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StopWiFiScan"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StopWiFiScan_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_stopProgressiveWifiScanning), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StopWiFiScan"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetKnownSSIDs_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + strncpy(mockParam.data.getConnectedSSID.ssid, "TestNetwork", SSID_SIZE - 1); + mockParam.data.getConnectedSSID.securityMode = NET_WIFI_SECURITY_WPA_WPA2_PSK; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetKnownSSIDs"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssids\":[\"TestNetwork\"],\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetKnownSSIDs_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetKnownSSIDs"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, AddToKnownSSIDs_Success) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_saveSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_Bus_WiFiSrvMgr_Param_t* param = static_cast(arg); + + // Verify the parameters + EXPECT_STREQ(param->data.connect.ssid, "TestNetwork"); + EXPECT_STREQ(param->data.connect.passphrase, "TestPassword"); + EXPECT_EQ(param->data.connect.security_mode, NET_WIFI_SECURITY_WPA2_PSK_AES); + param->status = true; + + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{\"ssid\":\"TestNetwork\",\"passphrase\":\"TestPassword\",\"security\":1}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, AddToKnownSSIDs_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_saveSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("AddToKnownSSIDs"), + _T("{\"ssid\":\"TestNetwork\",\"passphrase\":\"TestPassword\",\"security\":1}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, RemoveKnownSSID_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_clearSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + IARM_Bus_WiFiSrvMgr_Param_t* param = static_cast(arg); + param->status = true; + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), + _T("{\"ssid\":\"TestNetwork\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, RemoveKnownSSID_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_clearSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("RemoveKnownSSID"), + _T("{\"ssid\":\"TestNetwork\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, WiFiConnect_Success) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_connect), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_Bus_WiFiSrvMgr_Param_t* param = static_cast(arg); + + // Verify the parameters + EXPECT_STREQ(param->data.connect.ssid, "TestNetwork"); + EXPECT_STREQ(param->data.connect.passphrase, "TestPassword"); + EXPECT_EQ(param->data.connect.security_mode, NET_WIFI_SECURITY_WPA2_PSK_AES); + param->status = true; + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), + _T("{\"ssid\":\"TestNetwork\",\"passphrase\":\"TestPassword\",\"security\":1}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, WiFiConnectEAP_Success) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_connect), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([](const char*, const char*, void* arg, size_t) { + IARM_Bus_WiFiSrvMgr_Param_t* param = static_cast(arg); + + // Verify the parameters + EXPECT_STREQ(param->data.connect.ssid, "TestNetwork"); + EXPECT_STREQ(param->data.connect.passphrase, "TestPassword"); + EXPECT_STREQ(param->data.connect.clientcert, "test_client_cert"); + EXPECT_STREQ(param->data.connect.privatekey, "test_private_key"); + EXPECT_EQ(param->data.connect.security_mode, NET_WIFI_SECURITY_WPA_WPA2_ENTERPRISE); + EXPECT_TRUE(param->data.connect.persistSSIDInfo); + param->status = true; + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), + _T("{\"ssid\":\"TestNetwork\",\"passphrase\":\"TestPassword\",\"security\":3,\"ca_cert\":\"test_ca_cert\",\"client_cert\":\"test_client_cert\",\"private_key\":\"test_private_key\",\"private_key_passwd\":\"test_private_key_passwd\",\"eap\":\"test_eap\",\"eap_identity\":\"test_eap_identity\",\"eap_password\":\"test_eap_password\",\"eap_phase1\":\"test_eap_phase1\",\"eap_phase2\":\"test_eap_phase2\",\"persist\":true}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, WiFiConnect_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_connect), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiConnect"), + _T("{\"ssid\":\"TestNetwork\",\"passphrase\":\"TestPassword\",\"security\":1}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, WiFiDisconnect_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_disconnectSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, WiFiDisconnect_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_disconnectSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("WiFiDisconnect"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetConnectedSSID_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + strncpy(mockParam.data.getConnectedSSID.ssid, "TestNetwork", SSID_SIZE - 1); + mockParam.data.getConnectedSSID.securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + mockParam.data.getConnectedSSID.signalStrength = -30.00; // Example signal strength + mockParam.data.getConnectedSSID.frequency = 2412; // Example frequency in MHz + mockParam.data.getConnectedSSID.rate = 0; // Example rate + mockParam.data.getConnectedSSID.noise = 6; // Example noise level + strncpy(mockParam.data.getConnectedSSID.bssid, "AB:CD:EF:GH:IJ:KL", BSSID_BUFF - 1); // Example BSSID + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"TestNetwork\",\"bssid\":\"AB:CD:EF:GH:IJ:KL\",\"security\":1,\"strength\":-30,\"frequency\":2.412,\"rate\":0,\"noise\":0,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetConnectedSSID_Success_noise_9999) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + strncpy(mockParam.data.getConnectedSSID.ssid, "TestNetwork", SSID_SIZE - 1); + mockParam.data.getConnectedSSID.securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + mockParam.data.getConnectedSSID.signalStrength = -30.00; // Example signal strength + mockParam.data.getConnectedSSID.frequency = 2412; // Example frequency in MHz + mockParam.data.getConnectedSSID.rate = 0; // Example rate + mockParam.data.getConnectedSSID.noise = 9999; // Example noise level + strncpy(mockParam.data.getConnectedSSID.bssid, "AB:CD:EF:GH:IJ:KL", BSSID_BUFF - 1); // Example BSSID + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"TestNetwork\",\"bssid\":\"AB:CD:EF:GH:IJ:KL\",\"security\":1,\"strength\":-30,\"frequency\":2.412,\"rate\":0,\"noise\":0,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetConnectedSSID_Success_noise_100) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + strncpy(mockParam.data.getConnectedSSID.ssid, "TestNetwork", SSID_SIZE - 1); + mockParam.data.getConnectedSSID.securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + mockParam.data.getConnectedSSID.signalStrength = -30.00; // Example signal strength + mockParam.data.getConnectedSSID.frequency = 2412; // Example frequency in MHz + mockParam.data.getConnectedSSID.rate = 0; // Example rate + mockParam.data.getConnectedSSID.noise = -100; // Example noise level + strncpy(mockParam.data.getConnectedSSID.bssid, "AB:CD:EF:GH:IJ:KL", BSSID_BUFF - 1); // Example BSSID + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"TestNetwork\",\"bssid\":\"AB:CD:EF:GH:IJ:KL\",\"security\":1,\"strength\":-30,\"frequency\":2.412,\"rate\":0,\"noise\":-96,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetConnectedSSID_Success_noise_50) +{ + IARM_Bus_WiFiSrvMgr_Param_t mockParam = {}; + mockParam.status = true; + strncpy(mockParam.data.getConnectedSSID.ssid, "TestNetwork", SSID_SIZE - 1); + mockParam.data.getConnectedSSID.securityMode = NET_WIFI_SECURITY_WPA2_PSK_AES; + mockParam.data.getConnectedSSID.signalStrength = -30.00; // Example signal strength + mockParam.data.getConnectedSSID.frequency = 2412; // Example frequency in MHz + mockParam.data.getConnectedSSID.rate = 0; // Example rate + mockParam.data.getConnectedSSID.noise = -50; // Example noise level + strncpy(mockParam.data.getConnectedSSID.bssid, "AB:CD:EF:GH:IJ:KL", BSSID_BUFF - 1); // Example BSSID + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockParam](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockParam, sizeof(mockParam)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"TestNetwork\",\"bssid\":\"AB:CD:EF:GH:IJ:KL\",\"security\":1,\"strength\":-30,\"frequency\":2.412,\"rate\":0,\"noise\":-50,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetConnectedSSID_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getConnectedSSID), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectedSSID"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, StartWPS_Success) +{ + IARM_Bus_WiFiSrvMgr_WPS_Parameters_t mockWpsParams = {}; + mockWpsParams.pbc = true; + mockWpsParams.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_initiateWPSPairing2), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockWpsParams](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockWpsParams, sizeof(mockWpsParams)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StartWPS_WIFI_WPS_PIN_Success) +{ + IARM_Bus_WiFiSrvMgr_WPS_Parameters_t mockWpsParams = {}; + mockWpsParams.pbc = true; + mockWpsParams.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_initiateWPSPairing2), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockWpsParams](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockWpsParams, sizeof(mockWpsParams)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), _T("{\"method\":\"PIN\", \"pin\":\"12345678\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StartWPS_WIFI_WPS_SERIALIZED_PIN_Success) +{ + IARM_Bus_WiFiSrvMgr_WPS_Parameters_t mockWpsParams = {}; + mockWpsParams.pbc = true; + mockWpsParams.status = true; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_initiateWPSPairing2), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockWpsParams](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockWpsParams, sizeof(mockWpsParams)); + return IARM_RESULT_SUCCESS; + }) + )); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), _T("{\"method\":\"SERIALIZED_PIN\", \"pin\":\"12345678\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StartWPS_Failed) +{ + IARM_Bus_WiFiSrvMgr_WPS_Parameters_t mockWpsParams = {}; + mockWpsParams.pbc = true; + mockWpsParams.status = false; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_initiateWPSPairing2), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([&mockWpsParams](const char*, const char*, void* arg, size_t) { + memcpy(arg, &mockWpsParams, sizeof(mockWpsParams)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StartWPS"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, StopWPS_Success) +{ + IARM_Bus_WiFiSrvMgr_Param_t param; + memset(¶m, 0, sizeof(param)); + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_cancelWPSPairing), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([¶m](const char*, const char*, void* arg, size_t) { + memcpy(arg, ¶m, sizeof(param)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StopWPS"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":true}")); +} + +TEST_F(NetworkManagerTest, StopWPS_Failed) +{ + IARM_Bus_WiFiSrvMgr_Param_t param; + memset(¶m, 0, sizeof(param)); + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_cancelWPSPairing), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("StopWPS"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetWifiState) +{ + IARM_Bus_WiFiSrvMgr_Param_t param; + param.data.wifiStatus = WIFI_CONNECTED; + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getCurrentState), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::DoAll( + ::testing::Invoke([¶m](const char*, const char*, void* arg, size_t) { + memcpy(arg, ¶m, sizeof(param)); + return IARM_RESULT_SUCCESS; + }) + )); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"state\":5,\"status\":\"WIFI_STATE_CONNECTED\",\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetWifiState_Failed) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_WIFI_MGR_API_getCurrentState), + ::testing::NotNull(), ::testing::_)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWifiState"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetPublicIP_Failed) +{ + // Mock STUN client behavior for a successful public IP lookup + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPublicIP"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv4\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPublicIP"), _T("{\"interface\":\"eth0\",\"ipversion\":\"IPv6\"}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetStunEndpoint) +{ + // The default values from the configuration in NetworkManagerTest constructor + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetStunEndpoint"), _T("{}"), response)); + std::string expectedResponse = _T("{\"endpoint\":\"stun.l.google.com\",\"port\":19302,\"timeout\":30,\"cacheLifetime\":0,\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, SetStunEndpoint_Success) +{ + std::string request = _T("{\"endpoint\":\"stun.example.com\",\"port\":3478,\"timeout\":60,\"cacheLifetime\":300}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetStunEndpoint"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + // Verify the settings were applied by getting them + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetStunEndpoint"), _T("{}"), response)); + std::string expectedResponse = _T("{\"endpoint\":\"stun.example.com\",\"port\":3478,\"timeout\":60,\"cacheLifetime\":300,\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, SetStunEndpoint_InvalidParams) +{ + // Test with empty request + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetStunEndpoint"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, ConfigLineEmpty) +{ + // Mock the service to return an empty configuration line + EXPECT_CALL(service, ConfigLine()) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Return(_T(""))); + + // Initialize the plugin with the mocked service + plugin->Initialize(&service); +} + +TEST_F(NetworkManagerTest, ComLinkEmpty) +{ + // Mock the service to return an empty configuration line + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + return nullptr; + })); + + // Initialize the plugin with the mocked service + plugin->Initialize(&service); +} + +TEST_F(NetworkManagerTest, platformInit) +{ + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Init(::testing::StrEq("netsrvmgr-thunder"))) + .Times(2) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)) + .WillOnce(::testing::Return(IARM_RESULT_SUCCESS)); + + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Connect()) + .Times(2) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)) + .WillOnce(::testing::Return(IARM_RESULT_SUCCESS)); + EXPECT_CALL(*p_iarmBusImplMock, IARM_Bus_Call_with_IPCTimeout(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME), + ::testing::StrEq(IARM_BUS_NETSRVMGR_API_isAvailable), + ::testing::NotNull(), ::testing::_, ::testing::_)) + .Times(3) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)) + .WillOnce(::testing::Return(IARM_RESULT_IPCCORE_FAIL)) + .WillOnce(::testing::Return(IARM_RESULT_SUCCESS)); + + plugin->Initialize(&service); +} + +TEST_F(NetworkManagerTest, GetConnectivityTestEndpoints) +{ + std::string request = _T("{\"endpoints\":[\"https://example.com/connectivity\",\"https://example.org/check\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Allow server to start + // The default values from the configuration in NetworkManagerTest constructor + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectivityTestEndpoints"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"endpoints\":[\"https:\\/\\/example.com\\/connectivity\",\"https:\\/\\/example.org\\/check\"],\"success\":true}")); +} + +TEST_F(NetworkManagerTest, SetConnectivityTestEndpoints_Success) +{ + std::string request = _T("{\"endpoints\":[\"https://example.com/connectivity\",\"https://example.org/check\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Allow server to start + // Verify the settings were applied by getting them + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetConnectivityTestEndpoints"), _T("{}"), response)); + std::string expectedResponse = _T("{\"endpoints\":[\"https:\\/\\/example.com\\/connectivity\",\"https:\\/\\/example.org\\/check\"],\"success\":true}"); + EXPECT_EQ(response, expectedResponse); +} + +TEST_F(NetworkManagerTest, SetConnectivityTestEndpoints_Empty) +{ + // Test with empty endpoints array + std::string request = _T("{\"endpoints\":[]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, m_ipv4AddressCache) +{ + Exchange::INetworkManager::IPAddress address{}; + address.ipversion = "IPv4"; + address.autoconfig = true; + address.dhcpserver = "192.168.1.1"; + address.ula = ""; + address.ipaddress = "192.168.1.100"; + address.prefix = 24; + address.gateway = "192.168.1.1"; + address.primarydns = "8.8.8.8"; + address.secondarydns = "8.8.4.4"; + plugin->m_ipv4AddressCache = address; +} + +TEST_F(NetworkManagerTest, m_ipv6AddressCache) +{ + Exchange::INetworkManager::IPAddress address{}; + address.ipversion = "IPv6"; + address.autoconfig = true; + address.dhcpserver = ""; + address.ula = "fd00:1234:5678::1"; + address.ipaddress = "2001:db8::1"; + address.prefix = 64; + address.gateway = "2001:db8::fffe"; + address.primarydns = "2001:4860:4860::8888"; + address.secondarydns = "2001:4860:4860::8844"; + plugin->m_ipv6AddressCache = address; +} + +TEST_F(NetworkManagerTest, GetWiFiSignalQualityWpa_cliFailed) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + return nullptr; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWiFiSignalQuality"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"success\":false}")); +} + +TEST_F(NetworkManagerTest, GetWiFiSignalQualityDisconnected2) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "wpa_state=DISCONNECTED\n" + "p2p_device_address=d4:52:ee:d9:0a:39\n" + "address=d4:52:ee:d9:0a:39\n" + "uuid=feb4fcc9-04c3-5be3-a3f9-03fed1d0604c\n", tempFile); + rewind(tempFile); + } + return tempFile; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWiFiSignalQuality"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"\",\"quality\":\"Disconnected\",\"snr\":0,\"strength\":0,\"noise\":0,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetWiFiSignalQualityConnected) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "bssid=aa:bb:cc:dd:ee:ff\n" + "freq=2462\n" + "ssid=dummySSID\n", tempFile); + rewind(tempFile); + } + return tempFile; + })) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli signal_poll")); + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "RSSI=-30\n" + "LINKSPEED=300\n" + "NOISE=-114\n" + "FREQUENCY=2417\n" + "AVG_RSSI=-30\n", tempFile); + rewind(tempFile); + } + return tempFile; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWiFiSignalQuality"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"dummySSID\",\"quality\":\"Excellent\",\"snr\":66,\"strength\":-30,\"noise\":-96,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetWiFiSignalQualityConnectedGood) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "bssid=aa:bb:cc:dd:ee:ff\n" + "freq=5462\n" + "ssid=dummySSID\n", tempFile); + rewind(tempFile); + } + return tempFile; + })) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli signal_poll")); + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "RSSI=\n" + "LINKSPEED=300\n" + "NOISE=-114\n" + "FREQUENCY=2417\n" + "AVG_RSSI=-90\n", tempFile); + rewind(tempFile); + } + return tempFile; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWiFiSignalQuality"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"dummySSID\",\"quality\":\"Weak\",\"snr\":6,\"strength\":-90,\"noise\":-96,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, GetWiFiSignalQualityConnectedLowBad) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "bssid=aa:bb:cc:dd:ee:ff\n" + "freq=5462\n" + "ssid=dummySSID\n", tempFile); + rewind(tempFile); + } + return tempFile; + })) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli signal_poll")); + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "RSSI=\n" + "LINKSPEED=300\n" + "NOISE=9999\n" + "FREQUENCY=5462\n" + "AVG_RSSI=-120\n", tempFile); + rewind(tempFile); + } + return tempFile; + })); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetWiFiSignalQuality"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ssid\":\"dummySSID\",\"quality\":\"Excellent\",\"snr\":120,\"strength\":-120,\"noise\":0,\"success\":true}")); +} + +TEST_F(NetworkManagerTest, Trace_Success_ipv4) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("traceroute -w 3 -m 6 -q 1 8.8.8.8 52 2>&1")); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("traceroute to 8.8.8.8 (8.8.8.8), 6 hops max, 52 byte packets\n" + "1 gateway (10.46.5.1) 0.448 ms\n" + "2 10.46.0.240 (10.46.0.240) 3.117 ms\n" + "3 *\n" + "4 *\n" + "5 *\n" + "6 *\n", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Trace"), + _T("{\"endpoint\":\"8.8.8.8\",\"ipversion\":\"IPv4\",\"packets\":1}"), response)); + + // We expect the response to contain success and results fields + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("\"results\":") != std::string::npos); + EXPECT_TRUE(response.find("\"endpoint\":\"8.8.8.8\"") != std::string::npos); + EXPECT_TRUE(response.find("6 hops max, 52 byte packets") != std::string::npos); +} + +TEST_F(NetworkManagerTest, Trace_Failed_ipv6) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("traceroute6 -w 3 -m 6 -q 1 8.8.8.8 64 2>&1")); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("traceroute6: bad address '8.8.8.8'", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Trace"), + _T("{\"endpoint\":\"8.8.8.8\",\"ipversion\":\"IPv6\",\"packets\":1}"), response)); + + EXPECT_EQ(response, _T("{\"results\":\"[\\\"traceroute6: bad address '8.8.8.8' \\\"]\",\"endpoint\":\"8.8.8.8\",\"success\":true}")); +} + +TEST_F(NetworkManagerTest, Trace_Success_ipv6) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("traceroute6 -w 3 -m 6 -q 1 2001:4860:4860::8888 64 2>&1")); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("traceroute to 2001:4860:4860::8888 (2001:4860:4860::8888), 6 hops max, 64 byte packets\n" + "1 2401:4900:9092:d613::d4 (2401:4900:9092:d613::d4) 14.503 ms !N\n", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Trace"), + _T("{\"endpoint\":\"2001:4860:4860::8888\",\"ipversion\":\"IPv6\",\"packets\":1}"), response)); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); + EXPECT_TRUE(response.find("6 hops max, 64 byte packets") != std::string::npos); + EXPECT_TRUE(response.find("\"endpoint\":\"2001:4860:4860::8888\"") != std::string::npos); +} + +TEST_F(NetworkManagerTest, Ping_Success_ipv4) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + std::string cmdStr = command; + EXPECT_TRUE(cmdStr.find("ping") != std::string::npos); + EXPECT_TRUE(cmdStr.find("8.8.8.8") != std::string::npos); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("PING 8.8.8.8 (8.8.8.8): 56 data bytes\n" + "64 bytes from 8.8.8.8: seq=0 ttl=119 time=23.363 ms\n" + "64 bytes from 8.8.8.8: seq=1 ttl=119 time=23.440 ms\n" + "64 bytes from 8.8.8.8: seq=2 ttl=119 time=23.384 ms\n" + "\n" + "--- 8.8.8.8 ping statistics ---\n" + "3 packets transmitted, 3 packets received, 0% packet loss\n" + "round-trip min/avg/max/mdev = 23.363/23.395/23.440/0.179 ms\n", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Ping"), + _T("{\"endpoint\":\"8.8.8.8\",\"ipversion\":\"IPv4\",\"packets\":5,\"timeout\":2}"), response)); + + EXPECT_EQ(response, _T("{\"endpoint\":\"8.8.8.8\",\"success\":true,\"tripStdDev\":\"0.179 ms\",\"tripMax\":\"23.440\",\"tripAvg\":\"23.395\",\"tripMin\":\" 23.363\",\"packetLoss\":\" 0\",\"packetsReceived\":3,\"packetsTransmitted\":3}")); +} + +TEST_F(NetworkManagerTest, Ping_Failed_ipv4) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + std::string cmdStr = command; + EXPECT_TRUE(cmdStr.find("ping") != std::string::npos); + EXPECT_TRUE(cmdStr.find("192.0.0.1") != std::string::npos); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("PING 192.0.0.1 (192.0.0.1): 56 data bytes\n" + "\n" + "--- 192.0.0.1 ping statistics ---\n" + "3 packets transmitted, 0 packets received, 100% packet loss\n", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Ping"), + _T("{\"endpoint\":\"192.0.0.1\",\"ipversion\":\"IPv4\",\"packets\":5,\"timeout\":2}"), response)); + + EXPECT_EQ(response, _T("{\"endpoint\":\"192.0.0.1\",\"success\":true,\"packetLoss\":\" 100\",\"packetsReceived\":0,\"packetsTransmitted\":3}")); +} + +TEST_F(NetworkManagerTest, Ping_success_ipv6) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + std::string cmdStr = command; + EXPECT_TRUE(cmdStr.find("ping6") != std::string::npos); + EXPECT_TRUE(cmdStr.find("2404:6800:4007:80b::200e") != std::string::npos); + EXPECT_EQ(type, "r"); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("PING 2404:6800:4007:80b::200e (2404:6800:4007:80b::200e): 56 data bytes\n" + "64 bytes from 2404:6800:4007:80b::200e: seq=0 ttl=117 time=39.546 ms\n" + "64 bytes from 2404:6800:4007:80b::200e: seq=0 ttl=117 time=46.505 ms (DUP!)\n" + "64 bytes from 2404:6800:4007:80b::200e: seq=1 ttl=117 time=35.637 ms\n" + "64 bytes from 2404:6800:4007:80b::200e: seq=2 ttl=117 time=44.998 ms\n" + "\n" + "--- 2404:6800:4007:80b::200e ping statistics ---\n" + "3 packets transmitted, 3 packets received, 1 duplicates, 0% packet loss\n" + "round-trip min/avg/max/mdev = 35.637/41.671/46.505/4.345 ms\n", tempFile); + rewind(tempFile); + } + return tempFile; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Ping"), + _T("{\"endpoint\":\"2404:6800:4007:80b::200e\",\"ipversion\":\"IPv6\",\"packets\":5,\"timeout\":2}"), response)); + + EXPECT_EQ(response, _T("{\"endpoint\":\"2404:6800:4007:80b::200e\",\"success\":true,\"tripStdDev\":\"4.345 ms\",\"tripMax\":\"46.505\",\"tripAvg\":\"41.671\",\"tripMin\":\" 35.637\",\"packetLoss\":\" 1 duplicates\",\"packetsReceived\":3,\"packetsTransmitted\":3}")); +} + +TEST_F(NetworkManagerTest, Ping_Failed_ipv6) +{ + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .WillOnce([](const char* command, const char* type) -> FILE* { + std::string cmdStr = command; + EXPECT_TRUE(cmdStr.find("ping6") != std::string::npos); + EXPECT_TRUE(cmdStr.find("2404:6800:4007:80b::200e") != std::string::npos); + EXPECT_EQ(type, "r"); + return NULL; + }); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("Ping"), + _T("{\"endpoint\":\"2404:6800:4007:80b::200e\",\"ipversion\":\"IPv6\",\"packets\":5,\"timeout\":2}"), response)); + + EXPECT_EQ(response, _T("{\"endpoint\":\"2404:6800:4007:80b::200e\"}")); +} diff --git a/tests/l2Test/rdk/l2_test_rdkproxyEvent.cpp b/tests/l2Test/rdk/l2_test_rdkproxyEvent.cpp new file mode 100644 index 000000000..7c27e2b24 --- /dev/null +++ b/tests/l2Test/rdk/l2_test_rdkproxyEvent.cpp @@ -0,0 +1,796 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "FactoriesImplementation.h" +#include "IarmBusMock.h" +#include "WrapsMock.h" +#include "CurlWrapsMock.h" +#include "ServiceMock.h" +#include "ThunderPortability.h" +#include "COMLinkMock.h" +#include "IarmBusMock.h" +#include "WorkerPoolImplementation.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerRDKProxy.h" +#include "NetworkManagerLogger.h" +#include "NetworkManager.h" +#include "ConnectivityMockServer.h" + +using namespace WPEFramework; +using ::testing::NiceMock; + +#ifdef USE_CONNECTIVITYCHECKMGR +namespace WPEFramework { +namespace Plugin { +class NetworkManagerConnectivityClientTestAccess { +public: + static void Notify(NetworkManagerConnectivityClient& client, + const Exchange::IConnectivityCheck::InternetStatus status, + const std::string& reason) + { + client.notifyInternetStatusChanged(status, reason); + } +}; +} // namespace Plugin +} // namespace WPEFramework +#endif + +class NetworkManagerEventTest : public ::testing::Test { +protected: + Core::ProxyType plugin; + Core::JSONRPC::Handler& handler; + DECL_CORE_JSONRPC_CONX connection; + Core::JSONRPC::Message message; + string response; + + // Mock classes + IarmBusImplMock *p_iarmBusImplMock = nullptr; + WrapsImplMock *p_wrapsImplMock = nullptr; + CurlWrapsImplMock *p_curlWrapsImplMock = nullptr; + IARM_EventHandler_t _nmEventHandler{}; + Core::ProxyType NetworkManagerImpl; + + NiceMock comLinkMock; + NiceMock service; + PLUGINHOST_DISPATCHER* dispatcher; + Core::ProxyType workerPool; + NiceMock factoriesImplementation; + + + NetworkManagerEventTest() + : plugin(Core::ProxyType::Create()) + , handler(*(plugin)) + , INIT_CONX(1, 0) + , workerPool(Core::ProxyType::Create(2, Core::Thread::DefaultStackSize(), 16)) + { + p_iarmBusImplMock = new NiceMock ; + IarmBus::setImpl(p_iarmBusImplMock); + p_wrapsImplMock = new NiceMock ; + Wraps::setImpl(p_wrapsImplMock); + p_curlWrapsImplMock = new NiceMock ; + CurlWraps::setImpl(p_curlWrapsImplMock); + ON_CALL(service, COMLink()) + .WillByDefault(::testing::Invoke( + [this]() { + return &comLinkMock; + })); + + ON_CALL(service, ConfigLine()) + .WillByDefault(::testing::Return( + "{" + " \"locator\":\"libWPEFrameworkNetworkManager.so\"," + " \"classname\":\"NetworkManager\"," + " \"callsign\":\"org.rdk.NetworkManager\"," + " \"startuporder\":55," + " \"autostart\":false," + " \"configuration\":{" + " \"root\":{" + " \"outofprocess\":true," + " \"locator\":\"libWPEFrameworkNetworkManagerImpl.so\"" + " }," + " \"connectivity\":{" + " \"endpoint_1\":\"http://localhost:8080/generate_204\"," + " \"interval\":60" + " }," + " \"stun\":{" + " \"endpoint\":\"stun.l.google.com\"," + " \"port\":19302," + " \"interval\":30" + " }" + " }" + "}" + )); + + ON_CALL(comLinkMock, Instantiate(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const RPC::Object& object, const uint32_t waitTime, uint32_t& connectionId) { + NetworkManagerImpl = Core::ProxyType::Create(); + return &NetworkManagerImpl; + })); + + ON_CALL(*p_iarmBusImplMock, IARM_Bus_Init(::testing::StrEq(IARM_BUS_NM_SRV_MGR_NAME))) + .WillByDefault(::testing::Return(IARM_RESULT_IPCCORE_FAIL)); + + ON_CALL(*p_iarmBusImplMock, IARM_Bus_RegisterEventHandler(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) { + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged)) { + _nmEventHandler = handler; + } + if ((string(IARM_BUS_NM_SRV_MGR_NAME) == string(ownerName)) && (eventId == IARM_BUS_WIFI_MGR_EVENT_onError)) { + _nmEventHandler = handler; + } + return IARM_RESULT_SUCCESS; + })); + + PluginHost::IFactories::Assign(&factoriesImplementation); + + Core::IWorkerPool::Assign(&(*workerPool)); + workerPool->Run(); + + dispatcher = static_cast( + plugin->QueryInterface(PLUGINHOST_DISPATCHER_ID)); + dispatcher->Activate(&service); + response = plugin->Initialize(&service); + EXPECT_EQ(string(""), response); + NetworkManagerLogger::SetLevel(static_cast(NetworkManagerLogger::DEBUG_LEVEL)); // Set log level to DEBUG_LEVEL + } + + virtual void SetUp() override + { + + } + + virtual ~NetworkManagerEventTest() override + { + plugin->Deinitialize(&service); + + dispatcher->Deactivate(); + dispatcher->Release(); + + Core::IWorkerPool::Assign(nullptr); + workerPool.Release(); + + IarmBus::setImpl(nullptr); + if (p_iarmBusImplMock != nullptr) + { + delete p_iarmBusImplMock; + p_iarmBusImplMock = nullptr; + } + + Wraps::setImpl(nullptr); + if (p_wrapsImplMock != nullptr) + { + delete p_wrapsImplMock; + p_wrapsImplMock = nullptr; + } + + CurlWraps::setImpl(nullptr); + if (p_curlWrapsImplMock != nullptr) + { + delete p_curlWrapsImplMock; + p_curlWrapsImplMock = nullptr; + } + } +}; + +TEST_F(NetworkManagerEventTest, iarmEventHandlerErrorChack) +{ + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t eventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, &eventData, 0); + + eventData = { .interface = "eth0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERNET_CONNECTION_CHANGED, &eventData, sizeof(eventData)); +} + +TEST_F(NetworkManagerEventTest, onInterfaceStateChange) +{ + Core::Event onInterfaceStateChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInterfaceStateChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_EQ(text, string(_T("{\"jsonrpc\":\"2.0\",\"method\":\"org.rdk.NetworkManager.onInterfaceStateChange\",\"params\":{\"state\":0,\"status\":\"INTERFACE_ADDED\",\"interface\":\"wlan0\"}}"))); + onInterfaceStateChange.SetEvent(); + return Core::ERROR_NONE; + })) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_EQ(text, string(_T("{\"jsonrpc\":\"2.0\",\"method\":\"org.rdk.NetworkManager.onInterfaceStateChange\",\"params\":{\"state\":0,\"status\":\"INTERFACE_ADDED\",\"interface\":\"eth0\"}}"))); + onInterfaceStateChange.SetEvent(); + return Core::ERROR_NONE; + })); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t eventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, &eventData, sizeof(eventData)); + + eventData = { .interface = "eth0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_ENABLED_STATUS, &eventData, sizeof(eventData)); + + EXPECT_EQ(Core::ERROR_NONE, onInterfaceStateChange.Lock()); + + sleep(5); + EVENT_UNSUBSCRIBE(2, _T("onInterfaceStateChange"), _T("org.rdk.NetworkManager"), message); +} + +TEST_F(NetworkManagerEventTest, onActiveInterfaceChange) +{ + Core::Event onActiveInterfaceChange(false, true); + EVENT_SUBSCRIBE(2, _T("onActiveInterfaceChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onActiveInterfaceChange\"") != std::string::npos); + EXPECT_TRUE(text.find("\"prevActiveInterface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(text.find("\"currentActiveInterface\":\"wlan0\"") != std::string::npos); + onActiveInterfaceChange.SetEvent(); + return Core::ERROR_NONE; + })); + + IARM_BUS_NetSrvMgr_Iface_EventDefaultInterface_t eventData; + strcpy(eventData.oldInterface, "eth0"); + strcpy(eventData.newInterface, "wlan0"); + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_DEFAULT_INTERFACE, &eventData, sizeof(eventData)); + + EXPECT_EQ(Core::ERROR_NONE, onActiveInterfaceChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onActiveInterfaceChange"), _T("org.rdk.NetworkManager"), message); +} + +TEST_F(NetworkManagerEventTest, onWiFiStateChange) +{ + Core::Event onWiFiStateChange(false, true); + EVENT_SUBSCRIBE(2, _T("onWiFiStateChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onWiFiStateChange\"") != std::string::npos); + EXPECT_TRUE(text.find("\"state\":5") != std::string::npos); // WIFI_STATE_CONNECTED = 4 + EXPECT_TRUE(text.find("\"status\":\"WIFI_STATE_CONNECTED\"") != std::string::npos); + onWiFiStateChange.SetEvent(); + return Core::ERROR_NONE; + })); + + EXPECT_CALL(*p_wrapsImplMock, popen(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli status")); + // Create a temporary file with the mock output + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "bssid=aa:bb:cc:dd:ee:ff\n" + "freq=2462\n" + "ssid=dummySSID\n", tempFile); + rewind(tempFile); + } + return tempFile; + })) + .WillOnce(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + EXPECT_THAT(string(command), ::testing::MatchesRegex("wpa_cli signal_poll")); + FILE* tempFile = tmpfile(); + if (tempFile) { + fputs("Selected interface 'wlan0'\n" + "RSSI=-30\n" + "LINKSPEED=300\n" + "NOISE=-114\n" + "FREQUENCY=2417\n" + "AVG_RSSI=-30\n", tempFile); + rewind(tempFile); + } + return tempFile; + })); + + // Create and populate event data for WiFi state change + IARM_BUS_WiFiSrvMgr_EventData_t eventData; + eventData.data.wifiStateChange.state = WIFI_CONNECTED; + + // Trigger the event handler + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onWIFIStateChanged, &eventData, sizeof(eventData)); + EXPECT_EQ(Core::ERROR_NONE, onWiFiStateChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onWiFiStateChange"), _T("org.rdk.NetworkManager"), message); +} + +TEST_F(NetworkManagerEventTest, onWiFiErrorStateChange) +{ + Core::Event onWiFiStateChange(false, true); + EVENT_SUBSCRIBE(2, _T("onWiFiStateChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onWiFiStateChange\"") != std::string::npos); + EXPECT_TRUE(text.find("\"state\":8") != std::string::npos); // WIFI_STATE_CONNECTION_LOST = 8 + EXPECT_TRUE(text.find("\"status\":\"WIFI_STATE_CONNECTION_LOST\"") != std::string::npos); + onWiFiStateChange.SetEvent(); + return Core::ERROR_NONE; + })); + + // Create and populate event data for WiFi error + IARM_BUS_WiFiSrvMgr_EventData_t eventData; + eventData.data.wifiError.code = WIFI_CONNECTION_LOST; + + // Trigger the event handler + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onError, &eventData, sizeof(eventData)); + + EXPECT_EQ(Core::ERROR_NONE, onWiFiStateChange.Lock()); + + EVENT_UNSUBSCRIBE(2, _T("onWiFiStateChange"), _T("org.rdk.NetworkManager"), message); +} + +TEST_F(NetworkManagerEventTest, onAvailableSSIDsEvent) +{ + Core::Event onAvailableSSIDs(false, true); + EVENT_SUBSCRIBE(2, _T("onAvailableSSIDs"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onAvailableSSIDs\"") != std::string::npos); + EXPECT_TRUE(text.find("\"ssids\"") != std::string::npos); + onAvailableSSIDs.SetEvent(); + return Core::ERROR_NONE; + })); + + // Create a simple JSON string with SSID list + const char* jsonData = R"({ + "getAvailableSSIDs": [ + { + "ssid": "HomeWiFi", + "security": 0, + "signalStrength": -65, + "frequency": 2.4 + }, + { + "ssid": "OfficeNetwork", + "security": 4, + "signalStrength": -72, + "frequency": 5.0 + } + ] + })"; + + // Create and populate event data for WiFi SSIDs + IARM_BUS_WiFiSrvMgr_EventData_t eventData; + strncpy(eventData.data.wifiSSIDList.ssid_list, jsonData, sizeof(eventData.data.wifiSSIDList.ssid_list)); + + // Trigger the event handler + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_WIFI_MGR_EVENT_onAvailableSSIDs, &eventData, sizeof(eventData)); + + EXPECT_EQ(Core::ERROR_NONE, onAvailableSSIDs.Lock()); + + EVENT_UNSUBSCRIBE(2, _T("onAvailableSSIDs"), _T("org.rdk.NetworkManager"), message); +} + + +TEST_F(NetworkManagerEventTest, ConnectivityMonitorEvents) +{ + Core::Event onAddressChange(false, true); + EVENT_SUBSCRIBE(2, _T("onIPAddressChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onIPAddressChange\"") != std::string::npos); + EXPECT_TRUE(text.find("\"interface\":\"eth0\"") != std::string::npos); + EXPECT_TRUE(text.find("\"ipversion\":\"IPv4\"") != std::string::npos); + EXPECT_TRUE(text.find("\"ipaddress\":\"192.168.1.100\"") != std::string::npos); + EXPECT_TRUE(text.find("\"status\":\"LOST\"") != std::string::npos); + onAddressChange.SetEvent(); + return Core::ERROR_NONE; + })); + + // Create and populate event data for IP address change + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t eventData{.interface = "eth0", + .ip_address = "192.168.1.100", + .is_ipv6 = false, + .acquired = false}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &eventData, sizeof(eventData)); + EXPECT_EQ(Core::ERROR_NONE, onAddressChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onIPAddressChange"), _T("org.rdk.NetworkManager"), message); +} + +TEST_F(NetworkManagerEventTest, onInterfaceStateChange2) +{ + Core::Event onInterfaceStateChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInterfaceStateChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(2) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_EQ(text, string(_T("{\"jsonrpc\":\"2.0\",\"method\":\"org.rdk.NetworkManager.onInterfaceStateChange\",\"params\":{\"state\":1,\"status\":\"INTERFACE_LINK_UP\",\"interface\":\"wlan0\"}}"))); + onInterfaceStateChange.SetEvent(); + return Core::ERROR_NONE; + })) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_EQ(text, string(_T("{\"jsonrpc\":\"2.0\",\"method\":\"org.rdk.NetworkManager.onInterfaceStateChange\",\"params\":{\"state\":2,\"status\":\"INTERFACE_LINK_DOWN\",\"interface\":\"eth0\"}}"))); + onInterfaceStateChange.SetEvent(); + return Core::ERROR_NONE; + })); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t eventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &eventData, sizeof(eventData)); + + eventData = { .interface = "eth0", .status = false }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &eventData, sizeof(eventData)); + + EXPECT_EQ(Core::ERROR_NONE, onInterfaceStateChange.Lock()); + sleep(5); + + EVENT_UNSUBSCRIBE(2, _T("onInterfaceStateChange"), _T("org.rdk.NetworkManager"), message); + +} +/* +TEST_F(NetworkManagerEventTest, onInternetStatusChange_CaptivePortal) +{ + Core::Event onInternetStatusChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(1) + .WillOnce(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onInternetStatusChange\"") != std::string::npos); + onInternetStatusChange.SetEvent(); + return Core::ERROR_NONE; + })); + + SimpleHttpServer server(8080); + server.setHttpResponseCode(302); // Found (Redirect) + server.setHttpMessage("http://captive.portal/login"); + server.start(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Allow server to start + + std::string request = _T("{\"endpoints\":[\"http://localhost:8080/generate_204\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "wlan0", + .ip_address = "fe80::1889:1e36:52a:a82f%26", + .is_ipv6 = true, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + + EXPECT_EQ(Core::ERROR_NONE, onInternetStatusChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + + // Test with both IPv4 and IPv6 + // EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{}"), response)); + // EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"wlan0\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetCaptivePortalURI"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"uri\":\"\",\"success\":true}")); + server.stop(); +} + +TEST_F(NetworkManagerEventTest, onInternetStatusChange_LimitedInternet) +{ + Core::Event onInternetStatusChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onInternetStatusChange\"") != std::string::npos); + onInternetStatusChange.SetEvent(); + return Core::ERROR_NONE; + })); + + SimpleHttpServer server(8080); + server.setHttpResponseCode(200); // OK + server.setHttpMessage(""); + server.start(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Allow server to start + + std::string request = _T("{\"endpoints\":[\"http://localhost:8080/generate_204\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "eth0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "eth0", + .ip_address = "192.168.148.1", + .is_ipv6 = false, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + + EXPECT_EQ(Core::ERROR_NONE, onInternetStatusChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Allow server to start + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"eth0\",\"connected\":false,\"state\":1,\"status\":\"LIMITED_INTERNET\",\"success\":true}")); + server.stop(); +} +*/ + +#ifdef USE_CONNECTIVITYCHECKMGR +TEST(NetworkManagerConnectivityClientTest, ClearsHandlerAfterAdmittedCallbackCompletes) +{ + NetworkManagerConnectivityClient client; + std::promise callbackEntered; + std::future callbackEnteredFuture = callbackEntered.get_future(); + std::promise allowCallbackCompletion; + std::shared_future allowCallbackCompletionFuture = allowCallbackCompletion.get_future().share(); + std::promise handlerCleared; + std::future handlerClearedFuture = handlerCleared.get_future(); + + client.SetInternetStatusChangeHandler( + [&callbackEntered, allowCallbackCompletionFuture](const Exchange::INetworkManager::InternetStatus, + const std::string&) { + callbackEntered.set_value(); + allowCallbackCompletionFuture.wait(); + }); + + std::thread notificationThread([&client] { + NetworkManagerConnectivityClientTestAccess::Notify( + client, Exchange::IConnectivityCheck::FULLY_CONNECTED, std::string()); + }); + EXPECT_EQ(std::future_status::ready, callbackEnteredFuture.wait_for(std::chrono::seconds(1))); + + std::thread clearThread([&client, &handlerCleared] { + client.SetInternetStatusChangeHandler(nullptr); + handlerCleared.set_value(); + }); + EXPECT_EQ(std::future_status::timeout, handlerClearedFuture.wait_for(std::chrono::milliseconds(0))); + + allowCallbackCompletion.set_value(); + notificationThread.join(); + clearThread.join(); + EXPECT_EQ(std::future_status::ready, handlerClearedFuture.wait_for(std::chrono::milliseconds(0))); +} + +TEST(NetworkManagerConnectivityClientTest, DoesNotInvokeHandlerAfterClear) +{ + NetworkManagerConnectivityClient client; + std::atomic callbackCount{0}; + + client.SetInternetStatusChangeHandler( + [&callbackCount](const Exchange::INetworkManager::InternetStatus, const std::string&) { + ++callbackCount; + }); + client.SetInternetStatusChangeHandler(nullptr); + + NetworkManagerConnectivityClientTestAccess::Notify( + client, Exchange::IConnectivityCheck::FULLY_CONNECTED, std::string()); + + EXPECT_EQ(0u, callbackCount.load()); +} +#endif +TEST_F(NetworkManagerEventTest, onInternetStatusChange_FULLY_CONNECTED) +{ + EXPECT_CALL(*p_curlWrapsImplMock, curl_multi_perform(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(CURLM_BAD_HANDLE)); + + Core::Event onInternetStatusChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onInternetStatusChange\"") != std::string::npos); + onInternetStatusChange.SetEvent(); + return Core::ERROR_NONE; + })); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "eth0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "eth0", + .ip_address = "192.168.148.1", + .is_ipv6 = false, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPrimaryInterface"), _T(""), response)); + EXPECT_EQ(response, _T("{\"interface\":\"eth0\",\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, onInternetStatusChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Allow server to start + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetCaptivePortalURI"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"uri\":\"\",\"success\":true}")); + +} + +TEST_F(NetworkManagerEventTest, GetPublicIP_SuccessWithEvent) +{ + EXPECT_CALL(*p_curlWrapsImplMock, curl_multi_perform(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(CURLM_BAD_HANDLE)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "wlan0", + .ip_address = "fe80::1889:1e36:52a:a82f%26", + .is_ipv6 = true, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPrimaryInterface"), _T(""), response)); + EXPECT_EQ(response, _T("{\"interface\":\"wlan0\",\"success\":true}")); + + // Test with both IPv4 and IPv6 + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetPublicIP"), _T("{}"), response)); + EXPECT_TRUE(response.find("\"ipaddress\"") != std::string::npos); + EXPECT_TRUE(response.find("\"ipversion\"") != std::string::npos); + EXPECT_TRUE(response.find("\"success\":true") != std::string::npos); +} + +TEST_F(NetworkManagerEventTest, GetSupportedSecurityModes) +{ + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("GetSupportedSecurityModes"), _T(""), response)); + EXPECT_EQ(response, _T("{\"security\":{\"NONE\":0,\"WPA_PSK\":1,\"SAE\":2,\"EAP\":3},\"success\":true}")); +} + +TEST_F(NetworkManagerEventTest, IsConnectedToInternet_Failed_CurlMultiPerform) +{ + EXPECT_CALL(*p_curlWrapsImplMock, curl_multi_perform(::testing::_, ::testing::_)) + .WillRepeatedly(::testing::Return(CURLM_BAD_HANDLE)); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); +} + + +TEST_F(NetworkManagerEventTest, IsConnectedToInternet_Failed) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Allow server to start + std::string request = _T("{\"endpoints\":[\"http://localhost:8077/generate_204\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Allow server to start + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "wlan0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "wlan0", + .ip_address = "fe80::1889:1e36:52a:a82f%26", + .is_ipv6 = true, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"wlan0\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); + + // Test with IPv4 only + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{\"ipversion\":\"IPv6\"}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv6\",\"interface\":\"wlan0\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); + + // Test with specific interface + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{\"interface\":\"eth0\"}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"eth0\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); + + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("IsConnectedToInternet"), _T("{\"interface\":\"wlan0\"}"), response)); + EXPECT_EQ(response, _T("{\"ipversion\":\"IPv4\",\"interface\":\"wlan0\",\"connected\":false,\"state\":0,\"status\":\"NO_INTERNET\",\"success\":true}")); + +} + +/* +TEST_F(NetworkManagerEventTest, FULLY_CONNECTED_wait) +{ + Core::Event onInternetStatusChange(false, true); + EVENT_SUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); + + EXPECT_CALL(service, Submit(::testing::_, ::testing::_)) + .Times(::testing::AnyNumber()) + .WillRepeatedly(::testing::Invoke( + [&](const uint32_t, const Core::ProxyType& json) { + string text; + EXPECT_TRUE(json->ToString(text)); + EXPECT_TRUE(text.find("\"method\":\"org.rdk.NetworkManager.onInternetStatusChange\"") != std::string::npos); + onInternetStatusChange.SetEvent(); + return Core::ERROR_NONE; + })); + + SimpleHttpServer server(8080); + server.setHttpResponseCode(204); // No Content + server.setHttpMessage(""); + server.start(); + + std::string request = _T("{\"endpoints\":[\"http://localhost:8080/generate_204\"]}"); + EXPECT_EQ(Core::ERROR_NONE, handler.Invoke(connection, _T("SetConnectivityTestEndpoints"), request, response)); + EXPECT_EQ(response, _T("{\"success\":true}")); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Allow server to start + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventData = { .interface = "eth0", .status = true }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventData, sizeof(ifaceEventData)); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceIPAddress_t ipEventData{.interface = "eth0", + .ip_address = "192.168.148.1", + .is_ipv6 = false, + .acquired = true}; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_IPADDRESS, &ipEventData, sizeof(ipEventData)); + std::this_thread::sleep_for(std::chrono::milliseconds(15000)); // Allow server to start + server.setTimeout(15000); // Set timeout to 15 seconds + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + server.stop(); + + IARM_BUS_NetSrvMgr_Iface_EventInterfaceConnectionStatus_t ifaceEventDataLost = { .interface = "eth0", .status = false }; + _nmEventHandler(IARM_BUS_NM_SRV_MGR_NAME, IARM_BUS_NETWORK_MANAGER_EVENT_INTERFACE_CONNECTION_STATUS, &ifaceEventDataLost, sizeof(ifaceEventDataLost)); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + + EXPECT_EQ(Core::ERROR_NONE, onInternetStatusChange.Lock()); + EVENT_UNSUBSCRIBE(2, _T("onInternetStatusChange"), _T("org.rdk.NetworkManager"), message); +} +*/ diff --git a/tests/l2Test/rdk/l2_test_rdkproxyImpl.cpp b/tests/l2Test/rdk/l2_test_rdkproxyImpl.cpp new file mode 100644 index 000000000..7158090d5 --- /dev/null +++ b/tests/l2Test/rdk/l2_test_rdkproxyImpl.cpp @@ -0,0 +1,423 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "FactoriesImplementation.h" +#include "ThunderPortability.h" + +#include +#include +#include +#include +#include "NetworkManagerImplementation.h" +#include "NetworkManager.h" +#include "IarmBusMock.h" +#include "ServiceMock.h" +#include "Module.h" + +#include +#include "ThunderPortability.h" +#include "INetworkManager.h" + +using namespace std; +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using IStringIterator = RPC::IIteratorType; +using ::testing::NiceMock; + +namespace { +class InternetStatusNotificationProbe : public Exchange::INetworkManager::INotification { +public: + void onInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, + const Exchange::INetworkManager::InternetStatus currState, + const string interface, + const string reason) override + { + std::lock_guard lock(mutex); + ++count; + lastPrev = prevState; + lastCurr = currState; + lastInterface = interface; + lastReason = reason; + cv.notify_all(); + } + + bool WaitForCount(const size_t expected, const std::chrono::milliseconds timeout) + { + std::unique_lock lock(mutex); + return cv.wait_for(lock, timeout, [&]() { return count >= expected; }); + } + + size_t Count() const + { + std::lock_guard lock(mutex); + return count; + } + + Exchange::INetworkManager::InternetStatus LastPrev() const + { + std::lock_guard lock(mutex); + return lastPrev; + } + + Exchange::INetworkManager::InternetStatus LastCurr() const + { + std::lock_guard lock(mutex); + return lastCurr; + } + + string LastInterface() const + { + std::lock_guard lock(mutex); + return lastInterface; + } + + string LastReason() const + { + std::lock_guard lock(mutex); + return lastReason; + } + + uint32_t AddRef() const override + { + return Core::ERROR_NONE; + } + + uint32_t Release() const override + { + return Core::ERROR_NONE; + } + + void* QueryInterface(const uint32_t id) override + { + if ((id == Exchange::INetworkManager::INotification::ID) || (id == Core::IUnknown::ID)) { + return static_cast(this); + } + return nullptr; + } + +private: + mutable std::mutex mutex; + std::condition_variable cv; + size_t count {0}; + Exchange::INetworkManager::InternetStatus lastPrev {Exchange::INetworkManager::INTERNET_UNKNOWN}; + Exchange::INetworkManager::InternetStatus lastCurr {Exchange::INetworkManager::INTERNET_UNKNOWN}; + string lastInterface; + string lastReason; +}; +} // namespace + +class NetworkManagerImplTest : public ::testing::Test { +protected: + static Core::ProxyType NetworkManagerImplementation; + static Exchange::INetworkManager* interface; + static IarmBusImplMock* p_iarmBusImplMock; + + static void SetUpTestSuite() { + // Create the mock implementation for IARM Bus + p_iarmBusImplMock = new NiceMock; + IarmBus::setImpl(p_iarmBusImplMock); + + // Create the NetworkManagerImplementation instance + NetworkManagerImplementation = Core::ProxyType::Create(); + interface = static_cast(NetworkManagerImplementation->QueryInterface(Exchange::INetworkManager::ID)); + ASSERT_TRUE(interface != nullptr); + } + + static void TearDownTestSuite() { + // Release the interface and clean up resources + interface->Release(); + IarmBus::setImpl(nullptr); + if (p_iarmBusImplMock != nullptr) { + delete p_iarmBusImplMock; + p_iarmBusImplMock = nullptr; + } + } + + virtual void SetUp() override { + ASSERT_TRUE(interface != nullptr); + } + + virtual void TearDown() override { + ASSERT_TRUE(interface != nullptr); + } +}; + +// Static member initialization +Core::ProxyType NetworkManagerImplTest::NetworkManagerImplementation; +Exchange::INetworkManager* NetworkManagerImplTest::interface = nullptr; +IarmBusImplMock* NetworkManagerImplTest::p_iarmBusImplMock = nullptr; + +TEST_F(NetworkManagerImplTest, SetLogLevel) { + // Test setting log level to FATAL + Exchange::INetworkManager::Logging logLevel = Exchange::INetworkManager::LOG_LEVEL_FATAL; + uint32_t result = interface->SetLogLevel(logLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + + // Test setting log level to ERROR + logLevel = Exchange::INetworkManager::LOG_LEVEL_ERROR; + result = interface->SetLogLevel(logLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + + // Test setting log level to WARNING + logLevel = Exchange::INetworkManager::LOG_LEVEL_WARNING; + result = interface->SetLogLevel(logLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + + // Test setting log level to INFO + logLevel = Exchange::INetworkManager::LOG_LEVEL_INFO; + result = interface->SetLogLevel(logLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + + // Test setting log level to DEBUG + logLevel = Exchange::INetworkManager::LOG_LEVEL_DEBUG; + result = interface->SetLogLevel(logLevel); + EXPECT_EQ(result, Core::ERROR_NONE); +} + +TEST_F(NetworkManagerImplTest, GetLogLevel) { + // Test getting log level after setting it to FATAL + Exchange::INetworkManager::Logging logLevel = Exchange::INetworkManager::LOG_LEVEL_FATAL; + interface->SetLogLevel(logLevel); + Exchange::INetworkManager::Logging retrievedLogLevel; + uint32_t result = interface->GetLogLevel(retrievedLogLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(retrievedLogLevel, Exchange::INetworkManager::LOG_LEVEL_FATAL); + + // Test getting log level after setting it to ERROR + logLevel = Exchange::INetworkManager::LOG_LEVEL_ERROR; + interface->SetLogLevel(logLevel); + result = interface->GetLogLevel(retrievedLogLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(retrievedLogLevel, Exchange::INetworkManager::LOG_LEVEL_ERROR); + + // Test getting log level after setting it to WARNING + logLevel = Exchange::INetworkManager::LOG_LEVEL_WARNING; + interface->SetLogLevel(logLevel); + result = interface->GetLogLevel(retrievedLogLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(retrievedLogLevel, Exchange::INetworkManager::LOG_LEVEL_WARNING); + + // Test getting log level after setting it to INFO + logLevel = Exchange::INetworkManager::LOG_LEVEL_INFO; + interface->SetLogLevel(logLevel); + result = interface->GetLogLevel(retrievedLogLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(retrievedLogLevel, Exchange::INetworkManager::LOG_LEVEL_INFO); + + // Test getting log level after setting it to DEBUG + logLevel = Exchange::INetworkManager::LOG_LEVEL_DEBUG; + interface->SetLogLevel(logLevel); + result = interface->GetLogLevel(retrievedLogLevel); + EXPECT_EQ(result, Core::ERROR_NONE); + EXPECT_EQ(retrievedLogLevel, Exchange::INetworkManager::LOG_LEVEL_DEBUG); +} + +TEST_F(NetworkManagerImplTest, LogMessageTruncation) { + // Generate a log message longer than the maximum allowed size + std::string longMessage(1025, 'A'); // Create a string with 1025 'A' characters + NetworkManagerLogger::logPrint(NetworkManagerLogger::DEBUG_LEVEL, __FILE__, __func__, __LINE__, "%s", longMessage.c_str()); + + // Since the log message is truncated, the last three characters should be "..." + // This test ensures that the truncation logic is executed + EXPECT_TRUE(true); // Placeholder assertion to ensure the test runs +} + +TEST_F(NetworkManagerImplTest, GetConnectivityTestEndpoints) +{ + // Set up mock endpoints in the ConnectivityMonitor + std::vector mockEndpoints = {"http://clients3.google.com/generate_204", "http://example.com"}; + NetworkManagerImplementation->connectivityMonitor.setConnectivityMonitorEndpoints(mockEndpoints); + + // Call GetConnectivityTestEndpoints + RPC::IIteratorType* endpoints = nullptr; + uint32_t result = interface->GetConnectivityTestEndpoints(endpoints); + + // Verify the result + EXPECT_EQ(result, Core::ERROR_NONE); + ASSERT_NE(endpoints, nullptr); + + // Verify the endpoints returned + std::vector retrievedEndpoints; + std::string endpoint; + while (endpoints->Next(endpoint)) { + retrievedEndpoints.push_back(endpoint); + } + EXPECT_EQ(retrievedEndpoints, mockEndpoints); + + // Clean up + endpoints->Release(); + // remove any previous connectivity endpoints file +} + +TEST_F(NetworkManagerImplTest, SetConnectivityTestEndpoints_EmptyEndpoints) { + // Prepare empty endpoints + std::vector emptyEndpoints; + RPC::IIteratorType* endpoints = Core::Service::Create(emptyEndpoints); + + // Call SetConnectivityTestEndpoints + uint32_t result = interface->SetConnectivityTestEndpoints(endpoints); + + // Verify the result + EXPECT_EQ(result, Core::ERROR_NONE); + + // Verify the endpoints were not set + std::vector retrievedEndpoints = NetworkManagerImplementation->connectivityMonitor.getConnectivityMonitorEndpoints(); + printf("Retrieved Endpoints Size: %zu %s\n", retrievedEndpoints.size(), retrievedEndpoints[0].c_str()); + EXPECT_TRUE(retrievedEndpoints.empty() != true && retrievedEndpoints[0] == "http://clients3.google.com/generate_204"); // default endpoint should remain + + // Clean up + endpoints->Release(); +} + +TEST_F(NetworkManagerImplTest, SetConnectivityTestEndpoints_ValidEndpoints) { + // Prepare valid endpoints + std::vector validEndpoints = {"http://clients3.google.com/generate_204", "http://example.com"}; + RPC::IIteratorType* endpoints = Core::Service::Create(validEndpoints); + + // Call SetConnectivityTestEndpoints + uint32_t result = interface->SetConnectivityTestEndpoints(endpoints); + + // Verify the result + EXPECT_EQ(result, Core::ERROR_NONE); + + // Verify the endpoints were set correctly + std::vector retrievedEndpoints = NetworkManagerImplementation->connectivityMonitor.getConnectivityMonitorEndpoints(); + EXPECT_EQ(retrievedEndpoints, validEndpoints); + + // Clean up + endpoints->Release(); + // remove any previous connectivity endpoints file +} + +TEST_F(NetworkManagerImplTest, SetConnectivityTestEndpoints_InvalidEndpoints) { + // Prepare invalid endpoints (too short to be valid URLs) + std::vector invalidEndpoints = {"1234567", "1234567890"}; // "1234567" is not a valid URL less than 7, "12345678" is too short + RPC::IIteratorType* endpoints = Core::Service::Create(invalidEndpoints); + + // Call SetConnectivityTestEndpoints + uint32_t result = interface->SetConnectivityTestEndpoints(endpoints); + + // Verify the result + EXPECT_EQ(result, Core::ERROR_NONE); + + // Verify the endpoints were not set + std::vector retrievedEndpoints = NetworkManagerImplementation->connectivityMonitor.getConnectivityMonitorEndpoints(); + EXPECT_TRUE(retrievedEndpoints.size() == 1 && retrievedEndpoints[0] == "1234567890"); + + // Clean up + endpoints->Release(); +} + +TEST_F(NetworkManagerImplTest, SetConnectivityTestEndpoints_TooManyEndpoints) { + + std::vector tooManyEndpoints; + for (int i = 0; i < 12; ++i) { + tooManyEndpoints.push_back("http://example.com/endpoint" + std::to_string(i)); + } + RPC::IIteratorType* endpoints = Core::Service::Create(tooManyEndpoints); + + // Call SetConnectivityTestEndpoints + uint32_t result = interface->SetConnectivityTestEndpoints(endpoints); + + // Verify the result + EXPECT_EQ(result, Core::ERROR_NONE); + + std::vector retrievedEndpoints = NetworkManagerImplementation->connectivityMonitor.getConnectivityMonitorEndpoints(); + EXPECT_EQ((int)retrievedEndpoints.size(), 12); + + // Clean up + endpoints->Release(); +} + +#ifdef USE_CONNECTIVITYCHECKMGR +TEST_F(NetworkManagerImplTest, DelegatedInternetStatusBridgePublishesAndDeduplicates) +{ + InternetStatusNotificationProbe notification; + ASSERT_EQ(interface->Register(¬ification), Core::ERROR_NONE); + + NetworkManagerImplementation->setDefaultInterface("eth0"); + NetworkManagerImplementation->OnDelegatedInternetStatusChange(Exchange::INetworkManager::INTERNET_LIMITED); + + EXPECT_TRUE(notification.WaitForCount(1, std::chrono::milliseconds(1000))); + EXPECT_EQ(notification.LastCurr(), Exchange::INetworkManager::INTERNET_LIMITED); + EXPECT_EQ(notification.LastInterface(), "eth0"); + + // Same state is deduplicated. + NetworkManagerImplementation->OnDelegatedInternetStatusChange(Exchange::INetworkManager::INTERNET_LIMITED); + EXPECT_EQ(notification.Count(), static_cast(1)); + + // New state transition is published. + NetworkManagerImplementation->OnDelegatedInternetStatusChange(Exchange::INetworkManager::INTERNET_FULLY_CONNECTED); + EXPECT_TRUE(notification.WaitForCount(2, std::chrono::milliseconds(1000))); + EXPECT_EQ(notification.LastPrev(), Exchange::INetworkManager::INTERNET_LIMITED); + EXPECT_EQ(notification.LastCurr(), Exchange::INetworkManager::INTERNET_FULLY_CONNECTED); + + EXPECT_EQ(interface->Unregister(¬ification), Core::ERROR_NONE); +} + +TEST_F(NetworkManagerImplTest, DelegatedNoInternetReasonIsPublishedOnlyForNoInternet) +{ + InternetStatusNotificationProbe notification; + ASSERT_EQ(interface->Register(¬ification), Core::ERROR_NONE); + + NetworkManagerImplementation->setDefaultInterface("eth0"); + NetworkManagerImplementation->OnDelegatedInternetStatusChange( + Exchange::INetworkManager::INTERNET_NOT_AVAILABLE, "PROBE_FAILED"); + + EXPECT_TRUE(notification.WaitForCount(1, std::chrono::milliseconds(1000))); + EXPECT_EQ(notification.LastCurr(), Exchange::INetworkManager::INTERNET_NOT_AVAILABLE); + EXPECT_EQ(notification.LastReason(), "PROBE_FAILED"); + + NetworkManagerImplementation->OnDelegatedInternetStatusChange( + Exchange::INetworkManager::INTERNET_FULLY_CONNECTED, "STALE_REASON"); + + EXPECT_TRUE(notification.WaitForCount(2, std::chrono::milliseconds(1000))); + EXPECT_EQ(notification.LastCurr(), Exchange::INetworkManager::INTERNET_FULLY_CONNECTED); + EXPECT_TRUE(notification.LastReason().empty()); + + EXPECT_EQ(interface->Unregister(¬ification), Core::ERROR_NONE); +} + +TEST_F(NetworkManagerImplTest, BuiltInMonitorInternetStatusEventOmitsReason) +{ + InternetStatusNotificationProbe notification; + ASSERT_EQ(interface->Register(¬ification), Core::ERROR_NONE); + + NetworkManagerImplementation->ReportInternetStatusChange( + Exchange::INetworkManager::INTERNET_UNKNOWN, + Exchange::INetworkManager::INTERNET_NOT_AVAILABLE, "eth0"); + + EXPECT_TRUE(notification.WaitForCount(1, std::chrono::milliseconds(1000))); + EXPECT_TRUE(notification.LastReason().empty()); + + EXPECT_EQ(interface->Unregister(¬ification), Core::ERROR_NONE); +} + +TEST_F(NetworkManagerImplTest, DelegatedInternetStatusBridgeIgnoredWhenDelegationDisabled) +{ + InternetStatusNotificationProbe notification; + ASSERT_EQ(interface->Register(¬ification), Core::ERROR_NONE); + + NetworkManagerImplementation->OnDelegatedInternetStatusChange(Exchange::INetworkManager::INTERNET_LIMITED); + EXPECT_FALSE(notification.WaitForCount(1, std::chrono::milliseconds(250))); + + EXPECT_EQ(interface->Unregister(¬ification), Core::ERROR_NONE); +} +#endif + diff --git a/tests/mocks/ConnectivityMockServer.h b/tests/mocks/ConnectivityMockServer.h new file mode 100644 index 000000000..1e5570176 --- /dev/null +++ b/tests/mocks/ConnectivityMockServer.h @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include + +class SimpleHttpServer { +public: + SimpleHttpServer(unsigned short port = 8080) + : m_port(port), m_running(false), m_httpResponseCode(204), m_httpMessage(""), m_timeoutMs(0) {} + + ~SimpleHttpServer() { + stop(); + } + + void setHttpResponseCode(int code) { + m_httpResponseCode = code; + } + + void setHttpMessage(const std::string& message) { + m_httpMessage = message; + } + + void setTimeout(unsigned int timeoutMs) { + m_timeoutMs = timeoutMs; + } + + void start() { + m_running = true; + m_serverThread = std::thread([this]() { run(); }); + } + + void stop() { + m_running = false; + if (m_serverThread.joinable()) { + m_serverThread.join(); + } + } + +private: + void run() { + int server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd == -1) { + std::cerr << "Failed to create socket" << std::endl; + return; + } + + // Set SO_REUSEADDR to allow immediate reuse of the port + int opt = 1; + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { + std::cerr << "Failed to set socket options" << std::endl; + close(server_fd); + return; + } + + sockaddr_in server_addr{}; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(m_port); + + if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { + std::cerr << "Failed to bind socket" << std::endl; + close(server_fd); + return; + } + + if (listen(server_fd, 5) < 0) { + std::cerr << "Failed to listen on socket" << std::endl; + close(server_fd); + return; + } + + std::cout << "Server running on http://localhost:" << m_port << "/generate_204" << std::endl; + + while (m_running) { + sockaddr_in client_addr{}; + socklen_t client_len = sizeof(client_addr); + int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len); + if (client_fd < 0) { + std::cerr << "Failed to accept connection" << std::endl; + continue; + } + + handleRequest(client_fd); + close(client_fd); + } + + close(server_fd); + } + + void handleRequest(int client_fd) { + // If timeout is set, simulate a timeout by sleeping + if (m_timeoutMs > 0) { + // Break timeout into 100ms chunks so we can check for stop conditions + const unsigned int chunkSize = 100; // 100ms chunks + unsigned int remaining = m_timeoutMs; + + while (m_running && remaining > 0) { + // Sleep for chunkSize or remaining time, whichever is smaller + unsigned int sleepTime = std::min(chunkSize, remaining); + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); + remaining -= sleepTime; + + // If stop() was called during the sleep, exit immediately + if (!m_running) { + break; + } + } + + // Return without sending a response to simulate timeout + return; + } + + char buffer[1024] = {0}; + ssize_t bytesRead = read(client_fd, buffer, sizeof(buffer)); + if (bytesRead < 0) { + std::cerr << "Error reading from client socket: " << strerror(errno) << std::endl; + return; + } + + std::string response; + + switch (m_httpResponseCode) { + case 204: // No Content + response = "HTTP/1.1 204 No Content\r\n"; + response += "Content-Length: 0\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + break; + + case 302: // Found (Redirect) + response = "HTTP/1.1 302 Found\r\n"; + response += "Content-Length: " + std::to_string(m_httpMessage.size()) + "\r\n"; + response += "Location: " + m_httpMessage + "\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + response += m_httpMessage; // Add the URI as the body + break; + + case 200: // OK + response = "HTTP/1.1 200 OK\r\n"; + response += "Content-Length: 0\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + response += m_httpMessage; + break; + + case 404: // Not Found + response = "HTTP/1.1 404 Not Found\r\n"; + response += "Content-Length: 0\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + break; + + case 511: // Authentication Required (Redirect as 302) + response = "HTTP/1.1 302 Found\r\n"; + response += "Content-Length: " + std::to_string(m_httpMessage.size()) + "\r\n"; + response += "Location: " + m_httpMessage + "\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + response += m_httpMessage; // Add the URI as the body + break; + + default: // Default case for unsupported codes + response = "HTTP/1.1 500 Internal Server Error\r\n"; + response += "Content-Length: 0\r\n"; + response += "Date: " + getCurrentDate() + "\r\n"; + response += "\r\n"; + break; + } + + send(client_fd, response.c_str(), response.size(), 0); + } + + std::string getCurrentDate() { + std::time_t now = std::time(nullptr); + char buf[100]; + std::strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", std::gmtime(&now)); + return std::string(buf); + } + + unsigned short m_port; + std::thread m_serverThread; + bool m_running; + int m_httpResponseCode; + std::string m_httpMessage; + unsigned int m_timeoutMs; +}; + +// int main() { +// SimpleHttpServer server(8080); + +// // Test all possible response codes +// std::vector> testCases = { +// {204, ""}, // No Content +// {302, "http://example.com/redirect"}, // Found (Redirect) +// {200, "OK"}, // OK +// {404, ""}, // Not Found +// {511, "http://captive.portal/login"} // Authentication Required (Redirect as 302) +// }; + +// for (const auto& testCase : testCases) { +// server.setHttpResponseCode(testCase.first); +// server.setHttpMessage(testCase.second); + +// std::cout << "Testing HTTP response code: " << testCase.first << std::endl; +// server.start(); + +// std::this_thread::sleep_for(std::chrono::seconds(5)); // Wait for 5 seconds to test +// server.stop(); +// std::this_thread::sleep_for(std::chrono::seconds(1)); +// } + +// // Test timeout functionality +// std::cout << "Testing timeout functionality..." << std::endl; +// server.setTimeout(5000); // Set a 5000ms timeout +// server.start(); +// std::this_thread::sleep_for(std::chrono::seconds(10)); // Wait longer than the timeout +// server.stop(); + +// return 0; +// } \ No newline at end of file diff --git a/tests/mocks/CurlWraps.cpp b/tests/mocks/CurlWraps.cpp new file mode 100644 index 000000000..6463f3981 --- /dev/null +++ b/tests/mocks/CurlWraps.cpp @@ -0,0 +1,54 @@ +#include "CurlWraps.h" +#include "CurlWrapsMock.h" +#include +#include + +extern "C" CURLMcode __wrap_curl_multi_perform(CURLM* multi_handle, int* still_running) +{ + return CurlWraps::getInstance().curl_multi_perform(multi_handle, still_running); +} + +extern "C" CURLMsg* __wrap_curl_multi_info_read(CURLM* multi_handle, int* msgs_left) +{ + return CurlWraps::getInstance().curl_multi_info_read(multi_handle, msgs_left); +} + +extern "C" CURLMcode __wrap_curl_multi_poll(CURLM* multi_handle, void* fds, unsigned int nfds, int timeout_ms, int* ret) +{ + return CurlWraps::getInstance().curl_multi_poll(multi_handle, fds, nfds, timeout_ms, ret); +} + +CurlWrapsImpl* CurlWraps::impl = nullptr; + +CurlWraps::CurlWraps() {} + +void CurlWraps::setImpl(CurlWrapsImpl* newImpl) +{ + EXPECT_TRUE((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} + +CurlWraps& CurlWraps::getInstance() +{ + static CurlWraps instance; + return instance; +} + +CURLMcode CurlWraps::curl_multi_perform(CURLM* multi_handle, int* still_running) +{ + EXPECT_NE(impl, nullptr); + return impl->curl_multi_perform(multi_handle, still_running); +} + +CURLMsg* CurlWraps::curl_multi_info_read(CURLM* multi_handle, int* msgs_left) +{ + EXPECT_NE(impl, nullptr); + return impl->curl_multi_info_read(multi_handle, msgs_left); +} + +CURLMcode CurlWraps::curl_multi_poll(CURLM* multi_handle, void* fds, unsigned int nfds, int timeout_ms, int* ret) +{ + EXPECT_NE(impl, nullptr); + return impl->curl_multi_poll(multi_handle, fds, nfds, timeout_ms, ret); +} + diff --git a/tests/mocks/CurlWraps.h b/tests/mocks/CurlWraps.h new file mode 100644 index 000000000..c303f453b --- /dev/null +++ b/tests/mocks/CurlWraps.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include +#include +#include + +class CurlWrapsImpl { +public: + virtual ~CurlWrapsImpl() = default; + + virtual CURLMcode curl_multi_perform(CURLM* multi_handle, int* still_running) = 0; + virtual CURLMsg* curl_multi_info_read(CURLM* multi_handle, int* msgs_left) = 0; + virtual CURLMcode curl_multi_poll(CURLM* multi_handle, void* fds, unsigned int nfds, int timeout_ms, int* ret) = 0; +}; + +class CurlWraps { +protected: + static CurlWrapsImpl* impl; + +public: + CurlWraps(); + CurlWraps(const CurlWraps &obj) = delete; + static void setImpl(CurlWrapsImpl* newImpl); + static CurlWraps& getInstance(); + + static CURLMcode curl_multi_perform(CURLM* multi_handle, int* still_running); + static CURLMsg* curl_multi_info_read(CURLM* multi_handle, int* msgs_left); + static CURLMcode curl_multi_poll(CURLM* multi_handle, void* fds, unsigned int nfds, int timeout_ms, int* ret); +}; diff --git a/tests/mocks/CurlWrapsMock.h b/tests/mocks/CurlWrapsMock.h new file mode 100644 index 000000000..38d893446 --- /dev/null +++ b/tests/mocks/CurlWrapsMock.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include + +#include "CurlWraps.h" + +class CurlWrapsImplMock : public CurlWrapsImpl { +public: + CurlWrapsImplMock():CurlWrapsImpl() {} + virtual ~CurlWrapsImplMock() = default; + + MOCK_METHOD(CURLMcode, curl_multi_perform, (CURLM* multi_handle, int* still_running), (override)); + MOCK_METHOD(CURLMsg*, curl_multi_info_read, (CURLM* multi_handle, int* msgs_left), (override)); + MOCK_METHOD(CURLMcode, curl_multi_poll, (CURLM* multi_handle, void* fds, unsigned int nfds, int timeout_ms, int* ret), (override)); +}; diff --git a/tests/mocks/GLibWraps.cpp b/tests/mocks/GLibWraps.cpp new file mode 100644 index 000000000..332c0a886 --- /dev/null +++ b/tests/mocks/GLibWraps.cpp @@ -0,0 +1,63 @@ +#include "GLibWraps.h" +#include + + +extern "C" gboolean __wrap_g_main_loop_is_running(GMainLoop* loop) { + return GLibWraps::getInstance().g_main_loop_is_running(loop); +} + +extern "C" gulong __wrap_g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags){ + return GLibWraps::getInstance().g_signal_connect_data(instance, detailed_signal, c_handler, data, destroy_data, connect_flags); +} + +extern "C" guint __wrap_g_signal_handlers_disconnect_by_data(gpointer instance, gpointer data) { + return GLibWraps::getInstance().signal_handlers_disconnect_by_data(instance, data); +} + +extern "C" guint __wrap_g_signal_handlers_disconnect_by_func(gpointer instance, gpointer func, gpointer data) { + return GLibWraps::getInstance().signal_handlers_disconnect_by_func(instance, func, data); +} + +GLibWrapsImpl* GLibWraps::impl = nullptr; +GLibWraps::GLibWraps() {} + +void GLibWraps::setImpl(GLibWrapsImpl* newImpl) { + EXPECT_TRUE((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} + +GLibWraps& GLibWraps::getInstance() { + static GLibWraps instance; + return instance; +} + +gboolean GLibWraps::g_main_loop_is_running(GMainLoop* loop) { + if (impl == nullptr) { + // During cleanup, impl may be nullptr, so call the actual GLib macro directly + return g_main_loop_is_running(loop); + } + return impl->g_main_loop_is_running(loop); +} + +gulong GLibWraps::g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) { + EXPECT_NE(impl, nullptr); + return impl->g_signal_connect_data(instance, detailed_signal, c_handler, data, destroy_data, connect_flags); +} + +guint GLibWraps::signal_handlers_disconnect_by_data(gpointer instance, gpointer data) { + if (impl == nullptr) { + // During cleanup, impl may be nullptr, so call the actual GLib macro directly + return g_signal_handlers_disconnect_by_data(instance, data); + } + return impl->signal_handlers_disconnect_by_data(instance, data); +} + +guint GLibWraps::signal_handlers_disconnect_by_func(gpointer instance, gpointer func, gpointer data) { + if (impl == nullptr) { + // During cleanup, impl may be nullptr, so call the actual GLib macro directly + return g_signal_handlers_disconnect_by_func(instance, func, data); + } + return impl->signal_handlers_disconnect_by_func(instance, func, data); +} diff --git a/tests/mocks/GLibWraps.h b/tests/mocks/GLibWraps.h new file mode 100644 index 000000000..cf59617c5 --- /dev/null +++ b/tests/mocks/GLibWraps.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +class GLibWrapsImpl { +public: + virtual ~GLibWrapsImpl() = default; + + virtual gboolean g_main_loop_is_running(GMainLoop* loop) = 0; + virtual gulong g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) = 0; + virtual guint signal_handlers_disconnect_by_data(gpointer instance, gpointer data) = 0; + virtual guint signal_handlers_disconnect_by_func(gpointer instance, gpointer func, gpointer data) = 0; +}; + +class GLibWraps { +protected: + static GLibWrapsImpl* impl; + +public: + GLibWraps(); + GLibWraps(const GLibWraps &obj) = delete; + static void setImpl(GLibWrapsImpl* newImpl); + static GLibWraps& getInstance(); + + static gboolean g_main_loop_is_running(GMainLoop* loop); + static gulong g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) __attribute__((visibility("default"))); + static guint signal_handlers_disconnect_by_data(gpointer instance, gpointer data) __attribute__((visibility("default"))); + static guint signal_handlers_disconnect_by_func(gpointer instance, gpointer func, gpointer data) __attribute__((visibility("default"))); +}; + +extern "C" { + gulong __wrap_g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) __attribute__((visibility("default"))); + guint __wrap_g_signal_handlers_disconnect_by_data(gpointer instance, gpointer data) __attribute__((visibility("default"))); + guint __wrap_g_signal_handlers_disconnect_by_func(gpointer instance, gpointer func, gpointer data) __attribute__((visibility("default"))); +} diff --git a/tests/mocks/GLibWrapsMock.h b/tests/mocks/GLibWrapsMock.h new file mode 100644 index 000000000..f1912cf97 --- /dev/null +++ b/tests/mocks/GLibWrapsMock.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include "GLibWraps.h" + +extern "C" gboolean __real_g_main_loop_is_running(GMainLoop* loop); +extern "C" gulong __real_g_signal_connect_data(gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags); + +class GLibWrapsImplMock : public GLibWrapsImpl { +public: + GLibWrapsImplMock() : GLibWrapsImpl() { + ON_CALL(*this, g_main_loop_is_running(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](GMainLoop* loop) -> gboolean { + return __real_g_main_loop_is_running(loop); + })); + + ON_CALL(*this, g_signal_connect_data(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags) -> gulong { + return __real_g_signal_connect_data(instance, detailed_signal, c_handler, data, destroy_data, connect_flags); + })); + } + + virtual ~GLibWrapsImplMock() = default; + + // Mock methods + MOCK_METHOD(gboolean, g_main_loop_is_running, (GMainLoop* loop), (override)); + MOCK_METHOD(gulong, g_signal_connect_data, (gpointer instance, const gchar *detailed_signal, GCallback c_handler, + gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags), (override)); + MOCK_METHOD(guint, signal_handlers_disconnect_by_data, (gpointer instance, gpointer data), (override)); + MOCK_METHOD(guint, signal_handlers_disconnect_by_func, (gpointer instance, gpointer func, gpointer data), (override)); +}; diff --git a/tests/mocks/INetworkManagerMock.h b/tests/mocks/INetworkManagerMock.h new file mode 100644 index 000000000..c7fd2408e --- /dev/null +++ b/tests/mocks/INetworkManagerMock.h @@ -0,0 +1,62 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "Module.h" + +class MockINetworkManager : public WPEFramework::Exchange::INetworkManager { +public: + MOCK_METHOD(uint32_t, GetAvailableInterfaces, (WPEFramework::Exchange::INetworkManager::IInterfaceDetailsIterator*& interfaces), (override)); + MOCK_METHOD(uint32_t, GetPrimaryInterface, (string& interface), (override)); MOCK_METHOD(uint32_t, SetInterfaceState, (const string& interface, const bool enabled), (override)); + MOCK_METHOD(uint32_t, GetInterfaceState, (const string& interface, bool& enabled), (override)); + MOCK_METHOD(uint32_t, GetIPSettings, (string& interface, const string& ipversion, IPAddress& address), (override)); + MOCK_METHOD(uint32_t, SetIPSettings, (const string& interface, const IPAddress& address), (override)); + MOCK_METHOD(uint32_t, GetStunEndpoint, (string& endpoint, uint32_t& port, uint32_t& timeout, uint32_t& cacheLifetime), (const)); + MOCK_METHOD(uint32_t, SetStunEndpoint, (string const endpoint, const uint32_t port, const uint32_t timeout, const uint32_t cacheLifetime), (override)); + MOCK_METHOD(uint32_t, GetConnectivityTestEndpoints, (IStringIterator*& endpoints), (const)); + MOCK_METHOD(uint32_t, SetConnectivityTestEndpoints, (IStringIterator* const endpoints), (override)); + MOCK_METHOD(uint32_t, IsConnectedToInternet, (string& ipversion, string& interface, WPEFramework::Exchange::INetworkManager::InternetStatus& status, string& reason), (override)); + MOCK_METHOD(uint32_t, GetCaptivePortalURI, (string& uri), (const)); + MOCK_METHOD(uint32_t, GetPublicIP, (string& interface, string& ipversion, string& ipaddress), (override)); + MOCK_METHOD(uint32_t, Ping, (const string ipversion, const string endpoint, const uint32_t count, const uint16_t timeout, const string guid, string& response), (override)); + MOCK_METHOD(uint32_t, Trace, (const string ipversion, const string endpoint, const uint32_t nqueries, const string guid, string& response), (override)); + MOCK_METHOD(uint32_t, StartWiFiScan, (IStringIterator* const frequencies, IStringIterator* const ssids), (override)); + MOCK_METHOD(uint32_t, StopWiFiScan, (), (override)); + MOCK_METHOD(uint32_t, GetKnownSSIDs, (IStringIterator*& ssids), (override)); + MOCK_METHOD(uint32_t, AddToKnownSSIDs, (const WiFiConnectTo& ssid), (override)); + MOCK_METHOD(uint32_t, RemoveKnownSSID, (const string& ssid), (override)); + MOCK_METHOD(uint32_t, ConnectToKnownSSID, (const string& ssid), (override)); + MOCK_METHOD(uint32_t, WiFiConnect, (const WiFiConnectTo& ssid), (override)); + MOCK_METHOD(uint32_t, WiFiDisconnect, (), (override)); + MOCK_METHOD(uint32_t, GetConnectedSSID, (WiFiSSIDInfo& ssidInfo), (override)); + MOCK_METHOD(uint32_t, StartWPS, (const WiFiWPS& method, const string& pin), (override)); + MOCK_METHOD(uint32_t, StopWPS, (), (override)); + MOCK_METHOD(uint32_t, GetWifiState, (WPEFramework::Exchange::INetworkManager::WiFiState& state), (override)); + MOCK_METHOD(uint32_t, GetWiFiSignalQuality, (string& ssid, int& strength, int& noise, int& snr, WPEFramework::Exchange::INetworkManager::WiFiSignalQuality& quality), (override)); + MOCK_METHOD(uint32_t, GetSupportedSecurityModes, (ISecurityModeIterator*& modes), (const)); + MOCK_METHOD(uint32_t, SetLogLevel, (const Logging& level), (override)); + MOCK_METHOD(uint32_t, SetHostname, (const string& hostname), (override)); + MOCK_METHOD(uint32_t, GetLogLevel, (Logging& level), (override)); + MOCK_METHOD(uint32_t, Configure, (const string configLine), (override)); + MOCK_METHOD(uint32_t, Register, (WPEFramework::Exchange::INetworkManager::INotification* notification), (override)); + MOCK_METHOD(uint32_t, Unregister, (WPEFramework::Exchange::INetworkManager::INotification* notification), (override)); + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); +}; diff --git a/tests/mocks/Iarm.cpp b/tests/mocks/Iarm.cpp new file mode 100644 index 000000000..63aa73353 --- /dev/null +++ b/tests/mocks/Iarm.cpp @@ -0,0 +1,121 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include "Iarm.h" +#include + +#ifdef ENABLE_TEST_LOG +#define TEST_LOG(x, ...) fprintf(stderr, "\033[1;32m[%s:%d](%s)" x "\n\033[0m", __FILE__, __LINE__, __FUNCTION__, getpid(), gettid(), ##__VA_ARGS__); fflush(stderr); +#else +#define TEST_LOG(x, ...) +#endif + +IarmBusImpl* IarmBus::impl = nullptr; + +IarmBus::IarmBus() {} + +void IarmBus::setImpl(IarmBusImpl* newImpl) +{ + // Handles both resetting 'impl' to nullptr and assigning a new value to 'impl' + EXPECT_TRUE ((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} + +IARM_Result_t IarmBus::IARM_Bus_Init(const char* name) +{ + TEST_LOG("Inside IARM_Bus_Init"); + + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_Init(name); +} + +IARM_Result_t IarmBus::IARM_Bus_Connect() +{ + TEST_LOG("Inside IARM_Bus_Connect"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_Connect(); +} + +IARM_Result_t IarmBus::IARM_Bus_IsConnected(const char* memberName, int* isRegistered) +{ + TEST_LOG("Inside IARM_Bus_IsConnected"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_IsConnected(memberName, isRegistered); +} + +IARM_Result_t IarmBus::IARM_Bus_RegisterEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + TEST_LOG("Inside IARM_Bus_RegisterEcnetHandler"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_RegisterEventHandler(ownerName, eventId, handler); +} + +IARM_Result_t IarmBus::IARM_Bus_UnRegisterEventHandler(const char* ownerName, IARM_EventId_t eventId) +{ + TEST_LOG("Inside IARM_Bus_UnRegisterEcnetHandler"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_UnRegisterEventHandler(ownerName, eventId); +} + +IARM_Result_t IarmBus::IARM_Bus_RemoveEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) +{ + TEST_LOG("Inside IARM_Bus_RemoveEventHandler"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_RemoveEventHandler(ownerName, eventId, handler); +} + +IARM_Result_t IarmBus::IARM_Bus_Call(const char* ownerName, const char* methodName, void* arg, size_t argLen) +{ + TEST_LOG("Inside IARM_Bus_Call"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_Call(ownerName, methodName, arg, argLen); +} + +IARM_Result_t IarmBus::IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) +{ + TEST_LOG("Inside IARM_Bus_BroadcastEvent"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_BroadcastEvent(ownerName, eventId, arg, argLen); +} + + +IARM_Result_t IarmBus::IARM_Bus_RegisterCall(const char* methodName, IARM_BusCall_t handler) +{ + TEST_LOG("Inside IARM_Bus_registerCall"); + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_RegisterCall(methodName, handler); +} + +IARM_Result_t IarmBus::IARM_Bus_Call_with_IPCTimeout(const char *ownerName, const char *methodName, void *arg, size_t argLen, int timeout) +{ + EXPECT_NE(impl, nullptr); + return impl->IARM_Bus_Call_with_IPCTimeout(ownerName, methodName, arg, argLen, timeout); +} + +IARM_Result_t (*IARM_Bus_Init)(const char*) = &IarmBus::IARM_Bus_Init; +IARM_Result_t (*IARM_Bus_Connect)() = &IarmBus::IARM_Bus_Connect; +IARM_Result_t (*IARM_Bus_IsConnected)(const char*,int*) = &IarmBus::IARM_Bus_IsConnected; +IARM_Result_t (*IARM_Bus_RegisterEventHandler)(const char*,IARM_EventId_t,IARM_EventHandler_t) = &IarmBus::IARM_Bus_RegisterEventHandler; +IARM_Result_t (*IARM_Bus_UnRegisterEventHandler)(const char*,IARM_EventId_t) = &IarmBus::IARM_Bus_UnRegisterEventHandler; +IARM_Result_t (*IARM_Bus_RemoveEventHandler)(const char*,IARM_EventId_t,IARM_EventHandler_t) = &IarmBus::IARM_Bus_RemoveEventHandler; +IARM_Result_t (*IARM_Bus_Call)(const char*,const char*,void*,size_t) = &IarmBus::IARM_Bus_Call; +IARM_Result_t (*IARM_Bus_BroadcastEvent)(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) = &IarmBus::IARM_Bus_BroadcastEvent; +IARM_Result_t (*IARM_Bus_RegisterCall)(const char*,IARM_BusCall_t) = &IarmBus::IARM_Bus_RegisterCall; +IARM_Result_t (*IARM_Bus_Call_with_IPCTimeout)(const char*,const char*,void*,size_t,int) = &IarmBus::IARM_Bus_Call_with_IPCTimeout; + diff --git a/tests/mocks/Iarm.h b/tests/mocks/Iarm.h new file mode 100644 index 000000000..3dd11b620 --- /dev/null +++ b/tests/mocks/Iarm.h @@ -0,0 +1,85 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include + +typedef int IARM_EventId_t; + +typedef enum _IARM_Result_t { + IARM_RESULT_SUCCESS, + IARM_RESULT_INVALID_PARAM, + IARM_RESULT_INVALID_STATE, + IARM_RESULT_IPCCORE_FAIL, + IARM_RESULT_OOM, +} IARM_Result_t; + +#define IARM_BUS_DAEMON_NAME "Daemon" + +typedef IARM_Result_t (*IARM_BusCall_t)(void* arg); +typedef void (*IARM_EventHandler_t)(const char* owner, IARM_EventId_t eventId, void* data, size_t len); + +class IarmBusImpl { +public: + virtual ~IarmBusImpl() = default; + + virtual IARM_Result_t IARM_Bus_Init(const char* name) = 0; + virtual IARM_Result_t IARM_Bus_Connect() = 0; + virtual IARM_Result_t IARM_Bus_IsConnected(const char* memberName, int* isRegistered) = 0; + virtual IARM_Result_t IARM_Bus_RegisterEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) = 0; + virtual IARM_Result_t IARM_Bus_UnRegisterEventHandler(const char* ownerName, IARM_EventId_t eventId) = 0; + virtual IARM_Result_t IARM_Bus_RemoveEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler) = 0; + virtual IARM_Result_t IARM_Bus_Call(const char* ownerName, const char* methodName, void* arg, size_t argLen) = 0; + virtual IARM_Result_t IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) = 0; + virtual IARM_Result_t IARM_Bus_RegisterCall(const char* methodName, IARM_BusCall_t handler) = 0; + virtual IARM_Result_t IARM_Bus_Call_with_IPCTimeout(const char *ownerName, const char *methodName, void *arg, size_t argLen, int timeout) = 0; +}; + +class IarmBus { +protected: + static IarmBusImpl* impl; +public: + IarmBus(); + IarmBus(const IarmBus &obj) = delete; // deleted copy constructor so that copy of the instance cannot be created. + static void setImpl(IarmBusImpl* newImpl); + static IARM_Result_t IARM_Bus_Init(const char* name); + static IARM_Result_t IARM_Bus_Connect(); + static IARM_Result_t IARM_Bus_IsConnected(const char* memberName, int* isRegistered); + static IARM_Result_t IARM_Bus_RegisterEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler); + static IARM_Result_t IARM_Bus_UnRegisterEventHandler(const char* ownerName, IARM_EventId_t eventId); + static IARM_Result_t IARM_Bus_RemoveEventHandler(const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler); + static IARM_Result_t IARM_Bus_Call(const char* ownerName, const char* methodName, void* arg, size_t argLen); + static IARM_Result_t IARM_Bus_BroadcastEvent(const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen) ; + + static IARM_Result_t IARM_Bus_RegisterCall(const char* methodName, IARM_BusCall_t handler); + static IARM_Result_t IARM_Bus_Call_with_IPCTimeout(const char *ownerName, const char *methodName, void *arg, size_t argLen, int timeout); +}; + + +extern IARM_Result_t (*IARM_Bus_Init)(const char*); +extern IARM_Result_t (*IARM_Bus_Connect)(); +extern IARM_Result_t (*IARM_Bus_IsConnected)(const char*,int*); +extern IARM_Result_t (*IARM_Bus_RegisterEventHandler)(const char*,IARM_EventId_t,IARM_EventHandler_t); +extern IARM_Result_t (*IARM_Bus_UnRegisterEventHandler)(const char*,IARM_EventId_t); +extern IARM_Result_t (*IARM_Bus_RemoveEventHandler)(const char*,IARM_EventId_t,IARM_EventHandler_t); +extern IARM_Result_t (*IARM_Bus_Call)(const char*,const char*,void*,size_t); +extern IARM_Result_t (*IARM_Bus_BroadcastEvent)(const char *,IARM_EventId_t,void *,size_t); +extern IARM_Result_t (*IARM_Bus_RegisterCall)(const char*,IARM_BusCall_t); +extern IARM_Result_t (*IARM_Bus_Call_with_IPCTimeout)(const char*,const char*,void*,size_t,int); diff --git a/tests/mocks/IarmBusMock.h b/tests/mocks/IarmBusMock.h new file mode 100644 index 000000000..5f344412c --- /dev/null +++ b/tests/mocks/IarmBusMock.h @@ -0,0 +1,80 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include +#include "libIBus.h" +#ifdef ENABLE_TEST_LOG +#define TEST_LOG(x, ...) fprintf(stderr, "\033[1;32m[%s:%d](%s)" x "\n\033[0m", __FILE__, __LINE__, __FUNCTION__, getpid(), gettid(), ##__VA_ARGS__); fflush(stderr); +#else +#define TEST_LOG(x, ...) +#endif +class IarmBusImplMock : public IarmBusImpl { +public: + IarmBusImplMock() + : IarmBusImpl() + { + // Defaults: + ON_CALL(*this, IARM_Bus_IsConnected(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [](const char*, int* isRegistered) { + *isRegistered = 1; + TEST_LOG("Mock IARM_Bus_IsConnected called"); + return IARM_RESULT_SUCCESS; + })); + + ON_CALL(*this, IARM_Bus_Init(::testing::_)) + .WillByDefault(::testing::Invoke( + [](const char* name) { + TEST_LOG("Mock IARM_Bus_Init called"); + return IARM_RESULT_SUCCESS; + })); + ON_CALL(*this, IARM_Bus_Connect()) + .WillByDefault(::testing::Invoke( + []() { + return IARM_RESULT_SUCCESS; + })); + ON_CALL(*this, IARM_Bus_RegisterEventHandler(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + ON_CALL(*this, IARM_Bus_UnRegisterEventHandler(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + ON_CALL(*this, IARM_Bus_Call(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + ON_CALL(*this, IARM_Bus_BroadcastEvent(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + ON_CALL(*this, IARM_Bus_RegisterCall(::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + ON_CALL(*this, IARM_Bus_Call_with_IPCTimeout(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Return(IARM_RESULT_SUCCESS)); + } + virtual ~IarmBusImplMock() = default; + + MOCK_METHOD(IARM_Result_t, IARM_Bus_Init, (const char* name), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_Connect, (), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_IsConnected, (const char* memberName, int* isRegistered), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_RegisterEventHandler, (const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_UnRegisterEventHandler, (const char* ownerName, IARM_EventId_t eventId), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_RemoveEventHandler, (const char* ownerName, IARM_EventId_t eventId, IARM_EventHandler_t handler), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_Call, (const char* ownerName, const char* methodName, void* arg, size_t argLen), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_RegisterCall, (const char* methodName, IARM_BusCall_t handler), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_BroadcastEvent, (const char *ownerName, IARM_EventId_t eventId, void *arg, size_t argLen), (override)); + MOCK_METHOD(IARM_Result_t, IARM_Bus_Call_with_IPCTimeout, (const char *ownerName, const char *methodName, void *arg, size_t argLen, int timeout), (override)); +}; + diff --git a/tests/mocks/LegacyNetworkMock.h b/tests/mocks/LegacyNetworkMock.h new file mode 100644 index 000000000..084690c8b --- /dev/null +++ b/tests/mocks/LegacyNetworkMock.h @@ -0,0 +1,65 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCKNETWORK_H +#define MOCKNETWORK_H + +#include +#include "Module.h" + +class StubNetwork : public WPEFramework::Plugin::Network { +public: + void onInterfaceStateChange(const JsonObject& parameters) { + WPEFramework::Plugin::Network::ReportonInterfaceStateChange(parameters); // Call the method on this object + } + + void onActiveInterfaceChange(const JsonObject& parameters) { + EXPECT_EQ(parameters["prevActiveInterface"].String(), "eth0"); + EXPECT_EQ(parameters["currentActiveInterface"].String(), "wlan0"); + WPEFramework::Plugin::Network::ReportonActiveInterfaceChange(parameters); + } + + void onIPAddressChange(const JsonObject& parameters) { + WPEFramework::Plugin::Network::ReportonIPAddressChange(parameters); + } + + void onInternetStatusChange(const JsonObject& parameters) { + EXPECT_EQ(parameters["state"].String(), "CONNECTED"); + EXPECT_EQ(parameters["status"].String(), "OK"); + WPEFramework::Plugin::Network::ReportonInternetStatusChange(parameters); + } + + string Information() const + { + WPEFramework::Plugin::Network::Information(); + return(string()); + } + + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); + MOCK_METHOD(const std::string, Initialize, (WPEFramework::PluginHost::IShell*), (override)); + MOCK_METHOD(void, Deinitialize, (WPEFramework::PluginHost::IShell*), (override)); + MOCK_METHOD(void, ReportonInterfaceStateChange, (const JsonObject&), ()); + MOCK_METHOD(void, ReportonActiveInterfaceChange, (const JsonObject&), ()); + MOCK_METHOD(void, ReportonIPAddressChange, (const JsonObject&), ()); + MOCK_METHOD(void, ReportonInternetStatusChange, (const JsonObject&), ()); + MOCK_METHOD(void, subscribeToEvents, (), ()); +}; +#endif diff --git a/tests/mocks/LegacyWiFiManagerMock.h b/tests/mocks/LegacyWiFiManagerMock.h new file mode 100644 index 000000000..5a44022a5 --- /dev/null +++ b/tests/mocks/LegacyWiFiManagerMock.h @@ -0,0 +1,54 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCKWIFIMANAGER_H +#define MOCKWIFIMANAGER_H + +#include +#include "Module.h" + +class StubWiFi : public WPEFramework::Plugin::WiFiManager { +public: + void onWiFiStateChange(const JsonObject& parameters) { + WPEFramework::Plugin::WiFiManager::onWiFiStateChange(parameters); // Call the method on this object + } + + void onAvailableSSIDs(const JsonObject& parameters) { + WPEFramework::Plugin::WiFiManager::onAvailableSSIDs(parameters); // Call the method on this object + } + + void onWiFiSignalQualityChange(const JsonObject& parameters) { + WPEFramework::Plugin::WiFiManager::onWiFiSignalQualityChange(parameters); // Call the method on this object + } + + string Information() const + { + WPEFramework::Plugin::WiFiManager::Information(); + return(string()); + } + + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); + MOCK_METHOD(const std::string, Initialize, (WPEFramework::PluginHost::IShell*), (override)); + MOCK_METHOD(void, Deinitialize, (WPEFramework::PluginHost::IShell*), (override)); + MOCK_METHOD(void, subscribeToEvents, (), ()); + MOCK_METHOD(uint32_t, cancelWPSPairing, (const JsonObject& parameters, JsonObject& response), ()); +}; +#endif diff --git a/tests/mocks/LibnmWraps.cpp b/tests/mocks/LibnmWraps.cpp new file mode 100644 index 000000000..3e039fd42 --- /dev/null +++ b/tests/mocks/LibnmWraps.cpp @@ -0,0 +1,743 @@ +#include "LibnmWraps.h" +#include +#include + +extern "C" const GPtrArray* __real_nm_client_get_devices(NMClient* client); + +extern "C" NMClient* __real_nm_client_new(GCancellable* cancellable, GError** error); + +extern "C" gint64 __wrap_nm_device_wifi_get_last_scan(NMDeviceWifi *device) { + return LibnmWraps::getInstance().nm_device_wifi_get_last_scan(device); +} + +extern "C" NMState __wrap_nm_client_get_state(NMClient *client) { + return LibnmWraps::getInstance().nm_client_get_state(client); +} + +extern "C" gboolean __wrap_nm_client_get_nm_running(NMClient *client) { + return LibnmWraps::getInstance().nm_client_get_nm_running(client); +} +extern "C" void __wrap_nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect) { + LibnmWraps::getInstance().nm_device_set_autoconnect(device, autoconnect); +} +extern "C" const GPtrArray* __wrap_nm_client_get_connections(NMClient *client) { + return LibnmWraps::getInstance().nm_client_get_connections(client); +} +extern "C" const char* __wrap_nm_connection_get_id(NMConnection *connection) { + return LibnmWraps::getInstance().nm_connection_get_id(connection); +} +extern "C" const char* __wrap_nm_connection_get_connection_type(NMConnection *connection) { + return LibnmWraps::getInstance().nm_connection_get_connection_type(connection); +} + +extern "C" void __wrap_nm_client_activate_connection_async(NMClient *client, NMConnection *connection, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + LibnmWraps::getInstance().nm_client_activate_connection_async(client, connection, device, specific_object, cancellable, callback, user_data); +} + +extern "C" NMActiveConnection* __wrap_nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) { + return LibnmWraps::getInstance().nm_client_activate_connection_finish(client, result, error); +} + +extern "C" void __wrap_nm_client_add_and_activate_connection_async(NMClient *client, NMConnection *partial, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + LibnmWraps::getInstance().nm_client_add_and_activate_connection_async(client, partial, device, specific_object, cancellable, callback, user_data); +} + +extern "C" NMActiveConnection* __wrap_nm_client_add_and_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) { + return LibnmWraps::getInstance().nm_client_add_and_activate_connection_finish(client, result, error); +} + +extern "C" GVariant* __wrap_nm_remote_connection_update2_finish(NMRemoteConnection *connection, GAsyncResult *result, GError **error) { + return LibnmWraps::getInstance().nm_remote_connection_update2_finish(connection, result, error); +} + +extern "C" void __wrap_nm_remote_connection_update2(NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + LibnmWraps::getInstance().nm_remote_connection_update2(connection, settings, flags, args, cancellable, callback, user_data); +} + +extern "C" void __wrap_nm_client_add_connection2(NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + LibnmWraps::getInstance().nm_client_add_connection2(client, settings, flags, args, ignore_out_result, cancellable, callback, user_data); +} + +extern "C" NMRemoteConnection* __wrap_nm_client_add_connection2_finish(NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) { + return LibnmWraps::getInstance().nm_client_add_connection2_finish(client, result, out_result, error); +} + +extern "C" const char* __wrap_nm_device_get_hw_address(NMDevice* device) { + return LibnmWraps::getInstance().nm_device_get_hw_address(device); +} + +extern "C" NMClient* __wrap_nm_client_new(GCancellable* cancellable, GError** error) { + return LibnmWraps::getInstance().nm_client_new(cancellable, error); +} + +extern "C" const char* __wrap_nm_device_get_iface(NMDevice* device) { + return LibnmWraps::getInstance().nm_device_get_iface(device); +} + +extern "C" NMActiveConnection* __wrap_nm_client_get_primary_connection(NMClient *client) { + return LibnmWraps::getInstance().nm_client_get_primary_connection(client); +} + +extern "C" NMActiveConnection* __wrap_nm_device_get_active_connection(NMDevice *device) { + return LibnmWraps::getInstance().nm_device_get_active_connection(device); +} + +extern "C" NMRemoteConnection* __wrap_nm_active_connection_get_connection(NMActiveConnection *connection) { + return LibnmWraps::getInstance().nm_active_connection_get_connection(connection); +} + +extern "C" const char* __wrap_nm_connection_get_interface_name(NMRemoteConnection *connection) { + return LibnmWraps::getInstance().nm_connection_get_interface_name(connection); +} + +extern "C" NMDevice* __wrap_nm_client_get_device_by_iface(NMClient *client, const char *iface) { + return LibnmWraps::getInstance().nm_client_get_device_by_iface(client, iface); +} + +extern "C" NMDeviceState __wrap_nm_device_get_state(NMDevice *device) { + return LibnmWraps::getInstance().nm_device_get_state(device); +} + +extern "C" void __wrap_nm_device_disconnect_async(NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + LibnmWraps::getInstance().nm_device_disconnect_async(device, cancellable, callback, user_data); +} + +extern "C" gboolean __wrap_nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error) { + return LibnmWraps::getInstance().nm_device_disconnect_finish(device, result, error); +} + +extern "C" const GPtrArray* __wrap_nm_client_get_devices(NMClient* client) { + return LibnmWraps::getInstance().nm_client_get_devices(client); +} + +extern "C" const GPtrArray* __wrap_nm_client_get_active_connections(NMClient* client) { + return LibnmWraps::getInstance().nm_client_get_active_connections(client); +} + +extern "C" NMSettingIPConfig* __wrap_nm_connection_get_setting_ip4_config(NMConnection* connection) { + return LibnmWraps::getInstance().nm_connection_get_setting_ip4_config(connection); +} + +extern "C" NMSettingIPConfig* __wrap_nm_connection_get_setting_ip6_config(NMConnection* connection) { + return LibnmWraps::getInstance().nm_connection_get_setting_ip6_config(connection); +} + +extern "C" const char* __wrap_nm_setting_ip_config_get_method(NMSettingIPConfig* setting) { + return LibnmWraps::getInstance().nm_setting_ip_config_get_method(setting); +} + +extern "C" NMSettingConnection* __wrap_nm_connection_get_setting_connection(NMConnection* connection) { + return LibnmWraps::getInstance().nm_connection_get_setting_connection(connection); +} + +extern "C" const char* __wrap_nm_setting_connection_get_interface_name(NMSettingConnection* setting) { + return LibnmWraps::getInstance().nm_setting_connection_get_interface_name(setting); +} + +extern "C" NMIPConfig* __wrap_nm_device_get_ip4_config(NMDevice* device) { + return LibnmWraps::getInstance().nm_device_get_ip4_config(device); +} + +extern "C" NMIPConfig* __wrap_nm_active_connection_get_ip4_config(NMActiveConnection* connection) { + return LibnmWraps::getInstance().nm_active_connection_get_ip4_config(connection); +} + +extern "C" NMIPConfig* __wrap_nm_active_connection_get_ip6_config(NMActiveConnection* connection) { + return LibnmWraps::getInstance().nm_active_connection_get_ip6_config(connection); +} + +extern "C" GPtrArray* __wrap_nm_ip_config_get_addresses(NMIPConfig* config) { + return LibnmWraps::getInstance().nm_ip_config_get_addresses(config); +} + +extern "C" const char* __wrap_nm_ip_address_get_address(NMIPAddress* address) { + return LibnmWraps::getInstance().nm_ip_address_get_address(address); +} + +extern "C" guint __wrap_nm_ip_address_get_prefix(NMIPAddress* address) { + return LibnmWraps::getInstance().nm_ip_address_get_prefix(address); +} + +extern "C" const char* __wrap_nm_ip_config_get_gateway(NMIPConfig* config) { + return LibnmWraps::getInstance().nm_ip_config_get_gateway(config); +} + +extern "C" const char* const* __wrap_nm_ip_config_get_nameservers(NMIPConfig* config) { + return LibnmWraps::getInstance().nm_ip_config_get_nameservers(config); +} + +extern "C" NMDhcpConfig* __wrap_nm_active_connection_get_dhcp4_config(NMActiveConnection* connection) { + return LibnmWraps::getInstance().nm_active_connection_get_dhcp4_config(connection); +} + +extern "C" NMDhcpConfig* __wrap_nm_active_connection_get_dhcp6_config(NMActiveConnection* connection) { + return LibnmWraps::getInstance().nm_active_connection_get_dhcp6_config(connection); +} + +extern "C" const char* __wrap_nm_dhcp_config_get_one_option(NMDhcpConfig* config, const char* option) { + return LibnmWraps::getInstance().nm_dhcp_config_get_one_option(config, option); +} + +// Access Point API wrappers +extern "C" NM80211ApFlags __wrap_nm_access_point_get_flags(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_flags(ap); +} + +extern "C" NM80211ApSecurityFlags __wrap_nm_access_point_get_wpa_flags(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_wpa_flags(ap); +} + +extern "C" NM80211ApSecurityFlags __wrap_nm_access_point_get_rsn_flags(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_rsn_flags(ap); +} + +extern "C" GBytes* __wrap_nm_access_point_get_ssid(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_ssid(ap); +} + +extern "C" const char* __wrap_nm_access_point_get_bssid(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_bssid(ap); +} + +extern "C" guint32 __wrap_nm_access_point_get_frequency(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_frequency(ap); +} + +extern "C" NM80211Mode __wrap_nm_access_point_get_mode(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_mode(ap); +} + +extern "C" guint32 __wrap_nm_access_point_get_max_bitrate(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_max_bitrate(ap); +} + +extern "C" guint8 __wrap_nm_access_point_get_strength(NMAccessPoint *ap) { + return LibnmWraps::getInstance().nm_access_point_get_strength(ap); +} + +extern "C" gboolean __wrap_nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection) { + return LibnmWraps::getInstance().nm_access_point_connection_valid(ap, connection); +} + +extern "C" NMAccessPoint* __wrap_nm_device_wifi_get_active_access_point(NMDeviceWifi *device) { + return LibnmWraps::getInstance().nm_device_wifi_get_active_access_point(device); +} + +extern "C" const GPtrArray* __wrap_nm_device_wifi_get_access_points(NMDeviceWifi *device) { + return LibnmWraps::getInstance().nm_device_wifi_get_access_points(device); +} + +extern "C" const GPtrArray* __wrap_nm_device_get_available_connections(NMDevice *device) { + return LibnmWraps::getInstance().nm_device_get_available_connections(device); +} + +extern "C" const char* __wrap_nm_object_get_path(NMObject *object) { + return LibnmWraps::getInstance().nm_object_get_path(object); +} + +extern "C" void __wrap_nm_client_dbus_set_property(NMClient *client, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + LibnmWraps::getInstance().nm_client_dbus_set_property(client, object_path, interface_name, property_name, value, timeout_msec, cancellable, callback, user_data); +} + +extern "C" gboolean __wrap_nm_client_dbus_set_property_finish(NMClient *client, + GAsyncResult *result, + GError **error) { + return LibnmWraps::getInstance().nm_client_dbus_set_property_finish(client, result, error); +} + +// New wrapper functions for commit changes and DHCP hostname +extern "C" gboolean __wrap_nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable *cancellable, + GError **error) { + return LibnmWraps::getInstance().nm_remote_connection_commit_changes(connection, save_to_disk, cancellable, error); +} + +extern "C" const char* __wrap_nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting) { + return LibnmWraps::getInstance().nm_setting_ip_config_get_dhcp_hostname(setting); +} + +extern "C" gboolean __wrap_nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting) { + return LibnmWraps::getInstance().nm_setting_ip_config_get_dhcp_send_hostname(setting); +} + +extern "C" void __wrap_nm_connection_add_setting(NMConnection *connection, NMSetting *setting) { + LibnmWraps::getInstance().nm_connection_add_setting(connection, setting); +} + +extern "C" NMSettingWireless* __wrap_nm_connection_get_setting_wireless(NMConnection *connection) { + return LibnmWraps::getInstance().nm_connection_get_setting_wireless(connection); +} + +extern "C" GBytes* __wrap_nm_setting_wireless_get_ssid(NMSettingWireless *setting) { + return LibnmWraps::getInstance().nm_setting_wireless_get_ssid(setting); +} + +extern "C" gboolean __wrap_nm_remote_connection_delete(NMRemoteConnection *connection, GCancellable *cancellable, GError **error) { + return LibnmWraps::getInstance().nm_remote_connection_delete(connection, cancellable, error); +} + +// WiFi Scan API wrappers +extern "C" void __wrap_nm_device_wifi_request_scan_async(NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + LibnmWraps::getInstance().nm_device_wifi_request_scan_async(device, cancellable, callback, user_data); +} + +extern "C" void __wrap_nm_device_wifi_request_scan_options_async(NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + LibnmWraps::getInstance().nm_device_wifi_request_scan_options_async(device, options, cancellable, callback, user_data); +} + +extern "C" gboolean __wrap_nm_device_wifi_request_scan_finish(NMDeviceWifi *device, + GAsyncResult *result, + GError **error) { + return LibnmWraps::getInstance().nm_device_wifi_request_scan_finish(device, result, error); +} + + +extern "C" const char* __wrap_nm_active_connection_get_id(NMActiveConnection *connection) { + return LibnmWraps::getInstance().nm_active_connection_get_id(connection); +} + +extern "C" const char* __wrap_nm_active_connection_get_connection_type(NMActiveConnection *connection) { + return LibnmWraps::getInstance().nm_active_connection_get_connection_type(connection); +} + +extern "C" NMDeviceStateReason __wrap_nm_device_get_state_reason(NMDevice *device) { + return LibnmWraps::getInstance().nm_device_get_state_reason(device); +} + +extern "C" int __wrap_nm_ip_address_get_family(NMIPAddress *address) { + return LibnmWraps::getInstance().nm_ip_address_get_family(address); +} + +extern "C" GVariant* __wrap_nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags) { + return LibnmWraps::getInstance().nm_connection_to_dbus(connection, flags); +} + +LibnmWrapsImpl* LibnmWraps::impl = nullptr; +LibnmWraps::LibnmWraps() {} + +void LibnmWraps::setImpl(LibnmWrapsImpl* newImpl) { + EXPECT_TRUE((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} + +LibnmWraps& LibnmWraps::getInstance() { + static LibnmWraps instance; + return instance; +} + +const char* LibnmWraps::nm_device_get_iface(NMDevice* device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_iface(device); +} + +NMActiveConnection* LibnmWraps::nm_client_get_primary_connection(NMClient *client) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_get_primary_connection(client); +} + +NMActiveConnection* LibnmWraps::nm_device_get_active_connection(NMDevice *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_active_connection(device); +} + +NMRemoteConnection* LibnmWraps::nm_active_connection_get_connection(NMActiveConnection *connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_connection(connection); +} + +const char* LibnmWraps::nm_connection_get_interface_name(NMRemoteConnection *connection) { // Changed to NMRemoteConnection* + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_interface_name(connection); +} + +NMDevice* LibnmWraps::nm_client_get_device_by_iface(NMClient *client, const char *iface) { + if (impl == nullptr) { + nm_client_get_device_by_iface(client, iface); + } + return impl->nm_client_get_device_by_iface(client, iface); +} + +NMDeviceState LibnmWraps::nm_device_get_state(NMDevice *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_state(device); +} + +void LibnmWraps::nm_device_disconnect_async(NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_device_disconnect_async(device, cancellable, callback, user_data); +} + +gboolean LibnmWraps::nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_disconnect_finish(device, result, error); +} + +const GPtrArray* LibnmWraps::nm_client_get_devices(NMClient* client) { + if (impl == nullptr) { + // During cleanup, impl may be nullptr, so call the real function directly + return __real_nm_client_get_devices(client); + } + return impl->nm_client_get_devices(client); +} + +const GPtrArray* LibnmWraps::nm_client_get_active_connections(NMClient* client) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_get_active_connections(client); +} + +NMClient* LibnmWraps::nm_client_new(GCancellable* cancellable, GError** error) { + if (impl == nullptr) { + // During cleanup, impl may be nullptr, so call the real function directly + return __real_nm_client_new(cancellable, error); + } + return impl->nm_client_new(cancellable, error); +} + +const char* LibnmWraps::nm_device_get_hw_address(NMDevice* device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_hw_address(device); +} + +NMSettingIPConfig* LibnmWraps::nm_connection_get_setting_ip4_config(NMConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_setting_ip4_config(connection); +} + +NMSettingIPConfig* LibnmWraps::nm_connection_get_setting_ip6_config(NMConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_setting_ip6_config(connection); +} + +const char* LibnmWraps::nm_setting_ip_config_get_method(NMSettingIPConfig* setting) { + EXPECT_NE(impl, nullptr); + return impl->nm_setting_ip_config_get_method(setting); +} + +NMSettingConnection* LibnmWraps::nm_connection_get_setting_connection(NMConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_setting_connection(connection); +} + +const char* LibnmWraps::nm_setting_connection_get_interface_name(NMSettingConnection* setting) { + EXPECT_NE(impl, nullptr); + return impl->nm_setting_connection_get_interface_name(setting); +} + +NMIPConfig* LibnmWraps::nm_device_get_ip4_config(NMDevice* device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_ip4_config(device); +} + +NMIPConfig* LibnmWraps::nm_active_connection_get_ip4_config(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_ip4_config(connection); +} + +NMIPConfig* LibnmWraps::nm_active_connection_get_ip6_config(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_ip6_config(connection); +} + +GPtrArray* LibnmWraps::nm_ip_config_get_addresses(NMIPConfig* config) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_config_get_addresses(config); +} + +const char* LibnmWraps::nm_ip_address_get_address(NMIPAddress* address) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_address_get_address(address); +} + +guint LibnmWraps::nm_ip_address_get_prefix(NMIPAddress* address) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_address_get_prefix(address); +} + +const char* LibnmWraps::nm_ip_config_get_gateway(NMIPConfig* config) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_config_get_gateway(config); +} + +const char* const* LibnmWraps::nm_ip_config_get_nameservers(NMIPConfig* config) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_config_get_nameservers(config); +} + +NMDhcpConfig* LibnmWraps::nm_active_connection_get_dhcp4_config(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_dhcp4_config(connection); +} + +NMDhcpConfig* LibnmWraps::nm_active_connection_get_dhcp6_config(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_dhcp6_config(connection); +} + +const char* LibnmWraps::nm_dhcp_config_get_one_option(NMDhcpConfig* config, const char* option) { + EXPECT_NE(impl, nullptr); + return impl->nm_dhcp_config_get_one_option(config, option); +} + +// Access Point API implementation +NM80211ApFlags LibnmWraps::nm_access_point_get_flags(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_flags(ap); +} + +NM80211ApSecurityFlags LibnmWraps::nm_access_point_get_wpa_flags(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_wpa_flags(ap); +} + +NM80211ApSecurityFlags LibnmWraps::nm_access_point_get_rsn_flags(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_rsn_flags(ap); +} + +GBytes* LibnmWraps::nm_access_point_get_ssid(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_ssid(ap); +} + +const char* LibnmWraps::nm_access_point_get_bssid(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_bssid(ap); +} + +guint32 LibnmWraps::nm_access_point_get_frequency(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_frequency(ap); +} + +NM80211Mode LibnmWraps::nm_access_point_get_mode(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_mode(ap); +} + +guint32 LibnmWraps::nm_access_point_get_max_bitrate(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_max_bitrate(ap); +} + +guint8 LibnmWraps::nm_access_point_get_strength(NMAccessPoint *ap) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_get_strength(ap); +} + +gboolean LibnmWraps::nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_access_point_connection_valid(ap, connection); +} + +NMAccessPoint* LibnmWraps::nm_device_wifi_get_active_access_point(NMDeviceWifi *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_wifi_get_active_access_point(device); +} + +const GPtrArray* LibnmWraps::nm_device_wifi_get_access_points(NMDeviceWifi *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_wifi_get_access_points(device); +} + +const GPtrArray* LibnmWraps::nm_device_get_available_connections(NMDevice *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_available_connections(device); +} + +const char* LibnmWraps::nm_object_get_path(NMObject *object) { + EXPECT_NE(impl, nullptr); + return impl->nm_object_get_path(object); +} + +void LibnmWraps::nm_client_dbus_set_property(NMClient *client, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_client_dbus_set_property(client, object_path, interface_name, property_name, value, timeout_msec, cancellable, callback, user_data); +} + +gboolean LibnmWraps::nm_client_dbus_set_property_finish(NMClient *client, + GAsyncResult *result, + GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_dbus_set_property_finish(client, result, error); +} + +// WiFi Scan API implementation +void LibnmWraps::nm_device_wifi_request_scan_async(NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_device_wifi_request_scan_async(device, cancellable, callback, user_data); +} + +void LibnmWraps::nm_device_wifi_request_scan_options_async(NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_device_wifi_request_scan_options_async(device, options, cancellable, callback, user_data); +} + +gboolean LibnmWraps::nm_device_wifi_request_scan_finish(NMDeviceWifi *device, + GAsyncResult *result, + GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_wifi_request_scan_finish(device, result, error); +} + +gint64 LibnmWraps::nm_device_wifi_get_last_scan(NMDeviceWifi *device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_wifi_get_last_scan(device); +} +void LibnmWraps::nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect) { + EXPECT_NE(impl, nullptr); + impl->nm_device_set_autoconnect(device, autoconnect); +} +const GPtrArray* LibnmWraps::nm_client_get_connections(NMClient *client) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_get_connections(client); +} +const char* LibnmWraps::nm_connection_get_id(NMConnection *connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_id(connection); +} +const char* LibnmWraps::nm_connection_get_connection_type(NMConnection *connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_connection_type(connection); +} + +void LibnmWraps::nm_client_activate_connection_async(NMClient *client, NMConnection *connection, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_client_activate_connection_async(client, connection, device, specific_object, cancellable, callback, user_data); +} + +NMActiveConnection* LibnmWraps::nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_activate_connection_finish(client, result, error); +} + +void LibnmWraps::nm_client_add_and_activate_connection_async(NMClient *client, NMConnection *partial, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_client_add_and_activate_connection_async(client, partial, device, specific_object, cancellable, callback, user_data); +} + +NMActiveConnection* LibnmWraps::nm_client_add_and_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_add_and_activate_connection_finish(client, result, error); +} + +GVariant* LibnmWraps::nm_remote_connection_update2_finish(NMRemoteConnection *connection, GAsyncResult *result, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_remote_connection_update2_finish(connection, result, error); +} + +void LibnmWraps::nm_remote_connection_update2(NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_remote_connection_update2(connection, settings, flags, args, cancellable, callback, user_data); +} + +void LibnmWraps::nm_client_add_connection2(NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + EXPECT_NE(impl, nullptr); + impl->nm_client_add_connection2(client, settings, flags, args, ignore_out_result, cancellable, callback, user_data); +} + +NMRemoteConnection* LibnmWraps::nm_client_add_connection2_finish(NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_add_connection2_finish(client, result, out_result, error); +} + +// New commit and DHCP hostname implementation +gboolean LibnmWraps::nm_remote_connection_commit_changes(NMRemoteConnection *connection, gboolean save_to_disk, GCancellable *cancellable, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_remote_connection_commit_changes(connection, save_to_disk, cancellable, error); +} + +const char* LibnmWraps::nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting) { + EXPECT_NE(impl, nullptr); + return impl->nm_setting_ip_config_get_dhcp_hostname(setting); +} + +gboolean LibnmWraps::nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting) { + EXPECT_NE(impl, nullptr); + return impl->nm_setting_ip_config_get_dhcp_send_hostname(setting); +} + +void LibnmWraps::nm_connection_add_setting(NMConnection *connection, NMSetting *setting) { + EXPECT_NE(impl, nullptr); + impl->nm_connection_add_setting(connection, setting); +} + +NMSettingWireless* LibnmWraps::nm_connection_get_setting_wireless(NMConnection *connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_get_setting_wireless(connection); +} + +GBytes* LibnmWraps::nm_setting_wireless_get_ssid(NMSettingWireless *setting) { + EXPECT_NE(impl, nullptr); + return impl->nm_setting_wireless_get_ssid(setting); +} + +gboolean LibnmWraps::nm_remote_connection_delete(NMRemoteConnection *connection, GCancellable *cancellable, GError **error) { + EXPECT_NE(impl, nullptr); + return impl->nm_remote_connection_delete(connection, cancellable, error); +} + +NMState LibnmWraps::nm_client_get_state(NMClient *client) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_get_state(client); +} + +gboolean LibnmWraps::nm_client_get_nm_running(NMClient *client) { + EXPECT_NE(impl, nullptr); + return impl->nm_client_get_nm_running(client); +} + +const char* LibnmWraps::nm_active_connection_get_id(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_id(connection); +} + +const char* LibnmWraps::nm_active_connection_get_connection_type(NMActiveConnection* connection) { + EXPECT_NE(impl, nullptr); + return impl->nm_active_connection_get_connection_type(connection); +} + +NMDeviceStateReason LibnmWraps::nm_device_get_state_reason(NMDevice* device) { + EXPECT_NE(impl, nullptr); + return impl->nm_device_get_state_reason(device); +} + +int LibnmWraps::nm_ip_address_get_family(NMIPAddress* address) { + EXPECT_NE(impl, nullptr); + return impl->nm_ip_address_get_family(address); +} + +GVariant* LibnmWraps::nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags) { + EXPECT_NE(impl, nullptr); + return impl->nm_connection_to_dbus(connection, flags); +} diff --git a/tests/mocks/LibnmWraps.h b/tests/mocks/LibnmWraps.h new file mode 100644 index 000000000..e492e64d2 --- /dev/null +++ b/tests/mocks/LibnmWraps.h @@ -0,0 +1,289 @@ +#pragma once + +#include +#include +#include +#include + +class LibnmWrapsImpl { +public: + virtual ~LibnmWrapsImpl() = default; + + // New APIs + virtual gint64 nm_device_wifi_get_last_scan(NMDeviceWifi *device) = 0; + virtual void nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect) = 0; + virtual const GPtrArray* nm_client_get_connections(NMClient *client) = 0; + virtual const char* nm_connection_get_id(NMConnection *connection) = 0; + virtual const char* nm_connection_get_connection_type(NMConnection *connection) = 0; + virtual const char* nm_device_get_iface(NMDevice* device) = 0; + virtual NMDevice* nm_client_get_device_by_iface(NMClient *client, const char *iface) = 0; + virtual NMDeviceState nm_device_get_state(NMDevice *device) = 0; + virtual void nm_device_disconnect_async(NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) = 0; + virtual gboolean nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error) = 0; + virtual NMActiveConnection* nm_client_get_primary_connection(NMClient *client) = 0; + virtual NMActiveConnection* nm_device_get_active_connection(NMDevice *device) = 0; + virtual NMRemoteConnection* nm_active_connection_get_connection(NMActiveConnection *connection) = 0; + virtual const char* nm_connection_get_interface_name(NMRemoteConnection *connection) = 0; + virtual NMClient* nm_client_new(GCancellable* cancellable, GError** error) = 0; + virtual const GPtrArray* nm_client_get_devices(NMClient* client) = 0; + virtual const char* nm_device_get_hw_address(NMDevice* device) = 0; + virtual const GPtrArray* nm_client_get_active_connections(NMClient* client) = 0; + virtual NMSettingIPConfig* nm_connection_get_setting_ip4_config(NMConnection* connection) = 0; + virtual NMSettingIPConfig* nm_connection_get_setting_ip6_config(NMConnection* connection) = 0; + virtual const char* nm_setting_ip_config_get_method(NMSettingIPConfig* setting) = 0; + virtual NMSettingConnection* nm_connection_get_setting_connection(NMConnection* connection) = 0; + virtual const char* nm_setting_connection_get_interface_name(NMSettingConnection* setting) = 0; + virtual NMIPConfig* nm_device_get_ip4_config(NMDevice* device) = 0; + virtual NMIPConfig* nm_active_connection_get_ip4_config(NMActiveConnection* connection) = 0; + virtual NMIPConfig* nm_active_connection_get_ip6_config(NMActiveConnection* connection) = 0; + virtual GPtrArray* nm_ip_config_get_addresses(NMIPConfig* config) = 0; + virtual const char* nm_ip_address_get_address(NMIPAddress* address) = 0; + virtual guint nm_ip_address_get_prefix(NMIPAddress* address) = 0; + virtual const char* nm_ip_config_get_gateway(NMIPConfig* config) = 0; + virtual const char* const* nm_ip_config_get_nameservers(NMIPConfig* config) = 0; + virtual NMDhcpConfig* nm_active_connection_get_dhcp4_config(NMActiveConnection* connection) = 0; + virtual NMDhcpConfig* nm_active_connection_get_dhcp6_config(NMActiveConnection* connection) = 0; + virtual const char* nm_dhcp_config_get_one_option(NMDhcpConfig* config, const char* option) = 0; + + // Access Point APIs + virtual NM80211ApFlags nm_access_point_get_flags(NMAccessPoint *ap) = 0; + virtual NM80211ApSecurityFlags nm_access_point_get_wpa_flags(NMAccessPoint *ap) = 0; + virtual NM80211ApSecurityFlags nm_access_point_get_rsn_flags(NMAccessPoint *ap) = 0; + virtual GBytes* nm_access_point_get_ssid(NMAccessPoint *ap) = 0; + virtual const char* nm_access_point_get_bssid(NMAccessPoint *ap) = 0; + virtual guint32 nm_access_point_get_frequency(NMAccessPoint *ap) = 0; + virtual NM80211Mode nm_access_point_get_mode(NMAccessPoint *ap) = 0; + virtual guint32 nm_access_point_get_max_bitrate(NMAccessPoint *ap) = 0; + virtual guint8 nm_access_point_get_strength(NMAccessPoint *ap) = 0; + virtual gboolean nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection) = 0; + virtual NMAccessPoint* nm_device_wifi_get_active_access_point(NMDeviceWifi *device) = 0; + virtual const GPtrArray* nm_device_wifi_get_access_points(NMDeviceWifi *device) = 0; + virtual const GPtrArray* nm_device_get_available_connections(NMDevice* device) = 0; + + // WiFi Scan APIs + virtual void nm_device_wifi_request_scan_async(NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual void nm_device_wifi_request_scan_options_async(NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual gboolean nm_device_wifi_request_scan_finish(NMDeviceWifi *device, + GAsyncResult *result, + GError **error) = 0; + virtual void nm_client_activate_connection_async(NMClient *client, NMConnection *connection, NMDevice *device, + const char *specific_object, GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data) = 0; + virtual NMActiveConnection *nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error) = 0; + virtual void nm_client_add_and_activate_connection_async(NMClient *client, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual NMActiveConnection *nm_client_add_and_activate_connection_finish(NMClient *client, + GAsyncResult *result, + GError **error) = 0; + virtual GVariant *nm_remote_connection_update2_finish(NMRemoteConnection *connection, + GAsyncResult *result, + GError **error) = 0; + virtual void nm_remote_connection_update2(NMRemoteConnection *connection, + GVariant *settings, + NMSettingsUpdate2Flags flags, + GVariant *args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual void nm_client_add_connection2(NMClient *client, + GVariant *settings, + NMSettingsAddConnection2Flags flags, + GVariant *args, + gboolean ignore_out_result, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual NMRemoteConnection *nm_client_add_connection2_finish(NMClient *client, + GAsyncResult *result, + GVariant **out_result, + GError **error) = 0; + virtual const char *nm_object_get_path(NMObject *object) = 0; + virtual void nm_client_dbus_set_property(NMClient *client, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) = 0; + virtual gboolean nm_client_dbus_set_property_finish(NMClient *client, + GAsyncResult *result, + GError **error) = 0; + + // New commit and DHCP hostname functions + virtual gboolean nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable *cancellable, + GError **error) = 0; + virtual const char *nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting) = 0; + virtual gboolean nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting) = 0; + virtual void nm_connection_add_setting(NMConnection *connection, NMSetting *setting) = 0; + virtual NMSettingWireless *nm_connection_get_setting_wireless(NMConnection *connection) = 0; + virtual GBytes *nm_setting_wireless_get_ssid(NMSettingWireless *setting) = 0; + virtual gboolean nm_remote_connection_delete(NMRemoteConnection *connection, GCancellable *cancellable, GError **error) = 0; + virtual NMState nm_client_get_state(NMClient *client) = 0; + virtual gboolean nm_client_get_nm_running(NMClient *client) = 0; + virtual const char* nm_active_connection_get_id(NMActiveConnection *connection) = 0; + virtual const char* nm_active_connection_get_connection_type(NMActiveConnection *connection) = 0; + virtual NMDeviceStateReason nm_device_get_state_reason(NMDevice *device) = 0; + virtual int nm_ip_address_get_family(NMIPAddress *address) = 0; + virtual GVariant* nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags) = 0; + + +}; + +class LibnmWraps { +protected: + static LibnmWrapsImpl* impl; + +public: + LibnmWraps(); + LibnmWraps(const LibnmWraps &obj) = delete; + static void setImpl(LibnmWrapsImpl* newImpl); + static LibnmWraps& getInstance(); + + static const char* nm_active_connection_get_id(NMActiveConnection *connection); + static const char* nm_active_connection_get_connection_type(NMActiveConnection *connection); + static NMDeviceStateReason nm_device_get_state_reason(NMDevice *device); + static int nm_ip_address_get_family(NMIPAddress *address); + static GVariant* nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags); + static NMState nm_client_get_state(NMClient *client); + static gboolean nm_client_get_nm_running(NMClient *client); + static gint64 nm_device_wifi_get_last_scan(NMDeviceWifi *device); + static void nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect); + static const GPtrArray* nm_client_get_connections(NMClient *client); + static const char* nm_connection_get_id(NMConnection *connection); + static const char* nm_connection_get_connection_type(NMConnection *connection); + static void nm_client_activate_connection_async(NMClient *client, NMConnection *connection, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); + static NMActiveConnection *nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error); + static void nm_client_add_and_activate_connection_async(NMClient *client, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static NMActiveConnection *nm_client_add_and_activate_connection_finish(NMClient *client, + GAsyncResult *result, + GError **error); + static GVariant *nm_remote_connection_update2_finish(NMRemoteConnection *connection, + GAsyncResult *result, + GError **error); + static void nm_remote_connection_update2(NMRemoteConnection *connection, + GVariant *settings, + NMSettingsUpdate2Flags flags, + GVariant *args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static void nm_client_add_connection2(NMClient *client, + GVariant *settings, + NMSettingsAddConnection2Flags flags, + GVariant *args, + gboolean ignore_out_result, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static NMRemoteConnection *nm_client_add_connection2_finish(NMClient *client, + GAsyncResult *result, + GVariant **out_result, + GError **error); + + // Access Point APIs + static const char* nm_device_get_iface(NMDevice* device); + static NMDevice* nm_client_get_device_by_iface(NMClient *client, const char *iface); + static NMDeviceState nm_device_get_state(NMDevice *device); + static void nm_device_disconnect_async(NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); + static gboolean nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error); + static NMActiveConnection* nm_client_get_primary_connection(NMClient *client); + static NMActiveConnection* nm_device_get_active_connection(NMDevice *device); + static NMRemoteConnection* nm_active_connection_get_connection(NMActiveConnection *connection); + static const char* nm_connection_get_interface_name(NMRemoteConnection *connection); + static NMClient* nm_client_new(GCancellable* cancellable, GError** error); + static const GPtrArray* nm_client_get_devices(NMClient* client); + static const char* nm_device_get_hw_address(NMDevice* device); + static const GPtrArray* nm_client_get_active_connections(NMClient* client); + static NMSettingIPConfig* nm_connection_get_setting_ip4_config(NMConnection* connection); + static NMSettingIPConfig* nm_connection_get_setting_ip6_config(NMConnection* connection); + static const char* nm_setting_ip_config_get_method(NMSettingIPConfig* setting); + static NMSettingConnection* nm_connection_get_setting_connection(NMConnection* connection); + static const char* nm_setting_connection_get_interface_name(NMSettingConnection* setting); + static NMIPConfig* nm_device_get_ip4_config(NMDevice* device); + static NMIPConfig* nm_active_connection_get_ip4_config(NMActiveConnection* connection); + static NMIPConfig* nm_active_connection_get_ip6_config(NMActiveConnection* connection); + static GPtrArray* nm_ip_config_get_addresses(NMIPConfig* config); + static const char* nm_ip_address_get_address(NMIPAddress* address); + static guint nm_ip_address_get_prefix(NMIPAddress* address); + static const char* nm_ip_config_get_gateway(NMIPConfig* config); + static const char* const* nm_ip_config_get_nameservers(NMIPConfig* config); + static NMDhcpConfig* nm_active_connection_get_dhcp4_config(NMActiveConnection* connection); + static NMDhcpConfig* nm_active_connection_get_dhcp6_config(NMActiveConnection* connection); + static const char* nm_dhcp_config_get_one_option(NMDhcpConfig* config, const char* option); + + // Access Point APIs + static NM80211ApFlags nm_access_point_get_flags(NMAccessPoint *ap); + static NM80211ApSecurityFlags nm_access_point_get_wpa_flags(NMAccessPoint *ap); + static NM80211ApSecurityFlags nm_access_point_get_rsn_flags(NMAccessPoint *ap); + static GBytes* nm_access_point_get_ssid(NMAccessPoint *ap); + static const char* nm_access_point_get_bssid(NMAccessPoint *ap); + static guint32 nm_access_point_get_frequency(NMAccessPoint *ap); + static NM80211Mode nm_access_point_get_mode(NMAccessPoint *ap); + static guint32 nm_access_point_get_max_bitrate(NMAccessPoint *ap); + static guint8 nm_access_point_get_strength(NMAccessPoint *ap); + static gboolean nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection); + static NMAccessPoint* nm_device_wifi_get_active_access_point(NMDeviceWifi *device); + static const GPtrArray* nm_device_wifi_get_access_points(NMDeviceWifi *device); + static const GPtrArray* nm_device_get_available_connections(NMDevice* device); + + // WiFi Scan APIs + static void nm_device_wifi_request_scan_async(NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static void nm_device_wifi_request_scan_options_async(NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static gboolean nm_device_wifi_request_scan_finish(NMDeviceWifi *device, + GAsyncResult *result, + GError **error); + static const char *nm_object_get_path(NMObject *object); + static void nm_client_dbus_set_property(NMClient *client, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + static gboolean nm_client_dbus_set_property_finish(NMClient *client, + GAsyncResult *result, + GError **error); + + // New commit and DHCP hostname functions + static gboolean nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable *cancellable, + GError **error); + static const char *nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting); + static gboolean nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting); + static void nm_connection_add_setting(NMConnection *connection, NMSetting *setting); + static NMSettingWireless *nm_connection_get_setting_wireless(NMConnection *connection); + static GBytes *nm_setting_wireless_get_ssid(NMSettingWireless *setting); + static gboolean nm_remote_connection_delete(NMRemoteConnection *connection, GCancellable *cancellable, GError **error); +}; diff --git a/tests/mocks/LibnmWrapsMock.h b/tests/mocks/LibnmWrapsMock.h new file mode 100644 index 000000000..5fb09e694 --- /dev/null +++ b/tests/mocks/LibnmWrapsMock.h @@ -0,0 +1,596 @@ +#pragma once + +#include +#include +#include "LibnmWraps.h" +#include + +extern "C" const char* __real_nm_active_connection_get_id(NMActiveConnection *connection); +extern "C" const char* __real_nm_active_connection_get_connection_type(NMActiveConnection *connection); +extern "C" NMDeviceStateReason __real_nm_device_get_state_reason(NMDevice *device); +extern "C" int __real_nm_ip_address_get_family(NMIPAddress *address); +extern "C" const char* __real_nm_device_get_iface(NMDevice* device); +extern "C" gboolean __real_nm_remote_connection_delete(NMRemoteConnection *connection, GCancellable *cancellable, GError **error); +extern "C" NMActiveConnection* __real_nm_client_get_primary_connection(NMClient *client); +extern "C" NMActiveConnection* __real_nm_device_get_active_connection(NMDevice *device); +extern "C" NMRemoteConnection* __real_nm_active_connection_get_connection(NMActiveConnection *connection); +extern "C" const char* __real_nm_connection_get_interface_name(NMRemoteConnection *connection); +extern "C" NMDevice* __real_nm_client_get_device_by_iface(NMClient *client, const char *iface); +extern "C" NMDeviceState __real_nm_device_get_state(NMDevice *device); +extern "C" void __real_nm_device_disconnect_async(NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); +extern "C" gboolean __real_nm_device_disconnect_finish(NMDevice *device, GAsyncResult *result, GError **error); +extern "C" const GPtrArray* __real_nm_client_get_devices(NMClient* client); +extern "C" NMClient* __real_nm_client_new(GCancellable* cancellable, GError** error); +extern "C" const char* __real_nm_device_get_hw_address(NMDevice* device); +extern "C" const GPtrArray* __real_nm_client_get_active_connections(NMClient* client); +extern "C" NMSettingIPConfig* __real_nm_connection_get_setting_ip4_config(NMConnection* connection); +extern "C" NMSettingIPConfig* __real_nm_connection_get_setting_ip6_config(NMConnection* connection); +extern "C" const char* __real_nm_setting_ip_config_get_method(NMSettingIPConfig* setting); +extern "C" NMSettingConnection* __real_nm_connection_get_setting_connection(NMConnection* connection); +extern "C" const char* __real_nm_setting_connection_get_interface_name(NMSettingConnection* setting); +extern "C" NMIPConfig* __real_nm_device_get_ip4_config(NMDevice* device); +extern "C" NMIPConfig* __real_nm_active_connection_get_ip4_config(NMActiveConnection* connection); +extern "C" NMIPConfig* __real_nm_active_connection_get_ip6_config(NMActiveConnection* connection); +extern "C" GPtrArray* __real_nm_ip_config_get_addresses(NMIPConfig* config); +extern "C" const char* __real_nm_ip_address_get_address(NMIPAddress* address); +extern "C" guint __real_nm_ip_address_get_prefix(NMIPAddress* address); +extern "C" const char* __real_nm_ip_config_get_gateway(NMIPConfig* config); +extern "C" const char* const* __real_nm_ip_config_get_nameservers(NMIPConfig* config); +extern "C" NMDhcpConfig* __real_nm_active_connection_get_dhcp4_config(NMActiveConnection* connection); +extern "C" NMDhcpConfig* __real_nm_active_connection_get_dhcp6_config(NMActiveConnection* connection); +extern "C" const char* __real_nm_dhcp_config_get_one_option(NMDhcpConfig* config, const char* option); + +extern "C" NM80211ApFlags __real_nm_access_point_get_flags(NMAccessPoint *ap); +extern "C" NM80211ApSecurityFlags __real_nm_access_point_get_wpa_flags(NMAccessPoint *ap); +extern "C" NM80211ApSecurityFlags __real_nm_access_point_get_rsn_flags(NMAccessPoint *ap); +extern "C" GBytes* __real_nm_access_point_get_ssid(NMAccessPoint *ap); +extern "C" const char* __real_nm_access_point_get_bssid(NMAccessPoint *ap); +extern "C" guint32 __real_nm_access_point_get_frequency(NMAccessPoint *ap); +extern "C" NM80211Mode __real_nm_access_point_get_mode(NMAccessPoint *ap); +extern "C" guint32 __real_nm_access_point_get_max_bitrate(NMAccessPoint *ap); +extern "C" guint8 __real_nm_access_point_get_strength(NMAccessPoint *ap); +extern "C" gboolean __real_nm_access_point_connection_valid(NMAccessPoint *ap, NMConnection *connection); +extern "C" NMAccessPoint* __real_nm_device_wifi_get_active_access_point(NMDeviceWifi *device); + +extern "C" void __real_nm_device_wifi_request_scan_async(NMDeviceWifi *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" void __real_nm_device_wifi_request_scan_options_async(NMDeviceWifi *device, + GVariant *options, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" gboolean __real_nm_device_wifi_request_scan_finish(NMDeviceWifi *device, + GAsyncResult *result, + GError **error); +extern "C" const GPtrArray *__real_nm_device_wifi_get_access_points(NMDeviceWifi *device); +extern "C" const GPtrArray *__real_nm_device_get_available_connections(NMDevice *device); + +extern "C" gint64 __real_nm_device_wifi_get_last_scan(NMDeviceWifi *device); +extern "C" void __real_nm_device_set_autoconnect(NMDevice *device, gboolean autoconnect); +extern "C" const GPtrArray* __real_nm_client_get_connections(NMClient *client); +extern "C" const char* __real_nm_connection_get_id(NMConnection *connection); +extern "C" const char* __real_nm_connection_get_connection_type(NMConnection *connection); +extern "C" void __real_nm_client_activate_connection_async(NMClient *client, + NMConnection *connection, + NMDevice *device, + const char *specific_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" NMActiveConnection* __real_nm_client_activate_connection_finish(NMClient *client, GAsyncResult *result, GError **error); +extern "C" void __real_nm_client_add_and_activate_connection_async(NMClient *client, + NMConnection *partial, + NMDevice *device, + const char *specific_object, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" NMActiveConnection* __real_nm_client_add_and_activate_connection_finish(NMClient *client, + GAsyncResult *result, + GError **error); +extern "C" GVariant* __real_nm_remote_connection_update2_finish(NMRemoteConnection *connection, + GAsyncResult *result, + GError **error); +extern "C" void __real_nm_remote_connection_update2(NMRemoteConnection *connection, + GVariant *settings, + NMSettingsUpdate2Flags flags, + GVariant *args, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" void __real_nm_client_add_connection2(NMClient *client, + GVariant *settings, + NMSettingsAddConnection2Flags flags, + GVariant *args, + gboolean ignore_out_result, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" NMRemoteConnection* __real_nm_client_add_connection2_finish(NMClient *client, + GAsyncResult *result, + GVariant **out_result, + GError **error); +extern "C" const char* __real_nm_object_get_path(NMObject *object); +extern "C" void __real_nm_client_dbus_set_property(NMClient *client, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + int timeout_msec, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +extern "C" gboolean __real_nm_client_dbus_set_property_finish(NMClient *client, + GAsyncResult *result, + GError **error); + +extern "C" gboolean __real_nm_remote_connection_commit_changes(NMRemoteConnection *connection, + gboolean save_to_disk, + GCancellable *cancellable, + GError **error); +extern "C" const char* __real_nm_setting_ip_config_get_dhcp_hostname(NMSettingIPConfig *setting); +extern "C" gboolean __real_nm_setting_ip_config_get_dhcp_send_hostname(NMSettingIPConfig *setting); +extern "C" void __real_nm_connection_add_setting(NMConnection *connection, NMSetting *setting); +extern "C" NMSettingWireless *__real_nm_connection_get_setting_wireless(NMConnection *connection); +extern "C" GBytes *__real_nm_setting_wireless_get_ssid(NMSettingWireless *setting); +extern "C" NMState __real_nm_client_get_state(NMClient *client); +extern "C" gboolean __real_nm_client_get_nm_running(NMClient *client); +extern "C" GVariant* __real_nm_connection_to_dbus(NMConnection *connection, NMConnectionSerializationFlags flags); + + +class LibnmWrapsImplMock : public LibnmWrapsImpl { +public: + LibnmWrapsImplMock() : LibnmWrapsImpl() { + + ON_CALL(*this, nm_active_connection_get_id(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> const char* { + return __real_nm_active_connection_get_id(connection); + })); + + ON_CALL(*this, nm_active_connection_get_connection_type(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> const char* { + return __real_nm_active_connection_get_connection_type(connection); + })); + + ON_CALL(*this, nm_device_get_state_reason(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device) -> NMDeviceStateReason { + return __real_nm_device_get_state_reason(device); + })); + + ON_CALL(*this, nm_ip_address_get_family(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPAddress* address) -> int { + return __real_nm_ip_address_get_family(address); + })); + + ON_CALL(*this, nm_connection_to_dbus(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection, NMConnectionSerializationFlags flags) -> GVariant* { + return __real_nm_connection_to_dbus(connection, flags); + })); + ON_CALL(*this, nm_device_wifi_get_last_scan(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi* device) -> gint64 { + return __real_nm_device_wifi_get_last_scan(device); + })); + ON_CALL(*this, nm_device_set_autoconnect(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device, gboolean autoconnect) { + __real_nm_device_set_autoconnect(device, autoconnect); + })); + ON_CALL(*this, nm_client_get_connections(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> const GPtrArray* { + return __real_nm_client_get_connections(client); + })); + ON_CALL(*this, nm_connection_get_id(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection) -> const char* { + return __real_nm_connection_get_id(connection); + })); + ON_CALL(*this, nm_connection_get_connection_type(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection) -> const char* { + return __real_nm_connection_get_connection_type(connection); + })); + ON_CALL(*this, nm_client_activate_connection_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, NMConnection* connection, NMDevice* device, const char* specific_object, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_client_activate_connection_async(client, connection, device, specific_object, cancellable, callback, user_data); + })); + ON_CALL(*this, nm_client_activate_connection_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, GAsyncResult* result, GError** error) -> NMActiveConnection* { + return __real_nm_client_activate_connection_finish(client, result, error); + })); + ON_CALL(*this, nm_client_add_and_activate_connection_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, NMConnection* partial, NMDevice* device, const char* specific_object, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_client_add_and_activate_connection_async(client, partial, device, specific_object, cancellable, callback, user_data); + })); + ON_CALL(*this, nm_client_add_and_activate_connection_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, GAsyncResult* result, GError** error) -> NMActiveConnection* { + return __real_nm_client_add_and_activate_connection_finish(client, result, error); + })); + ON_CALL(*this, nm_remote_connection_update2_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMRemoteConnection* connection, GAsyncResult* result, GError** error) -> GVariant* { + return __real_nm_remote_connection_update2_finish(connection, result, error); + })); + + ON_CALL(*this, nm_remote_connection_update2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMRemoteConnection* connection, GVariant* settings, NMSettingsUpdate2Flags flags, GVariant* args, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_remote_connection_update2(connection, settings, flags, args, cancellable, callback, user_data); + })); + ON_CALL(*this, nm_client_add_connection2(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, GVariant* settings, NMSettingsAddConnection2Flags flags, GVariant* args, gboolean ignore_out_result, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_client_add_connection2(client, settings, flags, args, ignore_out_result, cancellable, callback, user_data); + })); + ON_CALL(*this, nm_client_add_connection2_finish(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, GAsyncResult* result, GVariant** out_result, GError** error) -> NMRemoteConnection* { + return __real_nm_client_add_connection2_finish(client, result, out_result, error); + })); + + ON_CALL(*this, nm_device_get_iface(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device) -> const char* { + return __real_nm_device_get_iface(device); + })); + ON_CALL(*this, nm_client_get_device_by_iface(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client, const char* iface) -> NMDevice* { + return __real_nm_client_get_device_by_iface(client, iface); + })); + ON_CALL(*this, nm_device_get_state(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device) -> NMDeviceState { + return __real_nm_device_get_state(device); + })); + ON_CALL(*this, nm_device_disconnect_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer user_data) { + return __real_nm_device_disconnect_async(device, cancellable, callback, user_data); + })); + + ON_CALL(*this, nm_device_disconnect_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device, GAsyncResult* result, GError** error) -> gboolean { + return __real_nm_device_disconnect_finish(device, result, error); + })); + ON_CALL(*this, nm_client_get_primary_connection(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> NMActiveConnection* { + return __real_nm_client_get_primary_connection(client); + })); + ON_CALL(*this, nm_device_get_active_connection(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device) -> NMActiveConnection* { + return __real_nm_device_get_active_connection(device); + })); + ON_CALL(*this, nm_active_connection_get_connection(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> NMRemoteConnection* { + return __real_nm_active_connection_get_connection(connection); + })); + ON_CALL(*this, nm_connection_get_interface_name(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMRemoteConnection* connection) -> const char* { + return __real_nm_connection_get_interface_name(connection); + })); + ON_CALL(*this, nm_client_get_devices(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> const GPtrArray* { + return __real_nm_client_get_devices(client); + })); + ON_CALL(*this, nm_client_new(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](GCancellable* cancellable, GError** error) -> NMClient* { + return __real_nm_client_new(cancellable, error); + })); + ON_CALL(*this, nm_device_get_hw_address(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice* device) -> const char* { + return __real_nm_device_get_hw_address(device); + })); + ON_CALL(*this, nm_client_get_active_connections(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> const GPtrArray* { + return __real_nm_client_get_active_connections(client); + })); + ON_CALL(*this, nm_connection_get_setting_ip4_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection) -> NMSettingIPConfig* { + return __real_nm_connection_get_setting_ip4_config(connection); + })); + ON_CALL(*this, nm_connection_get_setting_ip6_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection) -> NMSettingIPConfig* { + return __real_nm_connection_get_setting_ip6_config(connection); + })); + ON_CALL(*this, nm_setting_ip_config_get_method(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMSettingIPConfig* setting) -> const char* { + return __real_nm_setting_ip_config_get_method(setting); + })); + ON_CALL(*this, nm_connection_get_setting_connection(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMConnection* connection) -> NMSettingConnection* { + return __real_nm_connection_get_setting_connection(connection); + })); + ON_CALL(*this, nm_setting_connection_get_interface_name(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMSettingConnection* setting) -> const char* { + return __real_nm_setting_connection_get_interface_name(setting); + })); + ON_CALL(*this, nm_active_connection_get_ip4_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> NMIPConfig* { + return __real_nm_active_connection_get_ip4_config(connection); + })); + ON_CALL(*this, nm_active_connection_get_ip6_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> NMIPConfig* { + return __real_nm_active_connection_get_ip6_config(connection); + })); + ON_CALL(*this, nm_ip_config_get_addresses(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPConfig* config) -> GPtrArray* { + return __real_nm_ip_config_get_addresses(config); + })); + ON_CALL(*this, nm_ip_address_get_address(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPAddress* address) -> const char* { + return __real_nm_ip_address_get_address(address); + })); + ON_CALL(*this, nm_ip_address_get_prefix(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPAddress* address) -> guint { + return __real_nm_ip_address_get_prefix(address); + })); + ON_CALL(*this, nm_ip_config_get_gateway(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPConfig* config) -> const char* { + return __real_nm_ip_config_get_gateway(config); + })); + ON_CALL(*this, nm_ip_config_get_nameservers(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMIPConfig* config) -> const char* const* { + return __real_nm_ip_config_get_nameservers(config); + })); + ON_CALL(*this, nm_active_connection_get_dhcp4_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> NMDhcpConfig* { + return __real_nm_active_connection_get_dhcp4_config(connection); + })); + ON_CALL(*this, nm_active_connection_get_dhcp6_config(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMActiveConnection* connection) -> NMDhcpConfig* { + return __real_nm_active_connection_get_dhcp6_config(connection); + })); + ON_CALL(*this, nm_dhcp_config_get_one_option(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDhcpConfig* config, const char* option) -> const char* { + return __real_nm_dhcp_config_get_one_option(config, option); + })); + + // Access Point API ON_CALL setups + ON_CALL(*this, nm_access_point_get_flags(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> NM80211ApFlags { + return __real_nm_access_point_get_flags(ap); + })); + + ON_CALL(*this, nm_access_point_get_wpa_flags(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> NM80211ApSecurityFlags { + return __real_nm_access_point_get_wpa_flags(ap); + })); + + ON_CALL(*this, nm_access_point_get_rsn_flags(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> NM80211ApSecurityFlags { + return __real_nm_access_point_get_rsn_flags(ap); + })); + + ON_CALL(*this, nm_access_point_get_ssid(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> GBytes* { + return __real_nm_access_point_get_ssid(ap); + })); + + ON_CALL(*this, nm_access_point_get_bssid(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> const char* { + return __real_nm_access_point_get_bssid(ap); + })); + + ON_CALL(*this, nm_access_point_get_frequency(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> guint32 { + return __real_nm_access_point_get_frequency(ap); + })); + + ON_CALL(*this, nm_access_point_get_mode(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> NM80211Mode { + return __real_nm_access_point_get_mode(ap); + })); + + ON_CALL(*this, nm_access_point_get_max_bitrate(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> guint32 { + return __real_nm_access_point_get_max_bitrate(ap); + })); + + ON_CALL(*this, nm_access_point_get_strength(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap) -> guint8 { + return __real_nm_access_point_get_strength(ap); + })); + + ON_CALL(*this, nm_access_point_connection_valid(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMAccessPoint* ap, NMConnection* connection) -> gboolean { + return __real_nm_access_point_connection_valid(ap, connection); + })); + + ON_CALL(*this, nm_device_wifi_get_active_access_point(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi* device) -> NMAccessPoint* { + return __real_nm_device_wifi_get_active_access_point(device); + })); + + // WiFi Scan API ON_CALL setups + ON_CALL(*this, nm_device_wifi_request_scan_async(::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_device_wifi_request_scan_async(device, cancellable, callback, user_data); + })); + + ON_CALL(*this, nm_device_wifi_request_scan_options_async(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi *device, GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_device_wifi_request_scan_options_async(device, options, cancellable, callback, user_data); + })); + + ON_CALL(*this, nm_device_wifi_request_scan_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi *device, GAsyncResult *result, GError **error) -> gboolean { + return __real_nm_device_wifi_request_scan_finish(device, result, error); + })); + ON_CALL(*this, nm_device_wifi_get_access_points(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDeviceWifi *device) -> const GPtrArray* { + return __real_nm_device_wifi_get_access_points(device); + })); + ON_CALL(*this, nm_device_get_available_connections(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMDevice *device) -> const GPtrArray* { + return __real_nm_device_get_available_connections(device); + })); + ON_CALL(*this, nm_object_get_path(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMObject *object) -> const char* { + return __real_nm_object_get_path(object); + })); + ON_CALL(*this, nm_client_dbus_set_property(::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { + __real_nm_client_dbus_set_property(client, object_path, interface_name, property_name, value, timeout_msec, cancellable, callback, user_data); + })); + ON_CALL(*this, nm_client_dbus_set_property_finish(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient *client, GAsyncResult *result, GError **error) -> gboolean { + return __real_nm_client_dbus_set_property_finish(client, result, error); + })); + ON_CALL(*this, nm_device_get_ip4_config(::testing::_)) + .WillByDefault(::testing::Return(nullptr)); + ON_CALL(*this, nm_remote_connection_delete(::testing::_, ::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMRemoteConnection *connection, GCancellable *cancellable, GError **error) -> gboolean { + return __real_nm_remote_connection_delete(connection, cancellable, error); + })); + ON_CALL(*this, nm_client_get_state(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> NMState { + return __real_nm_client_get_state(client); + })); + + ON_CALL(*this, nm_client_get_nm_running(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](NMClient* client) -> gboolean { + return __real_nm_client_get_nm_running(client); + })); + } + + virtual ~LibnmWrapsImplMock() = default; + + MOCK_METHOD(const char*, nm_device_get_iface, (NMDevice* device), (override)); + MOCK_METHOD(NMDevice*, nm_client_get_device_by_iface, (NMClient *client, const char *iface), (override)); + MOCK_METHOD(NMDeviceState, nm_device_get_state, (NMDevice *device), (override)); + MOCK_METHOD(void, nm_device_disconnect_async, (NMDevice *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(gboolean, nm_device_disconnect_finish, (NMDevice *device, GAsyncResult *result, GError **error), (override)); + MOCK_METHOD(NMActiveConnection*, nm_client_get_primary_connection, (NMClient *client), (override)); + MOCK_METHOD(NMActiveConnection*, nm_device_get_active_connection, (NMDevice *device), (override)); + MOCK_METHOD(NMRemoteConnection*, nm_active_connection_get_connection, (NMActiveConnection *connection), (override)); + MOCK_METHOD(const char*, nm_connection_get_interface_name, (NMRemoteConnection *connection), (override)); + MOCK_METHOD(NMClient*, nm_client_new, (GCancellable* cancellable, GError** error), (override)); + MOCK_METHOD(const GPtrArray*, nm_client_get_devices, (NMClient* client), (override)); + MOCK_METHOD(const char*, nm_device_get_hw_address, (NMDevice* device), (override)); + MOCK_METHOD(const char*, nm_setting_connection_get_interface_name, (NMSettingConnection* settings), (override)); + MOCK_METHOD(const GPtrArray*, nm_client_get_active_connections, (NMClient* client), (override)); + MOCK_METHOD(NMSettingIPConfig*, nm_connection_get_setting_ip4_config, (NMConnection* connection), (override)); + MOCK_METHOD(NMSettingIPConfig*, nm_connection_get_setting_ip6_config, (NMConnection* connection), (override)); + MOCK_METHOD(const char*, nm_setting_ip_config_get_method, (NMSettingIPConfig* setting), (override)); + MOCK_METHOD(NMSettingConnection*, nm_connection_get_setting_connection, (NMConnection* connection), (override)); + MOCK_METHOD(NMIPConfig*, nm_active_connection_get_ip4_config, (NMActiveConnection* connection), (override)); + MOCK_METHOD(NMIPConfig*, nm_active_connection_get_ip6_config, (NMActiveConnection* connection), (override)); + MOCK_METHOD(NMDhcpConfig*, nm_active_connection_get_dhcp6_config, (NMActiveConnection* connection), (override)); + MOCK_METHOD(GPtrArray*, nm_ip_config_get_addresses, (NMIPConfig* config), (override)); + MOCK_METHOD(const char*, nm_ip_address_get_address, (NMIPAddress* address), (override)); + MOCK_METHOD(guint, nm_ip_address_get_prefix, (NMIPAddress* address), (override)); + MOCK_METHOD(const char*, nm_ip_config_get_gateway, (NMIPConfig* config), (override)); + MOCK_METHOD(const char* const*, nm_ip_config_get_nameservers, (NMIPConfig* config), (override)); + MOCK_METHOD(NMDhcpConfig*, nm_active_connection_get_dhcp4_config, (NMActiveConnection* connection), (override)); + MOCK_METHOD(const char*, nm_dhcp_config_get_one_option, (NMDhcpConfig* config, const char* option), (override)); + + MOCK_METHOD(NM80211ApFlags, nm_access_point_get_flags, (NMAccessPoint *ap), (override)); + MOCK_METHOD(NM80211ApSecurityFlags, nm_access_point_get_wpa_flags, (NMAccessPoint *ap), (override)); + MOCK_METHOD(NM80211ApSecurityFlags, nm_access_point_get_rsn_flags, (NMAccessPoint *ap), (override)); + MOCK_METHOD(GBytes*, nm_access_point_get_ssid, (NMAccessPoint *ap), (override)); + MOCK_METHOD(const char*, nm_access_point_get_bssid, (NMAccessPoint *ap), (override)); + MOCK_METHOD(guint32, nm_access_point_get_frequency, (NMAccessPoint *ap), (override)); + MOCK_METHOD(NM80211Mode, nm_access_point_get_mode, (NMAccessPoint *ap), (override)); + MOCK_METHOD(guint32, nm_access_point_get_max_bitrate, (NMAccessPoint *ap), (override)); + MOCK_METHOD(guint8, nm_access_point_get_strength, (NMAccessPoint *ap), (override)); + MOCK_METHOD(gboolean, nm_access_point_connection_valid, (NMAccessPoint *ap, NMConnection *connection), (override)); + MOCK_METHOD(NMAccessPoint*, nm_device_wifi_get_active_access_point, (NMDeviceWifi *device), (override)); + MOCK_METHOD(const GPtrArray*, nm_device_wifi_get_access_points, (NMDeviceWifi *device), (override)); + MOCK_METHOD(const GPtrArray*, nm_device_get_available_connections, (NMDevice *device), (override)); + MOCK_METHOD(const char*, nm_object_get_path, (NMObject *object), (override)); + MOCK_METHOD(void, nm_client_dbus_set_property, (NMClient *client, const char *object_path, const char *interface_name, const char *property_name, GVariant *value, int timeout_msec, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(gboolean, nm_client_dbus_set_property_finish, (NMClient *client, GAsyncResult *result, GError **error), (override)); + + MOCK_METHOD(void, nm_device_wifi_request_scan_async, (NMDeviceWifi *device, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(void, nm_device_wifi_request_scan_options_async, (NMDeviceWifi *device, GVariant *options, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(gboolean, nm_device_wifi_request_scan_finish, (NMDeviceWifi *device, GAsyncResult *result, GError **error), (override)); + + MOCK_METHOD(gint64, nm_device_wifi_get_last_scan, (NMDeviceWifi *device), (override)); + MOCK_METHOD(void, nm_device_set_autoconnect, (NMDevice *device, gboolean autoconnect), (override)); + MOCK_METHOD(const GPtrArray*, nm_client_get_connections, (NMClient *client), (override)); + MOCK_METHOD(const char*, nm_connection_get_id, (NMConnection *connection), (override)); + MOCK_METHOD(const char*, nm_connection_get_connection_type, (NMConnection *connection), (override)); + MOCK_METHOD(void, nm_client_activate_connection_async, (NMClient *client, NMConnection *connection, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(NMActiveConnection*, nm_client_activate_connection_finish, (NMClient *client, GAsyncResult *result, GError **error), (override)); + MOCK_METHOD(void, nm_client_add_and_activate_connection_async, (NMClient *client, NMConnection *partial, NMDevice *device, const char *specific_object, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + + MOCK_METHOD(gboolean, nm_remote_connection_commit_changes, (NMRemoteConnection *connection, gboolean save_to_disk, GCancellable *cancellable, GError **error), (override)); + MOCK_METHOD(const char*, nm_setting_ip_config_get_dhcp_hostname, (NMSettingIPConfig *setting), (override)); + MOCK_METHOD(gboolean, nm_setting_ip_config_get_dhcp_send_hostname, (NMSettingIPConfig *setting), (override)); + MOCK_METHOD(void, nm_connection_add_setting, (NMConnection *connection, NMSetting *setting), (override)); + MOCK_METHOD(NMSettingWireless*, nm_connection_get_setting_wireless, (NMConnection *connection), (override)); + MOCK_METHOD(GBytes*, nm_setting_wireless_get_ssid, (NMSettingWireless *setting), (override)); + MOCK_METHOD(NMActiveConnection*, nm_client_add_and_activate_connection_finish, (NMClient *client, GAsyncResult *result, GError **error), (override)); + MOCK_METHOD(GVariant*, nm_remote_connection_update2_finish, (NMRemoteConnection *connection, GAsyncResult *result, GError **error), (override)); + MOCK_METHOD(void, nm_remote_connection_update2, (NMRemoteConnection *connection, GVariant *settings, NMSettingsUpdate2Flags flags, GVariant *args, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(void, nm_client_add_connection2, (NMClient *client, GVariant *settings, NMSettingsAddConnection2Flags flags, GVariant *args, gboolean ignore_out_result, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data), (override)); + MOCK_METHOD(NMRemoteConnection*, nm_client_add_connection2_finish, (NMClient *client, GAsyncResult *result, GVariant **out_result, GError **error), (override)); + MOCK_METHOD(gboolean, nm_remote_connection_delete, (NMRemoteConnection *connection, GCancellable *cancellable, GError **error), (override)); + MOCK_METHOD(NMState, nm_client_get_state, (NMClient *client), (override)); + MOCK_METHOD(gboolean, nm_client_get_nm_running, (NMClient *client), (override)); + MOCK_METHOD(const char*, nm_active_connection_get_id, (NMActiveConnection *connection), (override)); + MOCK_METHOD(const char*, nm_active_connection_get_connection_type, (NMActiveConnection *connection), (override)); + MOCK_METHOD(NMDeviceStateReason, nm_device_get_state_reason, (NMDevice *device), (override)); + MOCK_METHOD(int, nm_ip_address_get_family, (NMIPAddress *address), (override)); + MOCK_METHOD(GVariant*, nm_connection_to_dbus, (NMConnection *connection, NMConnectionSerializationFlags flags), (override)); + MOCK_METHOD(NMIPConfig*, nm_device_get_ip4_config, (NMDevice* device), (override)); +}; diff --git a/tests/mocks/Wraps.cpp b/tests/mocks/Wraps.cpp new file mode 100644 index 000000000..2ff5b4815 --- /dev/null +++ b/tests/mocks/Wraps.cpp @@ -0,0 +1,162 @@ +#include +#include +#include "Wraps.h" +#include +#include + +extern "C" int __wrap_system(const char* command) +{ + return Wraps::getInstance().system(command); +} + +extern "C" FILE* __wrap_popen(const char* command, const char* type) +{ + return Wraps::getInstance().popen(command, type); +} + +extern "C" int __wrap_pclose(FILE* pipe) +{ + return Wraps::getInstance().pclose(pipe); +} + +//This function will be called for syslog() in the code (added -Wl,-wrap,syslog) +extern "C" void __wrap_syslog(int pri, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + Wraps::getInstance().syslog(pri, fmt, args); + vsyslog(pri, fmt, args); + va_end(args); +} + +extern "C" FILE* __wrap_setmntent(const char* command, const char* type) +{ + return Wraps::getInstance().setmntent(command, type); +} + +extern "C" struct mntent* __wrap_getmntent(FILE* pipe) +{ + return Wraps::getInstance().getmntent(pipe); +} + +extern "C" FILE * __wrap_v_secure_popen(const char *direction, const char *command, ...) +{ + va_list args; + FILE *retFp = nullptr; + va_start(args, command); + retFp = Wraps::getInstance().v_secure_popen(direction, command, args); + va_end(args); + return retFp; +} + + +extern "C" int __wrap_v_secure_pclose(FILE *file) +{ + return Wraps::getInstance().v_secure_pclose(file); +} + + +extern "C" int __wrap_v_secure_system(const char *command, ...) +{ + int ret; + va_list args; + va_start(args,command); + ret = Wraps::getInstance().v_secure_system(command, args); + va_end(args); + return ret; +} + +extern "C" ssize_t __wrap_readlink(const char *pathname, char *buf, size_t bufsiz) +{ + return Wraps::getInstance().readlink(pathname, buf, bufsiz); +} + +extern "C" time_t __wrap_time(time_t *arg) +{ + return Wraps::getInstance().time(arg); +} + +WrapsImpl* Wraps::impl = nullptr; + +Wraps::Wraps() {} + +void Wraps::setImpl(WrapsImpl* newImpl) +{ + // Handles both resetting 'impl' to nullptr and assigning a new value to 'impl' + EXPECT_TRUE ((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} + +Wraps& Wraps::getInstance() +{ + static Wraps instance; + return instance; +} + +int Wraps::system(const char* command) +{ + EXPECT_NE(impl, nullptr); + return impl->system(command); +} + +FILE* Wraps::popen(const char* command, const char* type) +{ + EXPECT_NE(impl, nullptr); + return impl->popen(command, type); +} + +int Wraps::pclose(FILE* pipe) +{ + EXPECT_NE(impl, nullptr); + return impl->pclose(pipe); +} + +void Wraps::syslog(int pri, const char* fmt, va_list args) +{ + EXPECT_NE(impl, nullptr); + impl->syslog(pri, fmt, args); +} + +FILE* Wraps::setmntent(const char* command, const char* type) +{ + EXPECT_NE(impl, nullptr); + return impl->setmntent(command,type); +} + +struct mntent* Wraps::getmntent(FILE* pipe) +{ + EXPECT_NE(impl, nullptr); + return impl->getmntent(pipe); +} + +FILE * Wraps::v_secure_popen(const char *direction, const char *command, va_list args) +{ + EXPECT_NE(impl, nullptr); + return impl->v_secure_popen(direction, command, args); +} + +int Wraps::v_secure_pclose(FILE *file) +{ + EXPECT_NE(impl, nullptr); + return impl->v_secure_pclose(file); +} + +int Wraps::v_secure_system(const char *command, va_list args) +{ + EXPECT_NE(impl, nullptr); + return impl->v_secure_system(command,args); +} + + +ssize_t Wraps::readlink(const char *pathname, char *buf, size_t bufsiz) +{ + EXPECT_NE(impl, nullptr); + return impl->readlink(pathname,buf,bufsiz); +} + +time_t Wraps::time(time_t* arg) +{ + EXPECT_NE(impl, nullptr); + return impl->time(arg); +} + diff --git a/tests/mocks/Wraps.h b/tests/mocks/Wraps.h new file mode 100644 index 000000000..ff34a0fda --- /dev/null +++ b/tests/mocks/Wraps.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include "secure_wrappermock.h" + +class WrapsImpl { +public: + virtual ~WrapsImpl() = default; + + virtual int system(const char* command) = 0; + virtual FILE* popen(const char* command, const char* type) = 0; + virtual int pclose(FILE *pipe) = 0; + virtual void syslog(int pri, const char* fmt, va_list args) = 0; + virtual FILE* setmntent(const char* command, const char* type) = 0; + virtual struct mntent* getmntent(FILE *pipe) = 0; + virtual FILE *v_secure_popen(const char *direction, const char *command, va_list args) = 0; + virtual int v_secure_pclose(FILE *) = 0; + virtual int v_secure_system(const char *command, va_list args) =0; + virtual ssize_t readlink(const char *pathname, char *buf, size_t bufsiz) = 0; + virtual time_t time(time_t* arg) = 0; +}; + +class Wraps { +protected: + static WrapsImpl* impl; + +public: + Wraps(); + Wraps(const Wraps &obj) = delete; + static void setImpl(WrapsImpl* newImpl); + static Wraps& getInstance(); + + static int system(const char* command); + + static FILE* popen(const char* command, const char* type); + + static int pclose(FILE* pipe); + + static void syslog(int pri, const char* fmt, va_list args); + + static FILE* setmntent(const char* command, const char* type); + + static struct mntent* getmntent(FILE* pipe); + + static FILE *v_secure_popen(const char *direction, const char *command, va_list args); + + static int v_secure_pclose(FILE *file); + + static int v_secure_system(const char *command, va_list args); + + ssize_t readlink(const char *pathname, char *buf, size_t bufsiz); + + static time_t time(time_t* arg); +}; diff --git a/tests/mocks/WrapsMock.h b/tests/mocks/WrapsMock.h new file mode 100644 index 000000000..b6f99728e --- /dev/null +++ b/tests/mocks/WrapsMock.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include "Wraps.h" + +extern "C" FILE* __real_setmntent(const char* command, const char* type); + +class WrapsImplMock : public WrapsImpl { +public: + WrapsImplMock():WrapsImpl() + { + /*Setting up Default behavior for setmntent: + * We are mocking setmntent in this file below with __wrap_setmntent, + * and the actual setmntent will be called via this interface */ + ON_CALL(*this, setmntent(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const char* command, const char* type) -> FILE* { + return __real_setmntent(command, type); + })); + } + virtual ~WrapsImplMock() = default; + + MOCK_METHOD(int, system, (const char* command), (override)); + MOCK_METHOD(FILE*, popen, (const char* command, const char* type), (override)); + MOCK_METHOD(int, pclose, (FILE* pipe), (override)); + MOCK_METHOD(void, syslog, (int pri, const char* fmt, va_list args), (override)); + MOCK_METHOD(FILE*, setmntent, (const char* command, const char* type), (override)); + MOCK_METHOD(struct mntent*, getmntent, (FILE* pipe), (override)); + MOCK_METHOD(FILE*, v_secure_popen, (const char *direction, const char *command, va_list args), (override)); + MOCK_METHOD(int, v_secure_pclose, (FILE *file), (override)); + MOCK_METHOD(int, v_secure_system,(const char *command, va_list args), (override)); + MOCK_METHOD(ssize_t, readlink, (const char *pathname, char *buf, size_t bufsiz), (override)); + MOCK_METHOD(time_t, time, (time_t* arg), (override)); +}; \ No newline at end of file diff --git a/tests/mocks/mfrMgr.h b/tests/mocks/mfrMgr.h new file mode 100644 index 000000000..8934e1d8b --- /dev/null +++ b/tests/mocks/mfrMgr.h @@ -0,0 +1,62 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#define SSID_SIZE 32 +#define WIFI_MAX_PASSWORD_LEN 64 + +#define IARM_BUS_MFRLIB_NAME "MFRLib" +#define IARM_BUS_MFRLIB_API_WIFI_Credentials "mfrWifiCredentials" +#define IARM_BUS_MFRLIB_API_WIFI_EraseAllData "mfrWifiEraseAllData" + +/** + * @brief WIFI API return status + * + */ +typedef enum _WIFI_API_RESULT { + WIFI_API_RESULT_SUCCESS = 0, ///< operation is successful + WIFI_API_RESULT_FAILED, ///< Operation general error. This enum is deprecated + WIFI_API_RESULT_NULL_PARAM, ///< NULL argument is passed to the module + WIFI_API_RESULT_INVALID_PARAM, ///< Invalid argument is passed to the module + WIFI_API_RESULT_NOT_INITIALIZED, ///< module not initialized + WIFI_API_RESULT_OPERATION_NOT_SUPPORTED, ///< operation not supported in the specific platform + WIFI_API_RESULT_READ_WRITE_FAILED, ///< flash read/write failed or crc check failed + WIFI_API_RESULT_MAX ///< Out of range - required to be the last item of the enum +} WIFI_API_RESULT; +/** + * @brief WIFI credentials data struct + * + */ +typedef struct { + char cSSID[SSID_SIZE+1]; ///< SSID field. + char cPassword[WIFI_MAX_PASSWORD_LEN+1]; ///< password field + int iSecurityMode; ///< security mode. Platform dependent and caller is responsible to validate it +} WIFI_DATA; + +typedef enum _WifiRequestType { + WIFI_GET_CREDENTIALS = 0, + WIFI_SET_CREDENTIALS = 1 +} WifiRequestType_t; + +typedef struct _IARM_BUS_MFRLIB_API_WIFI_Credentials_Param_t { + WIFI_DATA wifiCredentials; + WifiRequestType_t requestType; + WIFI_API_RESULT returnVal; +} IARM_BUS_MFRLIB_API_WIFI_Credentials_Param_t; diff --git a/tests/mocks/secure_wrappermock.h b/tests/mocks/secure_wrappermock.h new file mode 100644 index 000000000..37dd7a2e4 --- /dev/null +++ b/tests/mocks/secure_wrappermock.h @@ -0,0 +1,37 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once +#include +#include + +# ifdef __cplusplus +extern "C" { +# endif + + +FILE *v_secure_popen(const char *direction, const char *command, ...); + +int v_secure_pclose(FILE *); + +int v_secure_system(const char *command, ...); + +# ifdef __cplusplus +} +# endif diff --git a/tests/mocks/thunder/COMLinkMock.h b/tests/mocks/thunder/COMLinkMock.h new file mode 100644 index 000000000..4b69f988a --- /dev/null +++ b/tests/mocks/thunder/COMLinkMock.h @@ -0,0 +1,38 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef RDKSERVICES_TESTS_MOCKS_COMLINKMOCK_H_ +#define RDKSERVICES_TESTS_MOCKS_COMLINKMOCK_H_ + +#include +#include "Module.h" + +class COMLinkMock : public WPEFramework::PluginHost::IShell::ICOMLink { +public: + virtual ~COMLinkMock() = default; + + MOCK_METHOD(void, Register, (WPEFramework::RPC::IRemoteConnection::INotification*), (override)); + MOCK_METHOD(void, Unregister, (const WPEFramework::RPC::IRemoteConnection::INotification*), (override)); + MOCK_METHOD(void, Register, (INotification*), (override)); + MOCK_METHOD(void, Unregister, (INotification*), (override)); + MOCK_METHOD(WPEFramework::RPC::IRemoteConnection*, RemoteConnection, (const uint32_t), (override)); + MOCK_METHOD(void*, Instantiate, (const WPEFramework::RPC::Object&, const uint32_t, uint32_t&), (override)); +}; + +#endif //RDKSERVICES_TESTS_MOCKS_COMLINKMOCK_H_ diff --git a/tests/mocks/thunder/Communicator.cpp b/tests/mocks/thunder/Communicator.cpp new file mode 100755 index 000000000..2bb1bceb8 --- /dev/null +++ b/tests/mocks/thunder/Communicator.cpp @@ -0,0 +1,30 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#include "Communicator.h" +#include + +ICommunicatorClient* WPEFramework::RPC::CommunicatorClient::impl = nullptr; + +void WPEFramework::RPC::CommunicatorClient::setImpl(ICommunicatorClient* newImpl) +{ + // Handles both resetting 'impl' to nullptr and assigning a new value to 'impl' + EXPECT_TRUE ((nullptr == impl) || (nullptr == newImpl)); + impl = newImpl; +} diff --git a/tests/mocks/thunder/Communicator.h b/tests/mocks/thunder/Communicator.h new file mode 100755 index 000000000..5d0524b84 --- /dev/null +++ b/tests/mocks/thunder/Communicator.h @@ -0,0 +1,286 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#ifndef __COM_PROCESSLAUNCH_H +#define __COM_PROCESSLAUNCH_H + +#include +#include "Proxy.h" +#include "NodeId.h" +#include "IPCConnector.h" + +using std::string; +const uint32_t CommunicationTimeOut = 3000; + +class ICommunicatorClient { +public: + virtual ~ICommunicatorClient() = default; + virtual uint32_t Open(const uint32_t waitTime) = 0; + virtual void* Open(const std::string& className, const uint32_t version, const uint32_t waitTime) = 0; + virtual void* Acquire(const uint32_t waitTime, const std::string& className, const uint32_t versionId) = 0; + virtual uint32_t Offer(void* offer, const uint32_t version, const uint32_t waitTime) = 0; + virtual uint32_t Revoke(void* offer, const uint32_t version, const uint32_t waitTime) = 0; + virtual uint32_t Close(const uint32_t waitTime) = 0; + virtual void* Acquire(const std::string& className, const uint32_t interfaceId, const uint32_t versionId) = 0; +}; + +namespace WPEFramework { +namespace RPC { + class Object { + public: + enum class HostType { + LOCAL, + DISTRIBUTED, + CONTAINER + }; + + Object() + : _locator() + , _className() + , _callsign() + , _interface(~0) + , _version(~0) + , _user() + , _group() + , _threads() + , _priority() + , _type(HostType::LOCAL) + , _systemRootPath() + , _remoteAddress() + , _configuration() + { + } + Object(const Object& copy) + : _locator(copy._locator) + , _className(copy._className) + , _callsign(copy._callsign) + , _interface(copy._interface) + , _version(copy._version) + , _user(copy._user) + , _group(copy._group) + , _threads(copy._threads) + , _priority(copy._priority) + , _type(copy._type) + , _systemRootPath(copy._systemRootPath) + , _remoteAddress(copy._remoteAddress) + , _configuration(copy._configuration) + { + } + Object(const string& locator, + const string& className, + const string& callsign, + const uint32_t interface, + const uint32_t version, + const string& user, + const string& group, + const uint8_t threads, + const int8_t priority, + const HostType type, + const string& systemRootPath, + const string& remoteAddress, + const string& configuration) + : _locator(locator) + , _className(className) + , _callsign(callsign) + , _interface(interface) + , _version(version) + , _user(user) + , _group(group) + , _threads(threads) + , _priority(priority) + , _type(type) + , _systemRootPath(systemRootPath) + , _remoteAddress(remoteAddress) + , _configuration(configuration) + { + } + ~Object() + { + } + + Object& operator=(const Object& RHS) + { + _locator = RHS._locator; + _className = RHS._className; + _callsign = RHS._callsign; + _interface = RHS._interface; + _version = RHS._version; + _user = RHS._user; + _group = RHS._group; + _threads = RHS._threads; + _priority = RHS._priority; + _systemRootPath = RHS._systemRootPath; + _type = RHS._type; + _remoteAddress = RHS._remoteAddress; + _configuration = RHS._configuration; + + return (*this); + } + + public: + inline const string& Locator() const + { + return (_locator); + } + inline const string& ClassName() const + { + return (_className); + } + inline const string& Callsign() const + { + return (_callsign); + } + inline uint32_t Interface() const + { + return (_interface); + } + inline uint32_t Version() const + { + return (_version); + } + inline const string& User() const + { + return (_user); + } + inline const string& Group() const + { + return (_group); + } + inline uint8_t Threads() const + { + return (_threads); + } + inline int8_t Priority() const + { + return (_priority); + } + inline HostType Type() const + { + return (_type); + } + inline const string& SystemRootPath() const + { + return (_systemRootPath); + } + inline const Core::NodeId RemoteAddress() const + { + return (Core::NodeId(_remoteAddress.c_str())); + } + inline const string& Configuration() const + { + return (_configuration); + } + + private: + string _locator; + string _className; + string _callsign; + uint32_t _interface; + uint32_t _version; + string _user; + string _group; + uint8_t _threads; + int8_t _priority; + HostType _type; + string _systemRootPath; + string _remoteAddress; + string _configuration; + }; + + class CommunicatorClient{ + protected: + static ICommunicatorClient* impl; + + public: + class Source { + bool IsOpen() const + { + return true; + } + }; + CommunicatorClient() = delete; + CommunicatorClient(CommunicatorClient&&) = delete; + CommunicatorClient(const CommunicatorClient&) = delete; + CommunicatorClient& operator=(CommunicatorClient&&) = delete; + CommunicatorClient& operator=(const CommunicatorClient&) = delete; + + static void setImpl(ICommunicatorClient* newImpl); + + CommunicatorClient(const Core::NodeId& remoteNode) { + //Mock CommunicatorClient constructor. + } + + CommunicatorClient(const Core::NodeId& remoteNode, const Core::ProxyType& handler) { + //Mock CommunicatorClient constructor. + } + + ~CommunicatorClient() { + } + + public: + inline uint32_t ConnectionId() const { + return _connectionId; + } + + // Open a communication channel with this process, no need for an initial exchange + uint32_t Open(const uint32_t waitTime) + { + return impl->Open(waitTime); + } + template + INTERFACE* Open(const std::string& className, const uint32_t version = static_cast(~0), const uint32_t waitTime = CommunicationTimeOut) + { + return static_cast(impl->Open(className, version, waitTime)); + } + template + INTERFACE* Acquire(const uint32_t waitTime, const std::string& className, const uint32_t versionId) + { + return static_cast(impl->Acquire(waitTime, className, versionId)); + } + template + uint32_t Offer(INTERFACE* offer, const uint32_t version = static_cast(~0), const uint32_t waitTime = CommunicationTimeOut) + { + return static_cast(impl->Offer(static_cast(offer), version, CommunicationTimeOut)); + } + template + uint32_t Revoke(INTERFACE* offer, const uint32_t version = static_cast(~0), const uint32_t waitTime = CommunicationTimeOut) + { + return static_cast(impl->Revoke(static_cast(offer), version, CommunicationTimeOut)); + } + uint32_t Close(const uint32_t waitTime) + { + if (impl) + return impl->Close(waitTime); + return Core::ERROR_GENERAL; + } + virtual void* Acquire(const std::string& className, const uint32_t interfaceId, const uint32_t versionId) + { + return impl->Acquire(className, interfaceId, versionId); + } + protected: + void StateChange() { + //Mock CommunicatorClient StateChange is calling. + } + private: + uint32_t _connectionId; + }; +} +} +#endif // __COM_PROCESSLAUNCH_H + diff --git a/tests/mocks/thunder/CommunicatorMock.h b/tests/mocks/thunder/CommunicatorMock.h new file mode 100755 index 000000000..b689de20e --- /dev/null +++ b/tests/mocks/thunder/CommunicatorMock.h @@ -0,0 +1,53 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +#pragma once + +#include +#include "Communicator.h" + +#if 0 +class CommunicatorClientMock : public ICommunicatorClient +{ +public: + virtual ~CommunicatorClientMock() = default; + + MOCK_METHOD(uint32_t, Open, (const uint32_t waitTime), (override)); + MOCK_METHOD(INTERFACE*, Open, (const std::string& className, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(INTERFACE*, Acquire, (const uint32_t waitTime, const std::string& className, const uint32_t versionId), (override)); + MOCK_METHOD(uint32_t, Offer, (INTERFACE* offer, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(uint32_t, Revoke, (INTERFACE* offer, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(uint32_t, Close, (const uint32_t waitTime), (override)); +}; +#else +class CommunicatorClientMock : public ICommunicatorClient { +public: + virtual ~CommunicatorClientMock() = default; + MOCK_METHOD(uint32_t, AddRef, (), (const)); + MOCK_METHOD(uint32_t, Release, (), (const)); + MOCK_METHOD(uint32_t, Open, (const uint32_t waitTime), (override)); + MOCK_METHOD(void*, Open, (const std::string& className, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(void*, Acquire, (const uint32_t waitTime, const std::string& className, const uint32_t versionId), (override)); + MOCK_METHOD(uint32_t, Offer, (void* offer, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(uint32_t, Revoke, (void* offer, const uint32_t version, const uint32_t waitTime), (override)); + MOCK_METHOD(uint32_t, Close, (const uint32_t waitTime), (override)); + MOCK_METHOD(void*, Acquire, (const std::string& className, const uint32_t interfaceId, const uint32_t versionId), (override)); +}; + +#endif diff --git a/tests/mocks/thunder/DispatcherMock.h b/tests/mocks/thunder/DispatcherMock.h new file mode 100644 index 000000000..6ae2b7f52 --- /dev/null +++ b/tests/mocks/thunder/DispatcherMock.h @@ -0,0 +1,43 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DISPATCHERMOCK_H +#define DISPATCHERMOCK_H + +#include + +#include "Module.h" + + class DispatcherMock: public WPEFramework::PluginHost::ILocalDispatcher{ + public: + virtual ~DispatcherMock() = default; + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (const uint32_t interfaceNummer), (override)); + MOCK_METHOD(void, Activate, (WPEFramework::PluginHost::IShell* service)); + MOCK_METHOD(void, Deactivate, ()); + MOCK_METHOD(void, Dropped, (const uint32_t)); + MOCK_METHOD(uint32_t, Invoke, (const uint32_t channelId, const uint32_t id, const string& token, const string& method, const string& parameters, string& response), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Invoke, (WPEFramework::PluginHost::IDispatcher::ICallback* callback, const uint32_t channelId, const uint32_t id, const string& token, const string& method, const string& parameters /* @restrict:(4M-1) */, string& response /* @restrict:(4M-1) @out */), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Revoke, (WPEFramework::PluginHost::IDispatcher::ICallback* callback), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Validate, (const string& token, const string& method, const string& paramaters /* @restrict:(4M-1) */), (const, override)); + MOCK_METHOD(WPEFramework::PluginHost::ILocalDispatcher*, Local, (), (override)); +}; + +#endif //DISPATCHERMOCK_H diff --git a/tests/mocks/thunder/FactoriesImplementation.h b/tests/mocks/thunder/FactoriesImplementation.h new file mode 100644 index 000000000..2fe178f85 --- /dev/null +++ b/tests/mocks/thunder/FactoriesImplementation.h @@ -0,0 +1,66 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include + +#include "Module.h" + +class FactoriesImplementation : public WPEFramework::PluginHost::IFactories { +public: + FactoriesImplementation(const FactoriesImplementation&) = delete; + FactoriesImplementation& operator=(const FactoriesImplementation&) = delete; + + FactoriesImplementation() + : _requestFactory(5) + , _responseFactory(5) + , _fileBodyFactory(5) + , _jsonRPCFactory(5) + { + // Defaults: + ON_CALL(*this, Request()) + .WillByDefault(::testing::Invoke( + [&]() { return (_requestFactory.Element()); })); + ON_CALL(*this, Response()) + .WillByDefault(::testing::Invoke( + [&]() { return (_responseFactory.Element()); })); + ON_CALL(*this, FileBody()) + .WillByDefault(::testing::Invoke( + [&]() { return (_fileBodyFactory.Element()); })); + ON_CALL(*this, JSONRPC()) + .WillByDefault(::testing::Invoke( + [&]() { + return (WPEFramework::Core::ProxyType>(_jsonRPCFactory.Element())); + })); + } + + virtual ~FactoriesImplementation() = default; + + MOCK_METHOD(WPEFramework::Core::ProxyType, Request, (), (override)); + MOCK_METHOD(WPEFramework::Core::ProxyType, Response, (), (override)); + MOCK_METHOD(WPEFramework::Core::ProxyType, FileBody, (), (override)); + MOCK_METHOD(WPEFramework::Core::ProxyType>, JSONRPC, (), (override)); + +private: + WPEFramework::Core::ProxyPoolType _requestFactory; + WPEFramework::Core::ProxyPoolType _responseFactory; + WPEFramework::Core::ProxyPoolType _fileBodyFactory; + WPEFramework::Core::ProxyPoolType _jsonRPCFactory; +}; diff --git a/tests/mocks/thunder/IConnectivityCheck.h b/tests/mocks/thunder/IConnectivityCheck.h new file mode 100644 index 000000000..7898ffbdc --- /dev/null +++ b/tests/mocks/thunder/IConnectivityCheck.h @@ -0,0 +1,73 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +/** + * Minimal CI stub for IConnectivityCheck — compatible with Thunder R4.4.3. + * + * This file is used only during CI builds (rdk proxy L1/L2 tests) where the + * full entservices-cpc-apis stack is not available. It defines exactly the + * surface consumed by NetworkManagerConnectivityClient and nothing more. + * + * DO NOT use this file outside of test/CI contexts. + */ + +#include + +namespace WPEFramework { +namespace Exchange { + + struct EXTERNAL IConnectivityCheck : virtual public Core::IUnknown { + + // Stub ID — not used for COM lookup in L1/L2 unit tests. + enum { ID = 0x8190 }; + + enum InternetStatus : uint8_t { + NO_INTERNET, + LIMITED_INTERNET, + CAPTIVE_PORTAL, + FULLY_CONNECTED, + UNKNOWN, + }; + + struct EXTERNAL StatusInfo { + InternetStatus status; + string reason; + string interface; + string ipversion; + }; + + struct EXTERNAL INotification : virtual public Core::IUnknown { + enum { ID = 0x8191 }; + + virtual void OnInternetStatusChange(const InternetStatus status, const string& reason) {} + virtual void OnExternalProbeResult(const InternetStatus status, const string& reason, + const string& captivePortalURI) {} + }; + + virtual Core::hresult GetInternetStatus(StatusInfo& info /* @out */) const {}; + virtual Core::hresult GetCaptivePortalURI(string& uri /* @out */) const {}; + + virtual Core::hresult Register(INotification* notification) {}; + virtual Core::hresult Unregister(INotification* notification) {}; + }; + +} // namespace Exchange +} // namespace WPEFramework diff --git a/tests/mocks/thunder/IPowerManager.h b/tests/mocks/thunder/IPowerManager.h new file mode 100644 index 000000000..30c1d4d75 --- /dev/null +++ b/tests/mocks/thunder/IPowerManager.h @@ -0,0 +1,85 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +/** + * Minimal CI stub for IPowerManager — compatible with Thunder R4.4.3. + * + * This file is used only during CI builds (gdbus/libnm L1 proxy tests) where + * the full entservices-apis stack is not available. It defines exactly the + * surface consumed by NetworkManagerPowerClient and nothing more. + * + * DO NOT use this file outside of test/CI contexts. + */ + +#include + +namespace WPEFramework { +namespace Exchange { + + struct EXTERNAL IPowerManager : virtual public Core::IUnknown { + + // Stub ID — not used for COM lookup in L1 unit tests. + enum { ID = 0x8180 }; + + enum PowerState : uint8_t { + POWER_STATE_UNKNOWN = 0, + POWER_STATE_OFF = 1, + POWER_STATE_STANDBY = 2, + POWER_STATE_ON = 3, + POWER_STATE_STANDBY_LIGHT_SLEEP = 4, + POWER_STATE_STANDBY_DEEP_SLEEP = 5, + }; + + // @event + struct EXTERNAL IModePreChangeNotification : virtual public Core::IUnknown { + enum { ID = 0x8182 }; + virtual void OnPowerModePreChange(const PowerState currentState, + const PowerState newState, + const int transactionId, + const int stateChangeAfter) {} + }; + + // @event + struct EXTERNAL IModeChangedNotification : virtual public Core::IUnknown { + enum { ID = 0x8183 }; + virtual void OnPowerModeChanged(const PowerState currentState, + const PowerState newState) {} + }; + + virtual Core::hresult Register(IModePreChangeNotification* notification) {}; + virtual Core::hresult Unregister(const IModePreChangeNotification* notification) {}; + + virtual Core::hresult Register(IModeChangedNotification* notification) {}; + virtual Core::hresult Unregister(const IModeChangedNotification* notification) {}; + + virtual Core::hresult AddPowerModePreChangeClient(const string& clientName, + uint32_t& clientId) {}; + virtual Core::hresult RemovePowerModePreChangeClient(const uint32_t clientId) {}; + virtual Core::hresult PowerModePreChangeComplete(const uint32_t clientId, + const int transactionId) {}; + virtual Core::hresult DelayPowerModeChangeBy(const uint32_t clientId, + const int transactionId, + const int delayPeriod) {}; + virtual Core::hresult GetNetworkStandbyMode(bool& standbyMode) {}; + }; + +} // namespace Exchange +} // namespace WPEFramework diff --git a/tests/mocks/thunder/IStringIteratorMock.h b/tests/mocks/thunder/IStringIteratorMock.h new file mode 100644 index 000000000..0477dbf27 --- /dev/null +++ b/tests/mocks/thunder/IStringIteratorMock.h @@ -0,0 +1,39 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCKSTRINGITERATOR_H +#define MOCKSTRINGITERATOR_H + +#include +#include "Module.h" + +template +class MockStringIterator : public WPEFramework::RPC::IStringIterator{ +public: + MOCK_METHOD(bool, Next, (ELEMENT&), (override)); + MOCK_METHOD(bool, Previous, (ELEMENT&), (override)); + MOCK_METHOD(void, Reset, (uint32_t), (override)); + MOCK_METHOD(bool, IsValid, (), (const)); + MOCK_METHOD(uint32_t, Count, (), (const)); + MOCK_METHOD(ELEMENT, Current, (), (const)); + MOCK_METHOD(uint32_t, AddRef, (), (const)); + MOCK_METHOD(uint32_t, Release, (), (const)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); +}; +#endif diff --git a/tests/mocks/thunder/InterfaceIteratorMock.h b/tests/mocks/thunder/InterfaceIteratorMock.h new file mode 100644 index 000000000..42aff4e61 --- /dev/null +++ b/tests/mocks/thunder/InterfaceIteratorMock.h @@ -0,0 +1,38 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCKINTERFACEITERATOR_H +#define MOCKINTERFACEITERATOR_H + +#include +#include "Module.h" + +class MockIInterfaceDetailsIterator : public WPEFramework::Exchange::INetworkManager::IInterfaceDetailsIterator { +public: + MOCK_METHOD(bool, Next, (WPEFramework::Exchange::INetworkManager::InterfaceDetails&), (override)); + MOCK_METHOD(bool, Previous, (WPEFramework::Exchange::INetworkManager::InterfaceDetails&), (override)); + MOCK_METHOD(void, Reset, (const uint32_t position), (override)); + MOCK_METHOD(bool, IsValid, (), (const, override)); + MOCK_METHOD(uint32_t, Count, (), (const, override)); + MOCK_METHOD(WPEFramework::Exchange::INetworkManager::InterfaceDetails, Current, (), (const, override)); + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); +}; +#endif diff --git a/Module.cpp b/tests/mocks/thunder/Module.cpp similarity index 95% rename from Module.cpp rename to tests/mocks/thunder/Module.cpp index a574f23db..cd6d4d14e 100644 --- a/Module.cpp +++ b/tests/mocks/thunder/Module.cpp @@ -2,7 +2,7 @@ * If not stated otherwise in this file or this component's LICENSE * file the following copyright and licenses apply: * -* Copyright 2022 RDK Management +* Copyright 2024 RDK Management * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,4 +18,5 @@ **/ #include "Module.h" + MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/tests/mocks/thunder/Module.h b/tests/mocks/thunder/Module.h new file mode 100644 index 000000000..e113be84f --- /dev/null +++ b/tests/mocks/thunder/Module.h @@ -0,0 +1,27 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME networkmanager_tests +#endif + +#include +#include diff --git a/tests/mocks/thunder/ServiceMock.h b/tests/mocks/thunder/ServiceMock.h new file mode 100644 index 000000000..537395005 --- /dev/null +++ b/tests/mocks/thunder/ServiceMock.h @@ -0,0 +1,76 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2022 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SERVICEMOCK_H +#define SERVICEMOCK_H + +#include + +#include "Module.h" +class ServiceMock : public WPEFramework::PluginHost::IShell { +public: + virtual ~ServiceMock() = default; + + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(string, Versions, (), (const, override)); + MOCK_METHOD(string, Locator, (), (const, override)); + MOCK_METHOD(string, ClassName, (), (const, override)); + MOCK_METHOD(string, Callsign, (), (const, override)); + MOCK_METHOD(string, WebPrefix, (), (const, override)); + MOCK_METHOD(string, ConfigLine, (), (const, override)); + MOCK_METHOD(string, PersistentPath, (), (const, override)); + MOCK_METHOD(string, VolatilePath, (), (const, override)); + MOCK_METHOD(string, DataPath, (), (const, override)); + MOCK_METHOD(state, State, (), (const, override)); + MOCK_METHOD(WPEFramework::Core::hresult, Activate, (const reason), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Deactivate, (const reason), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Unavailable, (const reason), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, ConfigLine, (const string& config), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, SystemRootPath, (const string& systemRootPath), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Hibernate, (const uint32_t timeout), (override)); + MOCK_METHOD(string, SystemPath, (), (const, override)); + MOCK_METHOD(string, PluginPath, (), (const, override)); + MOCK_METHOD(WPEFramework::PluginHost::IShell::startup, Startup, (), (const, override)); + MOCK_METHOD(WPEFramework::Core::hresult, Startup, (const startup value), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Resumed, (const bool value), (override)); + MOCK_METHOD(WPEFramework::Core::hresult, Metadata, (string& info), (const, override)); + MOCK_METHOD(bool, Resumed, (), (const, override)); + MOCK_METHOD(bool, IsSupported, (const uint8_t), (const, override)); + MOCK_METHOD(void, EnableWebServer, (const string&, const string&), (override)); + MOCK_METHOD(void, DisableWebServer, (), (override)); + MOCK_METHOD(WPEFramework::PluginHost::ISubSystem*, SubSystems, (), (override)); + MOCK_METHOD(uint32_t, Submit, (const uint32_t, const WPEFramework::Core::ProxyType&), (override)); + MOCK_METHOD(void, Notify, (const string&), (override)); + MOCK_METHOD(void*, QueryInterface, (const uint32_t), (override)); + MOCK_METHOD(void*, QueryInterfaceByCallsign, (const uint32_t, const string&), (override)); + MOCK_METHOD(void, Register, (WPEFramework::PluginHost::IPlugin::INotification*), (override)); + MOCK_METHOD(void, Unregister, (WPEFramework::PluginHost::IPlugin::INotification*), (override)); + MOCK_METHOD(string, Model, (), (const, override)); + MOCK_METHOD(bool, Background, (), (const, override)); + MOCK_METHOD(string, Accessor, (), (const, override)); + MOCK_METHOD(string, ProxyStubPath, (), (const, override)); + MOCK_METHOD(string, HashKey, (), (const, override)); + MOCK_METHOD(string, Substitute, (const string&), (const, override)); + MOCK_METHOD(WPEFramework::PluginHost::IShell::ICOMLink*, COMLink, (), (override)); + MOCK_METHOD(reason, Reason, (), (const, override)); + MOCK_METHOD(string, SystemRootPath, (), (const, override)); +}; + +#endif //SERVICEMOCK_H diff --git a/tests/mocks/thunder/SystemInfo.h b/tests/mocks/thunder/SystemInfo.h new file mode 100644 index 000000000..c20266274 --- /dev/null +++ b/tests/mocks/thunder/SystemInfo.h @@ -0,0 +1,83 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include + +#include "Module.h" + +class SystemInfo : public WPEFramework::PluginHost::ISubSystem { +private: + SystemInfo(const SystemInfo&) = delete; + SystemInfo& operator=(const SystemInfo&) = delete; + +public: + SystemInfo() + : _flags(0) + { + // Defaults: + ON_CALL(*this, Set(::testing::_, ::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const subsystem type, WPEFramework::Core::IUnknown* information) { + _subsystems.emplace(type, information); + + if (type >= NEGATIVE_START) { + _flags &= ~(1 << (type - NEGATIVE_START)); + } else { + _flags |= (1 << type); + } + })); + ON_CALL(*this, Get(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const subsystem type) -> const WPEFramework::Core::IUnknown* { + const WPEFramework::Core::IUnknown* result(nullptr); + + auto it = _subsystems.find(type); + if (it != _subsystems.end()) { + result = it->second; + } + + return result; + })); + ON_CALL(*this, IsActive(::testing::_)) + .WillByDefault(::testing::Invoke( + [&](const subsystem type) -> bool { + return ((type < END_LIST) && ((_flags & (1 << type)) != 0)); + })); + } + virtual ~SystemInfo() = default; + +public: + MOCK_METHOD(void, Register, (WPEFramework::PluginHost::ISubSystem::INotification * notification), (override)); + MOCK_METHOD(void, Unregister, (WPEFramework::PluginHost::ISubSystem::INotification * notification), (override)); + MOCK_METHOD(string, BuildTreeHash, (), (const, override)); + MOCK_METHOD(void, Set, (const subsystem type, WPEFramework::Core::IUnknown* information), (override)); + MOCK_METHOD(const WPEFramework::Core::IUnknown*, Get, (const subsystem type), (const, override)); + MOCK_METHOD(bool, IsActive, (const subsystem type), (const, override)); + MOCK_METHOD(string, Version, (), (const, override)); + + BEGIN_INTERFACE_MAP(SystemInfo) + INTERFACE_ENTRY(WPEFramework::PluginHost::ISubSystem) + END_INTERFACE_MAP + +private: + std::map _subsystems; + uint32_t _flags; +}; diff --git a/tests/mocks/thunder/ThunderPortability.h b/tests/mocks/thunder/ThunderPortability.h new file mode 100755 index 000000000..62310708a --- /dev/null +++ b/tests/mocks/thunder/ThunderPortability.h @@ -0,0 +1,31 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include +#include + +#define EVENT_SUBSCRIBE(__A__, __B__, __C__, __D__) { plugin->Subscribe(__A__, __B__, __C__); (void)__D__; } +#define EVENT_UNSUBSCRIBE(__A__, __B__, __C__, __D__) { plugin->Unsubscribe(__A__, __B__, __C__); (void)__D__; } + +#define DECL_CORE_JSONRPC_CONX Core::JSONRPC::Context +#define INIT_CONX(__X__, __Y__) connection(__X__, __Y__, "") +#define PLUGINHOST_DISPATCHER PluginHost::ILocalDispatcher +#define PLUGINHOST_DISPATCHER_ID PluginHost::ILocalDispatcher::ID diff --git a/tests/mocks/thunder/WorkerPoolImplementation.h b/tests/mocks/thunder/WorkerPoolImplementation.h new file mode 100644 index 000000000..bb7466150 --- /dev/null +++ b/tests/mocks/thunder/WorkerPoolImplementation.h @@ -0,0 +1,63 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#pragma once + +#include + +#include "Module.h" + +class WorkerPoolImplementation : public WPEFramework::Core::WorkerPool { +private: + class Dispatcher : public WPEFramework::Core::ThreadPool::IDispatcher { + public: + Dispatcher(const Dispatcher&) = delete; + Dispatcher& operator=(const Dispatcher&) = delete; + + Dispatcher() = default; + ~Dispatcher() override = default; + + private: + void Initialize() override {} + void Deinitialize() override {} + void Dispatch(WPEFramework::Core::IDispatch* job) override + { + job->Dispatch(); + } + }; + +public: + WorkerPoolImplementation() = delete; + WorkerPoolImplementation(const WorkerPoolImplementation&) = delete; + WorkerPoolImplementation& operator=(const WorkerPoolImplementation&) = delete; + + WorkerPoolImplementation(const uint8_t threads, const uint32_t stackSize, const uint32_t queueSize) + : WPEFramework::Core::WorkerPool(threads - 1, stackSize, queueSize, &_dispatcher) + , _dispatcher() + { + } + + virtual ~WorkerPoolImplementation() + { + WPEFramework::Core::WorkerPool::Stop(); + } + +private: + Dispatcher _dispatcher; +}; diff --git a/tests/mocks/thunder/mockauthservices.h b/tests/mocks/thunder/mockauthservices.h new file mode 100644 index 000000000..35e522a94 --- /dev/null +++ b/tests/mocks/thunder/mockauthservices.h @@ -0,0 +1,36 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2023 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MOCKAUTHSERVICES_H +#define MOCKAUTHSERVICES_H + +#include + +#include "Module.h" + +class MockIAuthenticate : public WPEFramework::PluginHost::IAuthenticate { +public: + MOCK_METHOD(void*, QueryInterfaceByCallsign, (const uint32_t, const string&)); + MOCK_METHOD(uint32_t, CreateToken, (uint16_t, const uint8_t*, std::string&)); + MOCK_METHOD(uint32_t, Release, (), (const, override)); + MOCK_METHOD(void*, QueryInterface, (uint32_t), (override)); + MOCK_METHOD(uint32_t, AddRef, (), (const, override)); + MOCK_METHOD(WPEFramework::PluginHost::ISecurity*, Officer, (const std::string& token), (override)); +}; +#endif diff --git a/tests/patches/thunder/SubscribeStub.patch b/tests/patches/thunder/SubscribeStub.patch new file mode 100644 index 000000000..91d16a775 --- /dev/null +++ b/tests/patches/thunder/SubscribeStub.patch @@ -0,0 +1,21 @@ +diff --git a/Source/websocket/JSONRPCLink.h b/Source/websocket/JSONRPCLink.h +--- a/Source/websocket/JSONRPCLink.h 2025-07-21 11:11:43.178011151 -0400 ++++ b/Source/websocket/JSONRPCLink.h 2025-07-21 11:13:11.330611722 -0400 +@@ -1238,12 +1238,15 @@ namespace WPEFramework { + template + uint32_t Subscribe(const uint32_t waitTime, const string& eventName, const METHOD& method) + { +- return Subscribe(waitTime, eventName, method); ++ const string parameters("{ \"event\": \"" + eventName + "\"}"); ++ if (eventName.empty() == false) ++ method(parameters); ++ return 0; + } + template + uint32_t Subscribe(const uint32_t waitTime, const string& eventName, const METHOD& method, REALOBJECT* objectPtr) + { +- return Subscribe(waitTime, eventName, method, objectPtr); ++ return 0; + } + bool IsActivated() + { diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..dd13d9bbe --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,30 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("building tools") + +option(ENABLE_PLUGIN_CLI "Enable Plugin cli" OFF) +option(ENABLE_ROUTER_DISCOVERY_TOOL "Enable UPNP based Router Details discovery tool" OFF) + +if(ENABLE_PLUGIN_CLI) + add_subdirectory(plugincli) +endif(ENABLE_PLUGIN_CLI) + +if(ENABLE_ROUTER_DISCOVERY_TOOL) + add_subdirectory(upnp) +endif(ENABLE_ROUTER_DISCOVERY_TOOL) diff --git a/tools/plugincli/CMakeLists.txt b/tools/plugincli/CMakeLists.txt new file mode 100644 index 000000000..6784ff846 --- /dev/null +++ b/tools/plugincli/CMakeLists.txt @@ -0,0 +1,124 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +message("Building plugin CLI tools") + +# Required packages +find_package(PkgConfig REQUIRED) +find_package(${NAMESPACE}Core REQUIRED) + +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(GIO REQUIRED gio-2.0) +pkg_check_modules(LIBNM REQUIRED libnm) + +set(COMMON_COMPILE_OPTIONS + -g + -Wall + -Wno-deprecated-declarations + -include ${CMAKE_SOURCE_DIR}/interface/INetworkManager.h +) + +set(COMMON_SOURCES + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerLogger.cpp + ${CMAKE_SOURCE_DIR}/plugin/NetworkManagerSecretAgent.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeUtils.cpp +) + +set(COMMON_INCLUDES + ${GLIB_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS} + ${LIBNM_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/plugin + ${CMAKE_SOURCE_DIR}/interface + ${CMAKE_SOURCE_DIR}/plugin/gnome + ${CMAKE_SOURCE_DIR}/plugin/gnome/gdbus +) + +set(COMMON_LIBS + ${NAMESPACE}Core::${NAMESPACE}Core + ${GLIB_LIBRARIES} + ${GIO_LIBRARIES} +) + +if(ENABLE_GNOME_GDBUS) + + message("Building plugin GDBUS CLI tool...") + + set(PLUGIN_GDBUS_CLI "nm-cli-gdbus") + set(GDBUS_SOURCES + ${CMAKE_SOURCE_DIR}/plugin/gnome/gdbus/NetworkManagerGdbusClient.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/gdbus/NetworkManagerGdbusMgr.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/gdbus/NetworkManagerGdbusUtils.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/gdbus/NetworkManagerGdbusEvent.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/NetworkManagerGdbusTest.cpp + ) + + add_executable(${PLUGIN_GDBUS_CLI} + ${COMMON_SOURCES} + ${GDBUS_SOURCES} + ) + + set_target_properties(${PLUGIN_GDBUS_CLI} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + ) + + set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/NetworkManagerGdbusTest.cpp + PROPERTIES COMPILE_FLAGS "-w" + ) + + target_compile_options(${PLUGIN_GDBUS_CLI} PRIVATE ${COMMON_COMPILE_OPTIONS}) + target_include_directories(${PLUGIN_GDBUS_CLI} PRIVATE ${COMMON_INCLUDES}) + target_link_libraries(${PLUGIN_GDBUS_CLI} PRIVATE ${COMMON_LIBS} ${LIBNM_LIBRARIES} uuid) + + install(TARGETS ${PLUGIN_GDBUS_CLI} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + +else() + + message("Building plugin Libnm CLI tool...") + + set(PLUGIN_LIBNM_CLI "nm-cli-libnm") + set(LIBNM_SOURCES + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeEvents.cpp + ${CMAKE_SOURCE_DIR}/plugin/gnome/NetworkManagerGnomeWIFI.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/NetworkManagerLibnmTest.cpp + ) + + add_executable(${PLUGIN_LIBNM_CLI} + ${COMMON_SOURCES} + ${LIBNM_SOURCES} + ) + + set_target_properties(${PLUGIN_LIBNM_CLI} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + ) + + set_source_files_properties( + ${CMAKE_CURRENT_SOURCE_DIR}/NetworkManagerLibnmTest.cpp + PROPERTIES COMPILE_FLAGS "-w" + ) + + target_compile_options(${PLUGIN_LIBNM_CLI} PRIVATE ${COMMON_COMPILE_OPTIONS}) + target_include_directories(${PLUGIN_LIBNM_CLI} PRIVATE ${COMMON_INCLUDES}) + target_link_libraries(${PLUGIN_LIBNM_CLI} PRIVATE ${COMMON_LIBS} ${LIBNM_LIBRARIES}) + + install(TARGETS ${PLUGIN_LIBNM_CLI} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) + +endif() diff --git a/tools/plugincli/NetworkManagerGdbusTest.cpp b/tools/plugincli/NetworkManagerGdbusTest.cpp new file mode 100644 index 000000000..abeb5f38a --- /dev/null +++ b/tools/plugincli/NetworkManagerGdbusTest.cpp @@ -0,0 +1,435 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include "NetworkManagerGdbusClient.h" +#include "NetworkManagerGdbusMgr.h" +#include "NetworkManagerLogger.h" +#include "NetworkManagerGdbusEvent.h" +#include "INetworkManager.h" +#include "NetworkManagerGdbusUtils.h" +#include "NetworkManagerImplementation.h" +#include +#include +#include +#include +#include + +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using namespace WPEFramework::Exchange; +using namespace std; + +namespace WPEFramework +{ + namespace Plugin + { + + void NetworkManagerImplementation::ReportInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface) + { + NMLOG_INFO("calling 'ReportInterfaceStateChange' cb"); + } + void NetworkManagerImplementation::ReportActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface) + { + NMLOG_INFO("calling 'ReportActiveInterfaceChange' cb"); + } + void NetworkManagerImplementation::ReportIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status) + { + NMLOG_INFO("calling 'ReportIPAddressChange' cb"); + } + void NetworkManagerImplementation::ReportInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string& reason) + { + NMLOG_INFO("calling 'ReportInternetStatusChange' cb"); + } + void NetworkManagerImplementation::ReportAvailableSSIDs(const JsonArray &arrayofWiFiScanResults) + { + NMLOG_INFO("calling 'ReportAvailableSSIDs' cb"); + } + void NetworkManagerImplementation::ReportWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid) + { + NMLOG_INFO("calling 'ReportWiFiStateChange' cb (state=%d, ssid='%s')", state, ssid.c_str()); + } + void NetworkManagerImplementation::ReportWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality) + { + NMLOG_INFO("calling 'ReportWiFiSignalQualityChange' cb"); + } + + NetworkManagerImplementation* _instance = nullptr; + } +} + +void displayMenu() +{ + std::cout << "\n--- Network Manager API Test Menu ---" << std::endl; + std::cout << "1. Get Known SSIDs" << std::endl; + std::cout << "2. Get Available SSIDs" << std::endl; + std::cout << "3. Get Connected SSID" << std::endl; + std::cout << "4. Add to Known SSIDs" << std::endl; + std::cout << "5. Remove Known SSIDs" << std::endl; + std::cout << "6. Start WiFi Scan" << std::endl; + std::cout << "7. WiFi Connect" << std::endl; + std::cout << "8. WiFi Disconnect" << std::endl; + std::cout << "9. Get WiFi State" << std::endl; + std::cout << "10. Get WiFi Signal Strength" << std::endl; + std::cout << "11. GetAvailableInterface" << std::endl; + std::cout << "12. SetInterfaceState" << std::endl; + std::cout << "13. SetIPSettings" << std::endl; + std::cout << "14. GetPrimaryInterface" << std::endl; + std::cout << "15. GetInterfaceState" << std::endl; + std::cout << "16. GetIPSettings" << std::endl; + std::cout << "0. Exit" << std::endl; + std::cout << "-------------------------------------" << std::endl; +} + +WPEFramework::Exchange::INetworkManager::WIFISecurityMode getSecurityType() +{ + int securityChoice; + std::cout << "Select Security Type:" << std::endl; + std::cout << "1. WPA-PSK" << std::endl; + std::cout << "2. SAE" << std::endl; + std::cout << "3. Open (No Security)" << std::endl; + std::cin >> securityChoice; + + switch (securityChoice) { + case 1: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_WPA_PSK; + case 2: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_SAE; + case 3: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_NONE; + default: + std::cout << "Invalid choice. Defaulting to open." << std::endl; + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_NONE; + } +} + +void printSSIDs(const std::list& ssids) +{ + if (ssids.empty()) { + std::cout << "No SSIDs found" << std::endl; + } else { + std::cout << "SSIDs:" << std::endl; + for (const auto& ssid : ssids) { + std::cout << "- " << ssid << std::endl; + } + } +} + + +int main() +{ + + NetworkManagerClient* nmClient = NetworkManagerClient::getInstance(); + NetworkManagerEvents* nmEvents = NetworkManagerEvents::getInstance(); + int choice = -1; + + while (choice != 0) { + displayMenu(); + std::cout << "Enter your choice: \n"; + std::cin >> choice; + + switch (choice) { + case 1: { + std::list ssids; + if (nmClient->getKnownSSIDs(ssids)) { + printSSIDs(ssids); + } else { + NMLOG_ERROR("Failed to get known SSIDs"); + } + break; + } + + case 2: { + std::list ssids; + // if (nmClient->getAvailableSSIDs(ssids)) { + // printSSIDs(ssids); + // } else { + // NMLOG_ERROR("Failed to get available SSIDs"); + // } + break; + } + + case 3: { + Exchange::INetworkManager::WiFiSSIDInfo ssidinfo; + if (nmClient->getConnectedSSID(ssidinfo)) { + NMLOG_INFO("Connected SSID: %s", ssidinfo.ssid.c_str()); + } else { + NMLOG_ERROR("Failed to get connected SSID"); + } + break; + } + + case 4: { + std::string ssid, passphrase; + std::cout << "Enter SSID to add : "; + std::cin.ignore(); + std::getline(std::cin, ssid); + std::cout << "Enter passphrase: "; + std::getline(std::cin, passphrase); + WPEFramework::Exchange::INetworkManager::WIFISecurityMode securityType = getSecurityType(); + Exchange::INetworkManager::WiFiConnectTo ssidinfo = { + .ssid = ssid, + .passphrase = passphrase, + .security = securityType, + .persist = true, + }; + if (nmClient->addToKnownSSIDs(ssidinfo)) { + NMLOG_INFO("SSID added to known list successfully"); + } else { + NMLOG_ERROR("Failed to add SSID to known list"); + } + break; + } + + case 5: { + std::string ssid; + std::cout << "Enter SSID to remove: "; + std::cin >> ssid; + if (nmClient->removeKnownSSIDs(ssid)) { + NMLOG_INFO("SSID removed successfully"); + } else { + NMLOG_ERROR("Failed to remove SSID"); + } + break; + } + + case 6: { + std::string ssid; + std::cout << "Enter SSID to scan (leave blank for all): "; + std::cin.ignore(); + std::getline(std::cin, ssid); + nmEvents->setwifiScanOptions(true); + if (nmClient->startWifiScan(ssid)) { + NMLOG_INFO("WiFi scan started successfully"); + } else { + NMLOG_ERROR("Failed to start WiFi scan"); + } + break; + } + + case 7: { + std::string ssid, passphrase; + bool persist; + + std::cout << "Enter SSID to connect: "; + std::cin.ignore(); + std::getline(std::cin, ssid); + + std::cout << "Enter passphrase: "; + std::getline(std::cin, passphrase); + + WPEFramework::Exchange::INetworkManager::WIFISecurityMode securityType = getSecurityType(); + + std::cout << "Persist SSID info? (1 for yes, 0 for no): "; + std::cin >> persist; + + Exchange::INetworkManager::WiFiConnectTo ssidinfo = { + .ssid = ssid, + .passphrase = passphrase, + .security = securityType, + .persist = persist + }; + + if (nmClient->wifiConnect(ssidinfo)) { + NMLOG_INFO("Connected to WiFi successfully"); + } else { + NMLOG_ERROR("Failed to connect to WiFi"); + } + break; + } + + case 8: { + if (nmClient->wifiDisconnect()) { + NMLOG_INFO("Disconnected from WiFi successfully"); + } else { + NMLOG_ERROR("Failed to disconnect from WiFi"); + } + break; + } + + case 9: { + Exchange::INetworkManager::WiFiState state; + if (nmClient->getWifiState(state)) { + NMLOG_INFO("WiFi State: %d", state); // Assuming state is an enum or int + } else { + NMLOG_ERROR("Failed to get WiFi state"); + } + break; + } + + case 10: { + std::string ssid; + int signalStrength; + Exchange::INetworkManager::WiFiSignalQuality quality; + if (nmClient->getWiFiSignalQuality(ssid, signalStrength, quality)) { + NMLOG_INFO("SSID: %s, Signal Strength: %d, Quality: %d", ssid.c_str(), signalStrength, quality); + } else { + NMLOG_ERROR("Failed to get WiFi signal strength"); + } + break; + } + case 11: { + std::vector interfaceList; + if(nmClient->getAvailableInterfaces(interfaceList)){ + for (const auto& interface : interfaceList) { + NMLOG_INFO("interface.type = %d interface.name = %s interface.mac = %s interface.enabled = %d interface.connected = %d", interface.type, interface.name.c_str(), interface.mac.c_str(), interface.enabled, interface.connected); + } + } + else + { + NMLOG_ERROR("Failed to get Available Interfaces"); + } + break; + } + case 12: { + std::string interface; + std::string input; + bool enable; + std::cout << "Enter interface name to change the state: "; + std::cin.ignore(); + std::getline(std::cin, interface); + std::cout << "Enable Interface(input true/false): "; + std::getline(std::cin, input); + enable = (input == "true")? true: false; + + if(nmClient->setInterfaceState(interface, enable)){ + NMLOG_INFO("setInterfaceState successful"); + } + else + { + NMLOG_ERROR("Failed to set Interface State"); + } + break; + } + case 13: { + std::string interface; + std::string ipVersion; + std::string input; + bool autoConfig; + std::string ipAddress; + uint32_t prefix = 0; + std::string gateway; + std::string primarydns; + std::string secondarydns; + Exchange::INetworkManager::IPAddress address; + std::cout << "Enter interface: "; + std::cin.ignore(); + std::getline(std::cin, interface); + + std::cout << "Enter IP version: (IPv4/IPv6)"; + std::getline(std::cin, ipVersion); + + std::cout << "Enter auto configuration setting(input true/false): "; + std::getline(std::cin, input); + autoConfig = (input == "true")? true: false; + + if(!autoConfig) + { + std::cout << "Enter IP address: "; + std::getline(std::cin, ipAddress); + + // For prefix, use std::cin because it's a uint32_t + std::cout << "Enter prefix length: "; + std::cin >> prefix; + std::cin.ignore(std::numeric_limits::max(), '\n'); // Ignore leftover newline + + std::cout << "Enter gateway: "; + std::getline(std::cin, gateway); + + std::cout << "Enter primary DNS: "; + std::getline(std::cin, primarydns); + + std::cout << "Enter secondary DNS: "; + std::getline(std::cin, secondarydns); + } + address.ipversion = ipVersion; + address.autoconfig = autoConfig; + address.ipaddress = ipAddress; + address.prefix = prefix; + address.gateway = gateway; + address.primarydns = primarydns; + address.secondarydns = secondarydns; + if(nmClient->setIPSettings(interface, address)){ + NMLOG_INFO("setIPSettings successful"); + } + else + { + NMLOG_ERROR("Failed to set IP settings"); + } + break; + } + case 14: { + std::string interface; + + if(nmClient->getPrimaryInterface(interface)){ + NMLOG_INFO("Primary Interface = %s", interface.c_str()); + } + else + { + NMLOG_ERROR("Failed to Get Primary Interface"); + } + break; + } + case 15: { + std::string interface; + bool isEnabled; + std::cout << "Enter interface: "; + std::cin.ignore(); + std::getline(std::cin, interface); + if(nmClient->getInterfaceState(interface, isEnabled)){ + NMLOG_INFO("Interface %s is %s", interface.c_str(), isEnabled ? "enabled": "disabled"); + } + else + { + NMLOG_ERROR("Failed to Get Interface state"); + } + break; + } + case 16: { + std::string interface; + std::string ipVersion; + Exchange::INetworkManager::IPAddress result; + std::cout << "Enter interface: "; + std::cin.ignore(); + std::getline(std::cin, interface); + std::cout << "Enter IP version(IPv4/IPv6): "; + std::getline(std::cin, ipVersion); + if(nmClient->getIPSettings(interface, ipVersion, result)){ + NMLOG_INFO("\nresult.ipversion = %s\n result.autoconfig = %d\n result.dhcpserver = %s\n result.ula = %s\n result.ipaddress = %s\n result.prefix = %d\nresult.gateway = %s\n result.primarydns = %s\n result.secondarydns = %s\n", result.ipversion.c_str(), result.autoconfig, result.dhcpserver.c_str(), result.ula.c_str(), result.ipaddress.c_str(), result.prefix, result.gateway.c_str(), result.primarydns.c_str(), result.secondarydns.c_str()); + } + else + { + NMLOG_ERROR("Failed to get IP Settings"); + } + break; + } + + + case 0: + std::cout << "Exiting program." << std::endl; + break; + + default: + std::cout << "Invalid choice, please try again." << std::endl; + break; + } + } + + NMLOG_INFO("Program completed successfully"); + nmEvents->stopNetworkMangerEventMonitor(); + return 0; +} diff --git a/tools/plugincli/NetworkManagerLibnmTest.cpp b/tools/plugincli/NetworkManagerLibnmTest.cpp new file mode 100644 index 000000000..34ece13ce --- /dev/null +++ b/tools/plugincli/NetworkManagerLibnmTest.cpp @@ -0,0 +1,392 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2023 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ +#include "NetworkManagerLogger.h" +#include "NetworkManagerGnomeEvents.h" +#include "INetworkManager.h" +#include "NetworkManagerGnomeUtils.h" +#include "NetworkManagerImplementation.h" +#include "NetworkManagerGnomeWIFI.h" +#include +#include +#include +#include +#include + +using namespace WPEFramework; +using namespace WPEFramework::Plugin; +using namespace WPEFramework::Exchange; +using namespace std; + +namespace WPEFramework +{ + namespace Plugin + { + + void NetworkManagerImplementation::ReportInterfaceStateChange(const Exchange::INetworkManager::InterfaceState state, const string interface) + { + NMLOG_INFO("calling 'ReportInterfaceStateChange' cb"); + } + void NetworkManagerImplementation::ReportActiveInterfaceChange(const string prevActiveInterface, const string currentActiveinterface) + { + NMLOG_INFO("calling 'ReportActiveInterfaceChange' cb"); + } + void NetworkManagerImplementation::ReportIPAddressChange(const string interface, const string ipversion, const string ipaddress, const Exchange::INetworkManager::IPStatus status) + { + NMLOG_INFO("calling 'ReportIPAddressChange' cb"); + } + void NetworkManagerImplementation::ReportInternetStatusChange(const Exchange::INetworkManager::InternetStatus prevState, const Exchange::INetworkManager::InternetStatus currState, const string interface, const string& reason) + { + NMLOG_INFO("calling 'ReportInternetStatusChange' cb"); + } + void NetworkManagerImplementation::ReportAvailableSSIDs(const JsonArray &arrayofWiFiScanResults) + { + NMLOG_INFO("calling 'ReportAvailableSSIDs' cb"); + } + void NetworkManagerImplementation::ReportWiFiStateChange(const Exchange::INetworkManager::WiFiState state, const string ssid) + { + NMLOG_INFO("calling 'ReportWiFiStateChange' cb"); + } + void NetworkManagerImplementation::ReportWiFiSignalQualityChange(const string ssid, const int strength, const int noise, const int snr, const Exchange::INetworkManager::WiFiSignalQuality quality) + { + NMLOG_INFO("calling 'ReportWiFiSignalQualityChange' cb"); + } + + NetworkManagerImplementation* _instance = nullptr; + } +} + +void displayMenu() +{ + std::cout << "\n--- Network Manager API Test Menu ---" << std::endl; + std::cout << "1. Get Known SSIDs" << std::endl; + std::cout << "2. Get Connected SSID" << std::endl; + std::cout << "3. Add to Known SSIDs" << std::endl; + std::cout << "4. Remove Known SSIDs" << std::endl; + std::cout << "5. Start WiFi Scan" << std::endl; + std::cout << "6. WiFi Connect" << std::endl; + std::cout << "7. WiFi Disconnect" << std::endl; + std::cout << "8. Get WiFi State" << std::endl; + std::cout << "9. start WPS" << std::endl; + std::cout << "10. stop WPS" << std::endl; + std::cout << "11. Get WiFi Signal Strength" << std::endl; + std::cout << "12. GetAvailableInterface" << std::endl; + std::cout << "13. GetPrimaryInterface" << std::endl; + std::cout << "14. SetPrimaryInterface" << std::endl; + std::cout << "15. SetInterfaceState" << std::endl; + std::cout << "16. GetInterfaceState" << std::endl; + std::cout << "17. GetIPSettings" << std::endl; + std::cout << "18. SetIPSettings" << std::endl; + std::cout << "0. Exit" << std::endl; + std::cout << "-------------------------------------" << std::endl; +} + +WPEFramework::Exchange::INetworkManager::WIFISecurityMode getSecurityType() +{ + int securityChoice; + std::cout << "Select Security Type:" << std::endl; + std::cout << "1. WIFI_SECURITY_NONE" << std::endl; + std::cout << "2. WIFI_SECURITY_WPA_PSK" << std::endl; + std::cout << "3. WIFI_SECURITY_SAE" << std::endl; + std::cout << "4. WIFI_SECURITY_EAP" << std::endl; + std::cin >> securityChoice; + + switch (securityChoice) { + case 4: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_EAP; + case 3: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_SAE; + case 2: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_WPA_PSK; + case 1: + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_NONE; + default: + std::cout << "Invalid choice. Defaulting to open." << std::endl; + return WPEFramework::Exchange::INetworkManager::WIFI_SECURITY_NONE; + } +} + +void printSSIDs(const std::list& ssids) +{ + if (ssids.empty()) { + std::cout << "No SSIDs found" << std::endl; + } else { + std::cout << "SSIDs:" << std::endl; + for (const auto& ssid : ssids) { + std::cout << "- " << ssid << std::endl; + } + } +} + +int main() +{ + int choice = -1; + NMLOG_INFO("Starting NetworkManager libnm cli tool"); + NetworkManagerLogger::SetLevel(NetworkManagerLogger::DEBUG_LEVEL); + GnomeNetworkManagerEvents* nmEvent = GnomeNetworkManagerEvents::getInstance(); + wifiManager *wifiMgr = wifiManager::getInstance(); + nmEvent->startNetworkMangerEventMonitor(); + if (wifiMgr == nullptr) { + NMLOG_ERROR("Failed to create wifiManager instance"); + return -1; + } + NMLOG_INFO("wifiManager instance created successfully"); + while (choice != 0) + { + displayMenu(); + std::cout << "Enter your choice: \n"; + std::cin >> choice; + switch (choice) + { + case 0: + break; + case 1: { + std::list ssids; + if (wifiMgr->getKnownSSIDs(ssids)) { + printSSIDs(ssids); + } else { + NMLOG_ERROR("Failed to get available SSIDs"); + } + break; + } + case 2: { + Exchange::INetworkManager::WiFiSSIDInfo ssidinfo; + if (wifiMgr->wifiConnectedSSIDInfo(ssidinfo)) + NMLOG_INFO("Connected SSID: %s", ssidinfo.ssid.c_str()); + else + NMLOG_ERROR("Failed to get connected SSID"); + break; + } + case 3: { + std::string ssid, passphrase; + std::cout << "Enter SSID to add : "; + std::cin.ignore(); + std::getline(std::cin, ssid); + std::cout << "Enter passphrase: "; + std::getline(std::cin, passphrase); + WPEFramework::Exchange::INetworkManager::WIFISecurityMode securityType = getSecurityType(); + Exchange::INetworkManager::WiFiConnectTo ssidinfo = { + .ssid = ssid, + .passphrase = passphrase, + .security = securityType, + .persist = true, + }; + if (wifiMgr->addToKnownSSIDs(ssidinfo)) { + NMLOG_INFO("SSID added to known list successfully"); + } else { + NMLOG_ERROR("Failed to add SSID to known list"); + } + break; + } + case 4: { + std::string ssid; + std::cout << "Enter know ssid to remove ? (leave blank for all): "; + std::cin.ignore(); + std::getline(std::cin, ssid); + if (wifiMgr->removeKnownSSID(ssid)) { + NMLOG_INFO("SSID removed successfully"); + } else { + NMLOG_ERROR("Failed to remove SSID"); + } + break; + } + case 5: + { + std::string ssid; + std::cout << "Enter SSID to scan (leave blank for all): "; + + if (std::cin.peek() == '\n') { + std::cin.ignore(); + } + + std::getline(std::cin, ssid); + + nmEvent->setwifiScanOptions(true); // If this triggers scan customization + + NMLOG_INFO("Sending WiFi scan request%s", ssid.empty() ? " (all SSIDs)" : (" for SSID: " + ssid).c_str()); + + bool scanRequestSent = ssid.empty() ? wifiMgr->wifiScanRequest() : wifiMgr->wifiScanRequest({ssid}); + if (scanRequestSent) { + NMLOG_INFO("WiFi scan request sent successfully."); + } else { + NMLOG_ERROR("Failed to send WiFi scan request."); + } + break; + } + case 6: + { + std::string ssid, passphrase; + bool persist; + + std::cout << "Enter SSID to connect: "; + std::cin.ignore(); + std::getline(std::cin, ssid); + + std::cout << "Enter passphrase: "; + std::getline(std::cin, passphrase); + + WPEFramework::Exchange::INetworkManager::WIFISecurityMode securityType = getSecurityType(); + + std::cout << "Persist SSID info? (1 for yes, 0 for no): "; + std::cin >> persist; + + Exchange::INetworkManager::WiFiConnectTo ssidinfo = { + .ssid = ssid, + .passphrase = passphrase, + .security = securityType, + .persist = persist + }; + + if (wifiMgr->wifiConnect(ssidinfo)) { + NMLOG_INFO("Connected to WiFi successfully"); + } else { + NMLOG_ERROR("Failed to connect to WiFi"); + } + break; + } + case 7: { + if (wifiMgr->wifiDisconnect()) { + NMLOG_INFO("Disconnected from WiFi successfully"); + } else { + NMLOG_ERROR("Failed to disconnect from WiFi"); + } + break; + } + case 8: { + Exchange::INetworkManager::WiFiState state; + if (wifiMgr->getWifiState(state)) { + NMLOG_INFO("WiFi State: %d", state); // Assuming state is an enum or int + } else { + NMLOG_ERROR("Failed to get WiFi state"); + } + break; + } + case 9: { + if (wifiMgr->startWPS()) { + NMLOG_INFO("WPS started successfully"); + } else { + NMLOG_ERROR("Failed to start WPS"); + } + break; + } + case 10: { + if (wifiMgr->stopWPS()) { + NMLOG_INFO("WPS started successfully"); + } else { + NMLOG_ERROR("Failed to start WPS"); + } + break; + } + case 11: { + std::string interface; + std::string input; + bool enable; + std::cout << "Enter interface name to change the state: "; + std::cin.ignore(); + std::getline(std::cin, interface); + std::cout << "Enable Interface(input true/false): "; + std::getline(std::cin, input); + enable = (input == "true")? true: false; + + if(wifiMgr->setInterfaceState(interface, enable)){ + NMLOG_INFO("setInterfaceState successful"); + } + else + { + NMLOG_ERROR("Failed to set Interface State"); + } + break; + } + case 12: { + std::string interface; + std::string ipVersion; + std::string input; + bool autoConfig; + std::string ipAddress; + uint32_t prefix = 0; + std::string gateway; + std::string primarydns; + std::string secondarydns; + Exchange::INetworkManager::IPAddress address; + std::cout << "Enter interface: "; + std::cin.ignore(); + std::getline(std::cin, interface); + + std::cout << "Enter IP version: (IPv4/IPv6)"; + std::getline(std::cin, ipVersion); + + std::cout << "Enter auto configuration setting(input true/false): "; + std::getline(std::cin, input); + autoConfig = (input == "true")? true: false; + + if(!autoConfig) + { + std::cout << "Enter IP address: "; + std::getline(std::cin, ipAddress); + + // For prefix, use std::cin because it's a uint32_t + std::cout << "Enter prefix length: "; + std::cin >> prefix; + std::cin.ignore(std::numeric_limits::max(), '\n'); // Ignore leftover newline + + std::cout << "Enter gateway: "; + std::getline(std::cin, gateway); + + std::cout << "Enter primary DNS: "; + std::getline(std::cin, primarydns); + + std::cout << "Enter secondary DNS: "; + std::getline(std::cin, secondarydns); + } + address.ipversion = ipVersion; + address.autoconfig = autoConfig; + address.ipaddress = ipAddress; + address.prefix = prefix; + address.gateway = gateway; + address.primarydns = primarydns; + address.secondarydns = secondarydns; + if(wifiMgr->setIpSettings(interface, address)){ + NMLOG_INFO("setIPSettings successful"); + } + else + { + NMLOG_ERROR("Failed to set IP settings"); + } + break; + } + case 13: { + std::string interface; + std::cout << "Enter interface name to set to: "; + std::cin.ignore(); + std::getline(std::cin, interface); + if (wifiMgr->setPrimaryInterface(interface)) { + NMLOG_INFO("setPrimaryInterface successful"); + } else { + NMLOG_ERROR("Failed to setPrimaryInterface"); + } + break; + } + default: + std::cout << "Invalid choice. Please try again." << std::endl; + break; + } + } + NMLOG_INFO("cliTool completed successfully"); + nmEvent->stopNetworkMangerEventMonitor(); + return 0; +} diff --git a/tools/upnp/CMakeLists.txt b/tools/upnp/CMakeLists.txt new file mode 100644 index 000000000..788f0540a --- /dev/null +++ b/tools/upnp/CMakeLists.txt @@ -0,0 +1,70 @@ +############################################################################# +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################# +cmake_minimum_required(VERSION 3.3) + +set(MODULE_NAME routerDiscovery) +find_package(PkgConfig REQUIRED) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4) +pkg_check_modules(GSSDP REQUIRED gssdp-1.2) +pkg_check_modules(GUPNP gupnp-1.2) + +if (NOT GUPNP_FOUND) + pkg_check_modules(GUPNP REQUIRED gupnp-1.0) + add_definitions(-DGUPNP_1_0) +endif () + +if (USE_TELEMETRY) + include_directories(${T2_INCLUDE_DIRS}) +endif (USE_TELEMETRY) + +add_definitions(-DENABLE_ROUTER_DISCOVERY_MAIN) + +add_executable(${MODULE_NAME} UpnpDiscoveryManager.cpp) +target_include_directories(${MODULE_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${GLIB_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS} ${GUPNP_INCLUDE_DIRS} ${GSSDP_INCLUDE_DIRS}) +target_link_libraries(${MODULE_NAME} PRIVATE ${GLIB_LIBRARIES} ${LIBSOUP_LIBRARIES} ${GUPNP_LIBRARIES} ${GSSDP_LIBRARIES}) + +if (USE_TELEMETRY) + target_link_libraries(${MODULE_NAME} PRIVATE ${T2_LIBRARIES}) +endif (USE_TELEMETRY) + +install(TARGETS ${MODULE_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if(ENABLE_GNOME_NETWORKMANAGER) +install(PROGRAMS scripts/getRouterInfo-NMdispatcher.sh + DESTINATION ${CMAKE_INSTALL_PREFIX}/../lib/rdk + RENAME getRouterInfo.sh) +else () +install(PROGRAMS scripts/getRouterInfo-nlmon.sh + DESTINATION ${CMAKE_INSTALL_PREFIX}/../lib/rdk + RENAME getRouterInfo.sh) +endif () + +install(PROGRAMS + scripts/readyToGetRouterInfo.sh + DESTINATION + ${CMAKE_INSTALL_PREFIX}/../lib/rdk) + +install(FILES + scripts/routerDiscovery@.service + DESTINATION + ${CMAKE_INSTALL_PREFIX}/../lib/systemd/system) diff --git a/tools/upnp/UpnpDiscoveryManager.cpp b/tools/upnp/UpnpDiscoveryManager.cpp new file mode 100644 index 000000000..bb47fc6fd --- /dev/null +++ b/tools/upnp/UpnpDiscoveryManager.cpp @@ -0,0 +1,244 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#include +#include "UpnpDiscoveryManager.h" + +UpnpDiscoveryManager::UpnpDiscoveryManager() +{ + m_context = NULL; + m_controlPoint = NULL; + m_mainLoop = NULL; + m_gatewayDetails.str(""); + m_gatewayDetails.clear(); + m_deviceInternetGateway = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; +#if USE_TELEMETRY + // Initialize Telemtry + t2_init("upnpdiscover"); +#endif +} + +UpnpDiscoveryManager::~UpnpDiscoveryManager() +{ + if (m_controlPoint) + g_object_unref(m_controlPoint); + if (m_context) + g_object_unref(m_context); + if (m_mainLoop) + { + g_main_loop_quit(m_mainLoop); + g_main_loop_unref(m_mainLoop); + } +} + +/* @brief Initialise GUPNP context */ +gboolean UpnpDiscoveryManager::initialiseUpnp(const std::string& interface) +{ + GError *error = NULL; + uint8_t errCount = 0; + + do + { + // Create a gupnp context + #ifdef GUPNP_1_0 + m_context = gupnp_context_new(NULL, interface.c_str(), UPNP_DISCOVERY_PORT, &error); + #else + m_context = gupnp_context_new(interface.c_str(), UPNP_DISCOVERY_PORT, &error); + #endif + if (!m_context) + { + LOG_ERR("Error creating Upnp context: %s", error->message); + g_clear_error(&error); + errCount++; + sleep(2); + } + } while ((m_context == NULL) && (errCount < UPNP_MAX_CONTEXT_FAIL)); + if (errCount == UPNP_MAX_CONTEXT_FAIL) + { + LOG_ERR("Context creation failed"); + return false; + } + + // Create a control point for InternetGatewayDevice + m_controlPoint = gupnp_control_point_new(m_context, m_deviceInternetGateway.c_str()); + if (!m_controlPoint) + { + LOG_ERR("Error creating control point"); + return false; + } + + // Connects a callback function to a signal for InternetGatewayDevice + g_signal_connect(m_controlPoint, "device-proxy-available", + G_CALLBACK(&UpnpDiscoveryManager::deviceProxyAvailableCallback), this); + return true; +} + +/* @brief Telemetry Logging */ +void UpnpDiscoveryManager::logTelemetry(std::string message) +{ +#if USE_TELEMETRY + //T2 telemtery logging + T2ERROR t2error = t2_event_s("Router_Discovered", (char*)message.c_str()); + if (t2error != T2ERROR_SUCCESS) + { + LOG_ERR("t2_event_s(\"%s\", \"%s\") returned error code %d", "Router_Discovered", message.c_str(), t2error); + } +#endif +} + +/* @brief SSDP Discovery Timeout */ +gboolean UpnpDiscoveryManager::discoveryTimeout(void *arg) +{ + auto manager = static_cast(arg); + manager->stopSearchGatewayDevice(); + manager->m_gatewayDetails << "Unknown"; + LOG_INFO("Router_Discovered : %s", manager->m_gatewayDetails.str().c_str()); + manager->logTelemetry(manager->m_gatewayDetails.str().c_str()); + manager->exitWait(); + return true; +} + +/* @brief Find the gateway details by sending SSDP discovery on wifi/ethernet interface */ +bool UpnpDiscoveryManager::findGatewayDevice(const std::string& interface) +{ + m_interface = interface; + //Initialise gupnp context + if (true == initialiseUpnp(interface)) + { + //Create timer to handle upnp discovery timeout + g_timeout_add_seconds (UPNP_DISCOVERY_TIMEOUT_IN_SEC, GSourceFunc(&UpnpDiscoveryManager::discoveryTimeout), this); + // Start discovery to find InternetGatewayDevice + gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(m_controlPoint), TRUE); + return true; + } + else + { + m_gatewayDetails << "Unknown"; + LOG_INFO("Router_Discovered : %s", m_gatewayDetails.str().c_str()); + logTelemetry(m_gatewayDetails.str().c_str()); + return false; + } +} + +/* @brief Callback getting invoked when SSDP reply is received from gateway */ +void UpnpDiscoveryManager::on_device_proxy_available(GUPnPControlPoint *controlPoint, GUPnPDeviceProxy *proxy) +{ + char* make = gupnp_device_info_get_manufacturer(GUPNP_DEVICE_INFO(proxy)); + if (make != NULL) { + m_apMake = make; + } else { + m_apMake = "Unknown"; + } + char* modelName = gupnp_device_info_get_model_name(GUPNP_DEVICE_INFO(proxy)); + if (modelName != NULL) { + m_apModelName = modelName; + } else { + m_apModelName = "Unknown"; + } + char* modelNumber = gupnp_device_info_get_model_number(GUPNP_DEVICE_INFO(proxy)); + if (modelNumber != NULL) { + m_apModelNumber = modelNumber; + } else { + m_apModelNumber = "Unknown"; + } + // Remove the string after symbol <,> For example: NETGEAR,Inc. + m_apMake = m_apMake.substr(0, m_apMake.find(',')); + m_apModelName = m_apModelName.substr(0, m_apModelName.find(',')); + m_apModelNumber = m_apModelNumber.substr(0, m_apModelNumber.find(',')); + // Stop discovery to find InternetGatewayDevice + stopSearchGatewayDevice(); + m_gatewayDetails << m_apMake << "," << m_apModelName << "," << m_apModelNumber; + LOG_INFO("Router_Discovered : %s", m_gatewayDetails.str().c_str()); + if (m_gatewayDetails.str().length() >= UPNP_T2_EVENT_DATA_LEN) + { + m_gatewayDetails.str(""); + m_gatewayDetails.clear(); + m_gatewayDetails << m_apModelName << "," << m_apModelNumber; + } + logTelemetry(m_gatewayDetails.str()); + // If router make/modelName/modelNumber is not available, log all other available router details + if ((m_apMake == "Unknown") || (m_apModelName == "Unknown") || (m_apModelNumber == "Unknown")) + { + const char* location = gupnp_device_info_get_location(GUPNP_DEVICE_INFO(proxy)); + if (location != NULL) + LOG_INFO("Location URL: %s", location); + char* name = gupnp_device_info_get_friendly_name(GUPNP_DEVICE_INFO(proxy)); + if (name != NULL) + LOG_INFO("Friendly name: %s", name); + char* manuUrl = gupnp_device_info_get_manufacturer_url(GUPNP_DEVICE_INFO(proxy)); + if (manuUrl != NULL) + LOG_INFO("Manufacturer URL: %s", manuUrl); + char* modelDes = gupnp_device_info_get_model_description(GUPNP_DEVICE_INFO(proxy)); + if (modelDes != NULL) + LOG_INFO("Model Description: %s", modelDes); + char* modelUrl = gupnp_device_info_get_model_url(GUPNP_DEVICE_INFO(proxy)); + if (modelUrl != NULL) + LOG_INFO("Model URL: %s", modelUrl); + char* serialNum = gupnp_device_info_get_serial_number(GUPNP_DEVICE_INFO(proxy)); + if (serialNum != NULL) + LOG_INFO("Serial Number: %s", serialNum); + char* upc = gupnp_device_info_get_upc(GUPNP_DEVICE_INFO(proxy)); + if (upc != NULL) + LOG_INFO("Universal Product Code: %s", upc); + } + exitWait(); +} + +/* @brief Stop sending SSDP discovery */ +void UpnpDiscoveryManager::stopSearchGatewayDevice() +{ + gssdp_resource_browser_set_active(GSSDP_RESOURCE_BROWSER(m_controlPoint), FALSE); +} + +/* @brief Wait in gmain() loop */ +void UpnpDiscoveryManager::enterWait() +{ + m_mainLoop = g_main_loop_new(NULL, FALSE); + // gmain loop need to be running for upnp discovery + g_main_loop_run(m_mainLoop); +} + +/* @brief Exit the gmain() loop */ +void UpnpDiscoveryManager::exitWait() +{ + if (m_mainLoop) + { + g_main_loop_quit(m_mainLoop); + g_main_loop_unref(m_mainLoop); + m_mainLoop = NULL; + } +} +#if ENABLE_ROUTER_DISCOVERY_MAIN +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + LOG_INFO("Usage: %s ", argv[0]); + return 1; + } + UpnpDiscoveryManager ssdpDiscover; + LOG_INFO("SSDP discover to fetch InternetGatewayDevice on interface %s", argv[1]); + if (true == ssdpDiscover.findGatewayDevice(argv[1])) + { + ssdpDiscover.enterWait(); + return 0; + } + return 1; +} +#endif diff --git a/tools/upnp/UpnpDiscoveryManager.h b/tools/upnp/UpnpDiscoveryManager.h new file mode 100644 index 000000000..55b3f0584 --- /dev/null +++ b/tools/upnp/UpnpDiscoveryManager.h @@ -0,0 +1,76 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2025 RDK Management +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +**/ + +#ifndef __UPNPDISCOVERYMANAGER_H__ +#define __UPNPDISCOVERYMANAGER_H__ + +#include +#include +#include +#if USE_TELEMETRY +#include +#endif + +#define LOG_ERR(msg, ...) g_printerr("ERROR: " msg "\n", ##__VA_ARGS__) +#define LOG_INFO(msg, ...) g_print("INFO: " msg "\n", ##__VA_ARGS__) + +#define UPNP_MAX_CONTEXT_FAIL 15 +#define UPNP_T2_EVENT_DATA_LEN 128 + +class UpnpDiscoveryManager +{ +public: + UpnpDiscoveryManager(); + ~UpnpDiscoveryManager(); + /* @brief Find the gateway details by sending SSDP discovery on wifi/ethernet interface */ + bool findGatewayDevice(const std::string& interface); + /* @brief Wait in gmain() loop */ + void enterWait(); + +private: + /* @brief Telemetry Logging */ + void logTelemetry(std::string message); + /* @brief SSDP Discovery Timeout */ + static gboolean discoveryTimeout(void* arg); + /* @brief Exit the gmain() loop */ + void exitWait(); + /* @brief Initialise GUPNP context */ + gboolean initialiseUpnp(const std::string& interface); + /* @brief Stop sending SSDP discovery */ + void stopSearchGatewayDevice(); + /* @brief Callback getting invoked when SSDP reply is received from gateway */ + void on_device_proxy_available(GUPnPControlPoint *control_point, GUPnPDeviceProxy *proxy); + static void deviceProxyAvailableCallback(GUPnPControlPoint *control_point, GUPnPDeviceProxy *proxy, gpointer user_data) + { + auto* self = static_cast(user_data); + self->on_device_proxy_available(control_point, proxy); + } + GUPnPContext* m_context; + GUPnPControlPoint* m_controlPoint; + GMainLoop* m_mainLoop; + std::string m_interface; + std::string m_apMake; + std::string m_apModelName; + std::string m_apModelNumber; + std::ostringstream m_gatewayDetails; + std::string m_deviceInternetGateway; + static const guint UPNP_DISCOVERY_PORT = 1901; + static const int UPNP_DISCOVERY_TIMEOUT_IN_SEC = 180; +}; +#endif /* __UPNPDISCOVERYMANAGER_H__ */ diff --git a/tools/upnp/scripts/getRouterInfo-NMdispatcher.sh b/tools/upnp/scripts/getRouterInfo-NMdispatcher.sh new file mode 100755 index 000000000..316c3bc29 --- /dev/null +++ b/tools/upnp/scripts/getRouterInfo-NMdispatcher.sh @@ -0,0 +1,34 @@ +#!/bin/sh +############################################################################## +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +. /etc/device.properties + +ifc=$1 +mode=$2 +#Perform routerDiscovery only in case of v4 IP acquisition +if [ "$mode" == "dhcp4-change" ]; then + # Find the PID of the routerDiscovery process that is running specifically on this interface. + pid=$(pgrep -f "routerDiscovery $ifc") + if [ -n "$pid" ]; then + echo "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") Found existing routerDiscovery ($pid) for $ifc, Killing it." >> /opt/logs/routerInfo.log + kill -15 "$pid" + fi + echo "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") Starting routerDiscovery on $ifc" >> /opt/logs/routerInfo.log + systemctl start routerDiscovery@$ifc +fi diff --git a/tools/upnp/scripts/getRouterInfo-nlmon.sh b/tools/upnp/scripts/getRouterInfo-nlmon.sh new file mode 100755 index 000000000..672c1e8c3 --- /dev/null +++ b/tools/upnp/scripts/getRouterInfo-nlmon.sh @@ -0,0 +1,41 @@ +#!/bin/sh +############################################################################## +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +. /etc/device.properties + +cmd=$1 +mode=$2 +ifc=$3 +flags=$5 +file="/tmp/.routerdiscover" +if [[ "$ifc" == "$WIFI_INTERFACE" || "$ifc" == "$ETHERNET_INTERFACE" ]]; then + if [ "$cmd" == "add" ] && [ "$flags" == "global" ] && [ "$mode" == "ipv4" ]; then + if [ ! -f $file ]; then + touch /tmp/.routerdiscover + # Find the PID of the routerDiscovery process that is running specifically on this interface. + pid=$(pgrep -f "routerDiscovery $ifc") + if [ -n "$pid" ]; then + echo "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") Found existing routerDiscovery ($pid) for $ifc, Killing it." >> /opt/logs/routerInfo.log + kill -15 $pid + fi + echo "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") Starting routerDiscovery on $ifc" >> /opt/logs/routerInfo.log + systemctl start routerDiscovery@$ifc + fi + fi +fi diff --git a/tools/upnp/scripts/readyToGetRouterInfo.sh b/tools/upnp/scripts/readyToGetRouterInfo.sh new file mode 100755 index 000000000..ceb0efd9c --- /dev/null +++ b/tools/upnp/scripts/readyToGetRouterInfo.sh @@ -0,0 +1,29 @@ +#!/bin/sh +############################################################################## +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2025 RDK Management +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +############################################################################## +. /etc/device.properties + +ifc=$1 +file="/tmp/.routerdiscover" +if [[ "$ifc" == "$WIFI_INTERFACE" || "$ifc" == "$ETHERNET_INTERFACE" ]]; then + if [ -f "$file" ]; then + echo "$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ") Ready to read router details" >> /opt/logs/routerInfo.log + rm "$file" + fi +fi diff --git a/tools/upnp/scripts/routerDiscovery@.service b/tools/upnp/scripts/routerDiscovery@.service new file mode 100644 index 000000000..78a1a4f20 --- /dev/null +++ b/tools/upnp/scripts/routerDiscovery@.service @@ -0,0 +1,6 @@ +[Unit] +Description=Router Discovery Service %i + +[Service] +Type=simple +ExecStart=/usr/bin/routerDiscovery %i