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

Commit 1a25b385 authored by En-Shuo Hsu's avatar En-Shuo Hsu Committed by Gerrit Code Review
Browse files

Merge "Reland floss: Add unittests for btm_sco_hci"

parents 94c81ef0 482f200d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -840,6 +840,7 @@ cc_test {
        ":TestMockStackHcic",
        ":TestMockStackL2cap",
        ":TestMockStackSmp",
        ":TestMockUdrv",
        "acl/acl.cc",
        "acl/ble_acl.cc",
        "acl/btm_acl.cc",
@@ -871,6 +872,7 @@ cc_test {
        "btm/hfp_msbc_encoder.cc",
        "btm/hfp_msbc_decoder.cc",
        "metrics/stack_metrics_logging.cc",
        "test/btm/sco_hci_test.cc",
        "test/btm/stack_btm_test.cc",
        "test/btm/peer_packet_types_test.cc",
        "test/common/mock_eatt.cc",
+6 −3
Original line number Diff line number Diff line
@@ -54,9 +54,12 @@ namespace bluetooth::audio::sco::wbs {
 *    pkt_size - Length of the SCO packet. It is determined based on the BT-USB
 *    adapter's capability and alt mode setting. The value should be queried
 *    from HAL interface. It will be used to determine the size of the SCO
 *    packet buffer.
 *    packet buffer. Currently, the stack only supports 60 and 72.
 * Returns:
 *    The selected packet size. Will fallback to the typical mSBC packet
 *    length(60) if the pkt_size argument is not supported.
 */
void init(size_t pkt_size);
size_t init(size_t pkt_size);

/* Clean up when the SCO connection is done */
void cleanup();
@@ -65,7 +68,7 @@ void cleanup();
 * Args:
 *    data - Pointer to received packet data bytes.
 *    pkt_size - Length of input packet. Passing packet with inconsistent size
 *        from the pkt_size set in init() will trigger a reset of the buffer.
 *        from the pkt_size set in init() will fail the call.
 * Returns:
 *    The length of enqueued bytes. 0 if failed.
 */
+31 −4
Original line number Diff line number Diff line
@@ -71,7 +71,13 @@ void open() {
  if (sco_uipc != nullptr) {
    LOG_WARN("Re-opening UIPC that is already running");
  }

  sco_uipc = UIPC_Init();
  if (sco_uipc == nullptr) {
    LOG_ERROR("%s failed to init UIPC", __func__);
    return;
  }

  UIPC_Open(*sco_uipc, UIPC_CH_ID_AV_AUDIO, sco_data_cb, SCO_HOST_DATA_PATH);
  struct group* grp = getgrnam(SCO_HOST_DATA_GROUP);
  chmod(SCO_HOST_DATA_PATH, 0770);
@@ -180,7 +186,7 @@ struct tBTM_MSBC_INFO {
  }

 public:
  void init(size_t pkt_size) {
  size_t init(size_t pkt_size) {
    decode_buf_wo = 0;
    decode_buf_ro = 0;
    encode_buf_wo = 0;
@@ -188,7 +194,7 @@ struct tBTM_MSBC_INFO {

    pkt_size = get_supported_packet_size(pkt_size, &buf_size);
    if (pkt_size != BTM_MSBC_PKT_LEN) check_alignment = true;
    if (pkt_size == packet_size) return;
    if (pkt_size == packet_size) return packet_size;
    packet_size = pkt_size;

    if (msbc_decode_buf) osi_free(msbc_decode_buf);
@@ -196,6 +202,7 @@ struct tBTM_MSBC_INFO {

    if (msbc_encode_buf) osi_free(msbc_encode_buf);
    msbc_encode_buf = (uint8_t*)osi_calloc(buf_size);
    return packet_size;
  }

  void deinit() {
@@ -292,7 +299,7 @@ struct tBTM_MSBC_INFO {

static tBTM_MSBC_INFO* msbc_info = nullptr;

void init(size_t pkt_size) {
size_t init(size_t pkt_size) {
  hfp_msbc_decoder_init();
  hfp_msbc_encoder_init();

@@ -303,7 +310,7 @@ void init(size_t pkt_size) {
  }

  msbc_info = (tBTM_MSBC_INFO*)osi_calloc(sizeof(*msbc_info));
  msbc_info->init(pkt_size);
  return msbc_info->init(pkt_size);
}

void cleanup() {
@@ -331,6 +338,11 @@ size_t enqueue_packet(const uint8_t* data, size_t pkt_size) {
    return 0;
  }

  if (data == nullptr) {
    LOG_WARN("Invalid data to enqueue");
    return 0;
  }

  if (msbc_info->check_alignment) {
    if (data[0] != BTM_MSBC_H2_HEADER_0 || data[2] != BTM_MSBC_SYNC_WORD) {
      LOG_DEBUG("Waiting for valid mSBC frame head");
@@ -356,6 +368,11 @@ size_t decode(const uint8_t** out_data) {
    return 0;
  }

  if (out_data == nullptr) {
    LOG_WARN("%s Invalid output pointer", __func__);
    return 0;
  }

  if (msbc_info->decodable() < BTM_MSBC_PKT_LEN) {
    LOG_DEBUG("No complete mSBC packet to decode");
    return 0;
@@ -395,6 +412,11 @@ size_t encode(int16_t* data, size_t len) {
    return 0;
  }

  if (data == nullptr) {
    LOG_WARN("Invalid data to encode");
    return 0;
  }

  if (len < BTM_MSBC_CODE_SIZE) {
    LOG_DEBUG(
        "PCM frames with size %lu is insufficient to be encoded into a mSBC "
@@ -425,6 +447,11 @@ size_t dequeue_packet(const uint8_t** output) {
    return 0;
  }

  if (output == nullptr) {
    LOG_WARN("%s Invalid output pointer", __func__);
    return 0;
  }

  *output = msbc_info->sco_pkt_read_ptr();
  if (*output == nullptr) {
    LOG_DEBUG("Insufficient data to dequeue.");
+279 −0
Original line number Diff line number Diff line
/*
 *
 *  Copyright 2022 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 <algorithm>
#include <map>
#include <memory>

#include "stack/btm/btm_sco.h"
#include "udrv/include/uipc.h"

extern std::map<std::string, int> mock_function_count_map;
extern std::unique_ptr<tUIPC_STATE> mock_uipc_init_ret;
extern uint32_t mock_uipc_read_ret;
extern bool mock_uipc_send_ret;

namespace {

using testing::Test;

const uint8_t msbc_zero_packet[] = {
    0x01, 0x08, 0xad, 0x00, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x77, 0x6d,
    0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb, 0x77,
    0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6, 0xdb,
    0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6d, 0xdd, 0xb6,
    0xdb, 0x77, 0x6d, 0xb6, 0xdd, 0xdb, 0x6d, 0xb7, 0x76, 0xdb, 0x6c, 0x00};

class ScoHciTest : public Test {
 public:
 protected:
  void SetUp() override {
    mock_function_count_map.clear();
    mock_uipc_init_ret = nullptr;
    mock_uipc_read_ret = 0;
    mock_uipc_send_ret = true;
  }
  void TearDown() override {}
};

class ScoHciWithOpenCleanTest : public ScoHciTest {
 public:
 protected:
  void SetUp() override {
    ScoHciTest::SetUp();
    mock_uipc_init_ret = std::make_unique<tUIPC_STATE>();
    bluetooth::audio::sco::open();
  }
  void TearDown() override { bluetooth::audio::sco::cleanup(); }
};

class ScoHciWbsWithInitCleanTest : public Test {
 public:
 protected:
  void SetUp() override { bluetooth::audio::sco::wbs::init(60); }
  void TearDown() override { bluetooth::audio::sco::wbs::cleanup(); }
};

TEST_F(ScoHciTest, ScoOverHciOpenFail) {
  bluetooth::audio::sco::open();
  ASSERT_EQ(mock_function_count_map["UIPC_Init"], 1);
  ASSERT_EQ(mock_function_count_map["UIPC_Open"], 0);
  bluetooth::audio::sco::cleanup();

  // UIPC is nullptr and shouldn't require an actual call of UIPC_Close;
  ASSERT_EQ(mock_function_count_map["UIPC_Close"], 0);
}

TEST_F(ScoHciWithOpenCleanTest, ScoOverHciOpenClean) {
  ASSERT_EQ(mock_function_count_map["UIPC_Init"], 1);
  ASSERT_EQ(mock_function_count_map["UIPC_Open"], 1);
  ASSERT_EQ(mock_uipc_init_ret, nullptr);

  mock_uipc_init_ret = std::make_unique<tUIPC_STATE>();
  // Double open will override uipc
  bluetooth::audio::sco::open();
  ASSERT_EQ(mock_function_count_map["UIPC_Init"], 2);
  ASSERT_EQ(mock_function_count_map["UIPC_Open"], 2);
  ASSERT_EQ(mock_uipc_init_ret, nullptr);

  bluetooth::audio::sco::cleanup();
  ASSERT_EQ(mock_function_count_map["UIPC_Close"], 1);

  // Double clean shouldn't fail
  bluetooth::audio::sco::cleanup();
  ASSERT_EQ(mock_function_count_map["UIPC_Close"], 1);
}

TEST_F(ScoHciTest, ScoOverHciReadNoOpen) {
  uint8_t buf[100];
  ASSERT_EQ(bluetooth::audio::sco::read(buf, sizeof(buf)), size_t(0));
  ASSERT_EQ(mock_function_count_map["UIPC_Read"], 0);
}

TEST_F(ScoHciWithOpenCleanTest, ScoOverHciRead) {
  uint8_t buf[100];
  // The UPIC should be ready
  ASSERT_EQ(mock_function_count_map["UIPC_Init"], 1);
  ASSERT_EQ(mock_function_count_map["UIPC_Open"], 1);
  ASSERT_EQ(mock_uipc_init_ret, nullptr);

  mock_uipc_read_ret = sizeof(buf);
  ASSERT_EQ(bluetooth::audio::sco::read(buf, sizeof(buf)), mock_uipc_read_ret);
  ASSERT_EQ(mock_function_count_map["UIPC_Read"], 1);
}

TEST_F(ScoHciTest, ScoOverHciWriteNoOpen) {
  uint8_t buf[100];
  bluetooth::audio::sco::write(buf, sizeof(buf));
  ASSERT_EQ(mock_function_count_map["UIPC_Send"], 0);
}

TEST_F(ScoHciWithOpenCleanTest, ScoOverHciWrite) {
  uint8_t buf[100];
  // The UPIC should be ready
  ASSERT_EQ(mock_function_count_map["UIPC_Init"], 1);
  ASSERT_EQ(mock_function_count_map["UIPC_Open"], 1);
  ASSERT_EQ(mock_uipc_init_ret, nullptr);

  ASSERT_EQ(bluetooth::audio::sco::write(buf, sizeof(buf)), sizeof(buf));
  ASSERT_EQ(mock_function_count_map["UIPC_Send"], 1);

  // Send fails
  mock_uipc_send_ret = false;
  ASSERT_EQ(bluetooth::audio::sco::write(buf, sizeof(buf)), size_t(0));
  ASSERT_EQ(mock_function_count_map["UIPC_Send"], 2);
}

TEST(ScoHciWbsTest, WbsInit) {
  ASSERT_EQ(bluetooth::audio::sco::wbs::init(60), size_t(60));
  ASSERT_EQ(bluetooth::audio::sco::wbs::init(72), size_t(72));
  // Fallback to 60 if the packet size is not supported
  ASSERT_EQ(bluetooth::audio::sco::wbs::init(48), size_t(60));
  bluetooth::audio::sco::wbs::cleanup();
}

TEST(ScoHciWbsTest, WbsEnqueuePacketWithoutInit) {
  uint8_t payload[60];
  // Return 0 if buffer is uninitialized
  ASSERT_EQ(
      bluetooth::audio::sco::wbs::enqueue_packet(payload, sizeof(payload)),
      size_t(0));
}

TEST_F(ScoHciWbsWithInitCleanTest, WbsEnqueuePacket) {
  uint8_t payload[60];
  // Return 0 if payload is invalid
  ASSERT_EQ(
      bluetooth::audio::sco::wbs::enqueue_packet(nullptr, sizeof(payload)),
      size_t(0));
  // Return 0 if packet size is consistent
  ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(payload, 72), size_t(0));
  ASSERT_EQ(
      bluetooth::audio::sco::wbs::enqueue_packet(payload, sizeof(payload)),
      size_t(60));
  // Return 0 if buffer is full
  ASSERT_EQ(
      bluetooth::audio::sco::wbs::enqueue_packet(payload, sizeof(payload)),
      size_t(0));
}

TEST(ScoHciWbsTest, WbsDecodeWithoutInit) {
  const uint8_t* decoded = nullptr;
  // Return 0 if buffer is uninitialized
  ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
  ASSERT_EQ(decoded, nullptr);
}

TEST_F(ScoHciWbsWithInitCleanTest, WbsDecode) {
  const uint8_t* decoded = nullptr;
  uint8_t payload[60] = {0};

  // No data to decode
  ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
  ASSERT_EQ(decoded, nullptr);
  // Fill in invalid packet, all zeros.
  ASSERT_EQ(
      bluetooth::audio::sco::wbs::enqueue_packet(payload, sizeof(payload)),
      sizeof(payload));

  // Return all zero frames when there comes an invalid packet.
  ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded),
            size_t(BTM_MSBC_CODE_SIZE));
  ASSERT_NE(decoded, nullptr);
  for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
    ASSERT_EQ(decoded[i], 0);
  }

  decoded = nullptr;
  ASSERT_EQ(bluetooth::audio::sco::wbs::enqueue_packet(msbc_zero_packet,
                                                       sizeof(payload)),
            sizeof(msbc_zero_packet));
  ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded),
            size_t(BTM_MSBC_CODE_SIZE));
  ASSERT_NE(decoded, nullptr);
  for (size_t i = 0; i < BTM_MSBC_CODE_SIZE; i++) {
    ASSERT_EQ(decoded[i], 0);
  }

  decoded = nullptr;
  // No remaining data to decode
  ASSERT_EQ(bluetooth::audio::sco::wbs::decode(&decoded), size_t(0));
  ASSERT_EQ(decoded, nullptr);
}

TEST(ScoHciWbsTest, WbsEncodeWithoutInit) {
  int16_t data[120] = {0};
  // Return 0 if buffer is uninitialized
  ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), size_t(0));
}

TEST_F(ScoHciWbsWithInitCleanTest, WbsEncode) {
  int16_t data[120] = {0};

  // Return 0 if data is invalid
  ASSERT_EQ(bluetooth::audio::sco::wbs::encode(nullptr, sizeof(data)),
            size_t(0));
  // Return 0 if data length is insufficient
  ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data) - 1),
            size_t(0));
  ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)),
            sizeof(data));

  // Return 0 if the packet buffer is full
  ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)), size_t(0));
}

TEST(ScoHciWbsTest, WbsDequeuePacketWithoutInit) {
  const uint8_t* encoded = nullptr;
  // Return 0 if buffer is uninitialized
  ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(0));
  ASSERT_EQ(encoded, nullptr);
}

TEST_F(ScoHciWbsWithInitCleanTest, WbsDequeuePacket) {
  const uint8_t* encoded = nullptr;
  // Return 0 if output pointer is invalid
  ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(nullptr), size_t(0));
  ASSERT_EQ(encoded, nullptr);

  // Return 0 if there is insufficient data to dequeue
  ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(0));
  ASSERT_EQ(encoded, nullptr);
}

TEST_F(ScoHciWbsWithInitCleanTest, WbsEncodeDequeuePackets) {
  uint8_t h2_header_frames_count[] = {0x08, 0x38, 0xc8, 0xf8};
  int16_t data[120] = {0};
  const uint8_t* encoded = nullptr;

  for (size_t i = 0; i < 5; i++) {
    ASSERT_EQ(bluetooth::audio::sco::wbs::encode(data, sizeof(data)),
              sizeof(data));
    ASSERT_EQ(bluetooth::audio::sco::wbs::dequeue_packet(&encoded), size_t(60));
    ASSERT_NE(encoded, nullptr);
    for (size_t j = 0; j < 60; j++) {
      ASSERT_EQ(encoded[j],
                j == 1 ? h2_header_frames_count[i % 4] : msbc_zero_packet[j]);
    }
  }
}

}  // namespace
+7 −3
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@ extern std::map<std::string, int> mock_function_count_map;
#define UNUSED_ATTR
#endif

std::unique_ptr<tUIPC_STATE> mock_uipc_init_ret;
uint32_t mock_uipc_read_ret;
bool mock_uipc_send_ret;

bool UIPC_Open(tUIPC_STATE& uipc, tUIPC_CH_ID ch_id, tUIPC_RCV_CBACK* p_cback,
               const char* socket_path) {
  mock_function_count_map[__func__]++;
@@ -40,7 +44,7 @@ bool UIPC_Send(tUIPC_STATE& uipc, tUIPC_CH_ID ch_id,
               UNUSED_ATTR uint16_t msg_evt, const uint8_t* p_buf,
               uint16_t msglen) {
  mock_function_count_map[__func__]++;
  return false;
  return mock_uipc_send_ret;
}
int uipc_start_main_server_thread(tUIPC_STATE& uipc) {
  mock_function_count_map[__func__]++;
@@ -48,7 +52,7 @@ int uipc_start_main_server_thread(tUIPC_STATE& uipc) {
}
std::unique_ptr<tUIPC_STATE> UIPC_Init() {
  mock_function_count_map[__func__]++;
  return nullptr;
  return std::move(mock_uipc_init_ret);
}
const char* dump_uipc_event(tUIPC_EVENT event) {
  mock_function_count_map[__func__]++;
@@ -57,7 +61,7 @@ const char* dump_uipc_event(tUIPC_EVENT event) {
uint32_t UIPC_Read(tUIPC_STATE& uipc, tUIPC_CH_ID ch_id, uint8_t* p_buf,
                   uint32_t len) {
  mock_function_count_map[__func__]++;
  return 0;
  return mock_uipc_read_ret;
}
bool UIPC_Ioctl(tUIPC_STATE& uipc, tUIPC_CH_ID ch_id, uint32_t request,
                void* param) {