Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b6112395 authored by Chris Manton's avatar Chris Manton
Browse files

Introduce stack_btm_power_mode_test

Bug: 295987716
Test: atest net_test_stack_btm

Change-Id: I8e8c5e9bc1204a0003563c7757edda7abfea7535
parent ad8a5c0f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -1421,6 +1421,7 @@ cc_test {
        "test/btm/peer_packet_types_test.cc",
        "test/btm/sco_hci_test.cc",
        "test/btm/sco_pkt_status_test.cc",
        "test/btm/stack_btm_power_mode_test.cc",
        "test/btm/stack_btm_regression_tests.cc",
        "test/btm/stack_btm_test.cc",
        "test/common/mock_eatt.cc",
+6 −3
Original line number Diff line number Diff line
@@ -699,9 +699,12 @@ enum : uint8_t {
  BTM_PM_STS_HOLD = HCI_MODE_HOLD,      // 0x01
  BTM_PM_STS_SNIFF = HCI_MODE_SNIFF,    // 0x02
  BTM_PM_STS_PARK = HCI_MODE_PARK,      // 0x03
  BTM_PM_STS_SSR,     /* report the SSR parameters in HCI_SNIFF_SUB_RATE_EVT */
  BTM_PM_STS_PENDING, /* when waiting for status from controller */
  BTM_PM_STS_ERROR    /* when HCI command status returns error */
  BTM_PM_STS_SSR,      // Hci sniff subrating event reported the SSR parameters
  BTM_PM_STS_PENDING,  // Successful hci mode change command status received and
                       // now waiting for mode change event to indicate
                       // change to desired mode target.
  BTM_PM_STS_ERROR  // Failed hci mode change command status which will result
                    // in no mode change event.
};
typedef uint8_t tBTM_PM_STATUS;

+202 −0
Original line number Diff line number Diff line
/*
 * Copyright 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0(the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <cstdint>

#include "btm_api_types.h"
#include "gd/common/init_flags.h"
#include "gd/os/log.h"
#include "stack/include/acl_api.h"
#include "stack/include/acl_hci_link_interface.h"
#include "stack/include/btm_api.h"
#include "stack/include/hci_error_code.h"
#include "test/common/mock_functions.h"
#include "types/raw_address.h"

namespace {
const char* test_flags[] = {
    "INIT_default_log_level_str=LOG_DEBUG",
    nullptr,
};

const RawAddress kRawAddress = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
const uint16_t kHciHandle = 123;

}  // namespace

struct power_mode_callback {
  const RawAddress bd_addr;
  tBTM_PM_STATUS status;
  uint16_t value;
  tHCI_STATUS hci_status;
};

#include <deque>
std::deque<power_mode_callback> power_mode_callback_queue;

class StackBtmPowerMode : public testing::Test {
 protected:
  void SetUp() override {
    power_mode_callback_queue.clear();
    reset_mock_function_count_map();
    bluetooth::common::InitFlags::Load(test_flags);
    ASSERT_EQ(BTM_SUCCESS,
              BTM_PmRegister(BTM_PM_REG_SET, &pm_id_,
                             [](const RawAddress& p_bda, tBTM_PM_STATUS status,
                                uint16_t value, tHCI_STATUS hci_status) {
                               power_mode_callback_queue.push_back(
                                   power_mode_callback{
                                       .bd_addr = p_bda,
                                       .status = status,
                                       .value = value,
                                       .hci_status = hci_status,
                                   });
                             }));
  }

  void TearDown() override {
    ASSERT_EQ(BTM_SUCCESS,
              BTM_PmRegister(BTM_PM_DEREG, &pm_id_,
                             [](const RawAddress& p_bda, tBTM_PM_STATUS status,
                                uint16_t value, tHCI_STATUS hci_status) {}));
  }

  uint8_t pm_id_{0};
};

class StackBtmPowerModeConnected : public StackBtmPowerMode {
 protected:
  void SetUp() override {
    StackBtmPowerMode::SetUp();
    BTM_PM_OnConnected(kHciHandle, kRawAddress);
  }

  void TearDown() override {
    BTM_PM_OnDisconnected(kHciHandle);
    StackBtmPowerMode::TearDown();
  }
};

TEST_F(StackBtmPowerMode, BTM_SetPowerMode__Undefined) {
  tBTM_PM_PWR_MD mode = {};
  ASSERT_EQ(BTM_UNKNOWN_ADDR, BTM_SetPowerMode(pm_id_, kRawAddress, &mode));
}

TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__AlreadyActive) {
  tBTM_PM_PWR_MD mode = {};
  ASSERT_EQ(BTM_SUCCESS, BTM_SetPowerMode(pm_id_, kRawAddress, &mode));
}

TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__ActiveToSniff) {
  tBTM_PM_PWR_MD mode = {
      .mode = BTM_PM_MD_SNIFF,
  };
  ASSERT_EQ("BTM_CMD_STARTED",
            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));

  // Respond with successful command status for mode command
  btm_pm_proc_cmd_status(HCI_SUCCESS);

  // Check power mode state directly
  {
    tBTM_PM_MODE current_power_mode;
    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
    ASSERT_EQ(BTM_PM_STS_PENDING, current_power_mode);
  }

  // Check power mode state from callback
  ASSERT_EQ(1U, power_mode_callback_queue.size());
  {
    const auto cb = power_mode_callback_queue.front();
    power_mode_callback_queue.pop_front();

    ASSERT_EQ(kRawAddress, cb.bd_addr);
    ASSERT_EQ(BTM_PM_STS_PENDING, cb.status);
    ASSERT_EQ(0, cb.value);
    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
  }

  // Respond with a successful mode change event
  btm_pm_proc_mode_change(HCI_SUCCESS, kHciHandle, HCI_MODE_SNIFF, 0);

  {
    tBTM_PM_MODE current_power_mode;
    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
    ASSERT_EQ(BTM_PM_STS_SNIFF, current_power_mode);
  }

  // Check power mode state from callback
  ASSERT_EQ(1U, power_mode_callback_queue.size());
  {
    const auto cb = power_mode_callback_queue.front();
    power_mode_callback_queue.pop_front();

    ASSERT_EQ(kRawAddress, cb.bd_addr);
    ASSERT_EQ(BTM_PM_STS_SNIFF, cb.status);
    ASSERT_EQ(0, cb.value);
    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
  }
}

TEST_F(StackBtmPowerModeConnected, BTM_SetPowerMode__ActiveToSniffTwice) {
  tBTM_PM_PWR_MD mode = {
      .mode = BTM_PM_MD_SNIFF,
  };
  ASSERT_EQ("BTM_CMD_STARTED",
            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));

  // Respond with successful command status for mode command
  btm_pm_proc_cmd_status(HCI_SUCCESS);

  // Check power mode state directly
  {
    tBTM_PM_MODE current_power_mode;
    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
    ASSERT_EQ(BTM_PM_STS_PENDING, current_power_mode);
  }

  // Check power mode state from callback
  ASSERT_EQ(1U, power_mode_callback_queue.size());
  {
    const auto cb = power_mode_callback_queue.front();
    power_mode_callback_queue.pop_front();

    ASSERT_EQ(kRawAddress, cb.bd_addr);
    ASSERT_EQ(BTM_PM_STS_PENDING, cb.status);
    ASSERT_EQ(0, cb.value);
    ASSERT_EQ(HCI_SUCCESS, cb.hci_status);
  }

  // Send a second active to sniff command
  ASSERT_EQ("BTM_CMD_STORED",
            btm_status_text(BTM_SetPowerMode(pm_id_, kRawAddress, &mode)));
  // No command should be issued
  ASSERT_EQ(1, get_func_call_count("btsnd_hcic_sniff_mode"));

  // Check power mode state directly
  {
    tBTM_PM_MODE current_power_mode;
    ASSERT_TRUE(BTM_ReadPowerMode(kRawAddress, &current_power_mode));
    // NOTE: The mixed enum values
    ASSERT_EQ(
        static_cast<tBTM_PM_MODE>(BTM_PM_STS_PENDING | BTM_PM_STORED_MASK),
        current_power_mode);
  }
}