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

Commit 8d90b191 authored by Yuyang Huang's avatar Yuyang Huang Committed by Gerrit Code Review
Browse files

Merge "add GMAP client" into main

parents c810cdfa b394a36f
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ cc_library_static {
        "av/bta_av_main.cc",
        "av/bta_av_ssm.cc",
        "csis/csis_client.cc",
        "gmap/gmap_client.cc",
        "groups/groups.cc",
        "has/has_client.cc",
        "has/has_ctp.cc",
@@ -866,6 +867,7 @@ cc_test {
        ":TestMockStackBtmIso",
        ":TestMockStackL2cap",
        ":TestStubOsi",
        "gmap/gmap_client.cc",
        "le_audio/audio_hal_client/audio_hal_client_test.cc",
        "le_audio/audio_hal_client/audio_sink_hal_client.cc",
        "le_audio/audio_hal_client/audio_source_hal_client.cc",
@@ -965,6 +967,8 @@ cc_test {
        ":TestStubOsi",
        "gatt/database.cc",
        "gatt/database_builder.cc",
        "gmap/gmap_client.cc",
        "gmap/gmap_client_test.cc",
        "le_audio/broadcaster/broadcast_configuration_provider.cc",
        "le_audio/broadcaster/broadcaster_types.cc",
        "le_audio/client.cc",
+1 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ static_library("bta") {
    "gatt/database.cc",
    "gatt/database_builder.cc",
    "groups/groups.cc",
    "gmap/gmap_client.cc",
    "has/has_client.cc",
    "has/has_ctp.cc",
    "has/has_preset.cc",
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 "bta/le_audio/gmap_client.h"

#include <base/functional/bind.h>
#include <base/functional/callback.h>
#include <base/strings/string_number_conversions.h>
#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
#include <hardware/bt_gatt_types.h>

#include <bitset>
#include <string>
#include <vector>

#include "bta_gatt_api.h"
#include "bta_gatt_queue.h"
#include "bta_le_audio_uuids.h"
#include "gap_api.h"
#include "gatt_api.h"
#include "internal_include/bt_trace.h"
#include "os/log.h"
#include "osi/include/properties.h"
#include "stack/include/bt_types.h"

using bluetooth::Uuid;
using namespace bluetooth;
using bluetooth::le_audio::GmapClient;
bool GmapClient::is_offloader_support_gmap_ = false;

void GmapClient::AddFromStorage(const RawAddress &addr, const uint8_t role,
                                const uint16_t role_handle, const uint8_t UGT_feature,
                                const uint16_t UGT_feature_handle) {
  addr_ = addr;
  role_ = role;
  role_handle_ = role_handle;
  UGT_feature_ = UGT_feature;
  UGT_feature_handle_ = UGT_feature_handle;
}

void GmapClient::DebugDump(int fd) {
  std::stringstream stream;
  if (!IsGmapClientEnabled()) {
    dprintf(fd, "%s", "GmapClient not enabled");
    return;
  }
  stream << "GmapClient device: " << addr_ << ", Role: " << role_ << ", ";
  stream << "UGT Feature: " << UGT_feature_ << "\n";
  dprintf(fd, "%s", stream.str().c_str());
}

bool GmapClient::IsGmapClientEnabled() {
  bool flag = com::android::bluetooth::flags::leaudio_gmap_client();
  bool system_prop = osi_property_get_bool("bluetooth.profile.gmap.enabled", false);

  bool result = flag && system_prop && is_offloader_support_gmap_;
  log::info("GmapClientEnabled={}, flag={}, system_prop={}, offloader_support={}", result,
            system_prop, flag, GmapClient::is_offloader_support_gmap_);
  return result;
}

void GmapClient::UpdateGmapOffloaderSupport(bool value) {
  GmapClient::is_offloader_support_gmap_ = value;
}

bool GmapClient::parseAndSaveGmapRole(uint16_t len, const uint8_t *value) {
  if (len != GmapClient::kGmapRoleLen) {
    log::error("device: {}, Wrong len of GMAP Role characteristic", addr_);
    return false;
  }

  STREAM_TO_UINT8(role_, value);
  log::info("GMAP device: {}, Role: {}", addr_, role_.to_string());
  return true;
}

bool GmapClient::parseAndSaveUGTFeature(uint16_t len, const uint8_t *value) {
  if (len != kGmapUGTFeatureLen) {
    log::error("device: {}, Wrong len of GMAP UGT Feature characteristic", addr_);
    return false;
  }
  STREAM_TO_UINT8(UGT_feature_, value);
  log::info("GMAP device: {}, Feature: {}", addr_, UGT_feature_.to_string());
  return true;
}

std::bitset<8> GmapClient::getRole() { return role_; }

uint16_t GmapClient::getRoleHandle() { return role_handle_; }

void GmapClient::setRoleHandle(uint16_t handle) { role_handle_ = handle; }

std::bitset<8> GmapClient::getUGTFeature() { return UGT_feature_; }

uint16_t GmapClient::getUGTFeatureHandle() { return UGT_feature_handle_; }

void GmapClient::setUGTFeatureHandle(uint16_t handle) { UGT_feature_handle_ = handle; }
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 "bta/le_audio/gmap_client.h"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <hardware/bluetooth.h>

#include "bta/le_audio/le_audio_types.h"
#include "fake_osi.h"
#include "test/mock/mock_osi_properties.h"

using bluetooth::le_audio::GmapClient;
using ::testing::_;

static constexpr char kGmapEnabledSysProp[] = "bluetooth.profile.gmap.enabled";

void osi_property_set_bool(const char* key, bool value);

class GmapClientTest : public ::testing::Test {
public:
  RawAddress addr = RawAddress({0x11, 0x22, 0x33, 0x44, 0x55, 0x66});
  GmapClient gmapClient = GmapClient(addr);
};

TEST_F(GmapClientTest, test_parse_role) {
  const uint8_t role = 0b0001;
  gmapClient.parseAndSaveGmapRole(1, &role);

  ASSERT_EQ(gmapClient.getRole(), role);
}

TEST_F(GmapClientTest, test_parse_invalid_role) {
  const uint8_t role = 0b0001;
  ASSERT_FALSE(gmapClient.parseAndSaveGmapRole(2, &role));
}

TEST_F(GmapClientTest, test_parse_ugt_feature) {
  const uint8_t value = 0b0001;
  gmapClient.parseAndSaveUGTFeature(1, &value);

  ASSERT_EQ(gmapClient.getUGTFeature(), value);
}

TEST_F(GmapClientTest, test_parse_invalid_ugt_feature) {
  const uint8_t value = 0b0001;
  ASSERT_FALSE(gmapClient.parseAndSaveUGTFeature(2, &value));
}

TEST_F(GmapClientTest, test_add_from_storage) {
  const uint8_t role = 0b0001;
  const uint16_t role_handle = 2;
  const uint8_t UGT_feature = 0b0011;
  const uint16_t UGT_feature_handle = 4;
  gmapClient.AddFromStorage(addr, role, role_handle, UGT_feature, UGT_feature_handle);
  ASSERT_EQ(gmapClient.getRole(), role);
  ASSERT_EQ(gmapClient.getRoleHandle(), role_handle);
  ASSERT_EQ(gmapClient.getUGTFeature(), UGT_feature);
  ASSERT_EQ(gmapClient.getUGTFeatureHandle(), UGT_feature_handle);
}

TEST_F(GmapClientTest, test_role_handle) {
  const uint16_t handle = 5;
  gmapClient.setRoleHandle(handle);
  ASSERT_EQ(gmapClient.getRoleHandle(), handle);
}

TEST_F(GmapClientTest, test_ugt_feature_handle) {
  const uint16_t handle = 6;
  gmapClient.setUGTFeatureHandle(handle);
  ASSERT_EQ(gmapClient.getUGTFeatureHandle(), handle);
}

TEST_F(GmapClientTest, test_is_gmap_client_enabled) {
  GmapClient::UpdateGmapOffloaderSupport(false);
  ASSERT_EQ(GmapClient::IsGmapClientEnabled(), false);

  com::android::bluetooth::flags::provider_->leaudio_gmap_client(true);
  osi_property_set_bool(kGmapEnabledSysProp, true);

  GmapClient::UpdateGmapOffloaderSupport(true);

  ASSERT_EQ(GmapClient::IsGmapClientEnabled(), true);
  osi_property_set_bool(kGmapEnabledSysProp, false);
}
+35 −1
Original line number Diff line number Diff line
@@ -45,7 +45,9 @@
#include "content_control_id_keeper.h"
#include "devices.h"
#include "gatt_api.h"
#include "gmap_client.h"
#include "hci/controller_interface.h"
#include "include/hardware/bt_gmap.h"
#include "internal_include/stack_config.h"
#include "le_audio/device_groups.h"
#include "le_audio_health_status.h"
@@ -72,6 +74,7 @@
using base::Closure;
using bluetooth::Uuid;
using bluetooth::common::ToString;
using bluetooth::gmap::RolesBitMask;
using bluetooth::groups::DeviceGroups;
using bluetooth::groups::DeviceGroupsCallbacks;
using bluetooth::hci::IsoManager;
@@ -84,6 +87,7 @@ using bluetooth::le_audio::ContentControlIdKeeper;
using bluetooth::le_audio::DeviceConnectState;
using bluetooth::le_audio::DsaMode;
using bluetooth::le_audio::DsaModes;
using bluetooth::le_audio::GmapClient;
using bluetooth::le_audio::GroupNodeStatus;
using bluetooth::le_audio::GroupStatus;
using bluetooth::le_audio::GroupStreamStatus;
@@ -117,7 +121,6 @@ using bluetooth::le_audio::types::LeAudioContextType;
using bluetooth::le_audio::types::PublishedAudioCapabilities;
using bluetooth::le_audio::utils::GetAudioContextsFromSinkMetadata;
using bluetooth::le_audio::utils::GetAudioContextsFromSourceMetadata;

using namespace bluetooth;

/* Enums */
@@ -2052,6 +2055,12 @@ public:
    } else if (hdl == leAudioDevice->tmap_role_hdl_) {
      bluetooth::le_audio::client_parser::tmap::ParseTmapRole(leAudioDevice->tmap_role_, len,
                                                              value);
    } else if (leAudioDevice->gmap_client_ != nullptr && GmapClient::IsGmapClientEnabled() &&
               hdl == leAudioDevice->gmap_client_->getRoleHandle()) {
      leAudioDevice->gmap_client_->parseAndSaveGmapRole(len, value);
    } else if (leAudioDevice->gmap_client_ != nullptr && GmapClient::IsGmapClientEnabled() &&
               hdl == leAudioDevice->gmap_client_->getUGTFeatureHandle()) {
      leAudioDevice->gmap_client_->parseAndSaveUGTFeature(len, value);
    } else {
      log::error("Unknown attribute read: 0x{:x}", hdl);
    }
@@ -2767,6 +2776,7 @@ public:
    const gatt::Service* pac_svc = nullptr;
    const gatt::Service* ase_svc = nullptr;
    const gatt::Service* tmas_svc = nullptr;
    const gatt::Service* gmap_svc = nullptr;

    std::vector<uint16_t> csis_primary_handles;
    uint16_t cas_csis_included_handle = 0;
@@ -2805,6 +2815,10 @@ public:
        log::info("Found Telephony and Media Audio service, handle: 0x{:04x}, device: {}",
                  tmp.handle, leAudioDevice->address_);
        tmas_svc = &tmp;
      } else if (tmp.uuid == bluetooth::le_audio::uuid::kGamingAudioServiceUuid) {
        log::info("Found Gaming Audio service, handle: 0x{:04x}, device: {}", tmp.handle,
                  leAudioDevice->address_);
        gmap_svc = &tmp;
      }
    }

@@ -3057,6 +3071,26 @@ public:
      }
    }

    if (gmap_svc && GmapClient::IsGmapClientEnabled()) {
      leAudioDevice->gmap_client_ = std::make_unique<GmapClient>(leAudioDevice->address_);
      for (const gatt::Characteristic& charac : gmap_svc->characteristics) {
        if (charac.uuid == bluetooth::le_audio::uuid::kRoleCharacteristicUuid) {
          uint16_t handle = charac.value_handle;
          leAudioDevice->gmap_client_->setRoleHandle(handle);
          BtaGattQueue::ReadCharacteristic(conn_id, handle, OnGattReadRspStatic, NULL);
          log::info("Found Gmap Role characteristic, handle: 0x{:04x}, device: {}",
                    leAudioDevice->gmap_client_->getRoleHandle(), leAudioDevice->address_);
        }
        if (charac.uuid == bluetooth::le_audio::uuid::kUnicastGameTerminalCharacteristicUuid) {
          uint16_t handle = charac.value_handle;
          leAudioDevice->gmap_client_->setUGTFeatureHandle(handle);
          BtaGattQueue::ReadCharacteristic(conn_id, handle, OnGattReadRspStatic, NULL);
          log::info("Found Gmap UGT Feature characteristic, handle: 0x{:04x}, device: {}",
                    leAudioDevice->gmap_client_->getUGTFeatureHandle(), leAudioDevice->address_);
        }
      }
    }

    leAudioDevice->known_service_handles_ = true;
    leAudioDevice->notify_connected_after_read_ = true;
    if (leAudioHealthStatus_) {
Loading