chromium/chromeos/ash/components/dbus/audio/floss_media_client.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/ash/components/dbus/audio/floss_media_client.h"

#include <stdint.h>

#include "base/memory/raw_ptr.h"
#include "chromeos/ash/components/dbus/audio/fake_floss_media_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"

#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

const char kFlossServiceName[] = "org.chromium.bluetooth";
const char kFlossMediaInterface[] = "org.chromium.bluetooth.BluetoothMedia";

FlossMediaClient* g_instance = nullptr;

// The FlossMediaClient implementation used in production.
class FlossMediaClientImpl : public FlossMediaClient {
 public:
  explicit FlossMediaClientImpl(dbus::Bus* bus,
                                const dbus::ObjectPath& object_path) {
    floss_media_proxy_ = bus->GetObjectProxy(kFlossServiceName, object_path);
  }

  FlossMediaClientImpl(const FlossMediaClientImpl&) = delete;
  FlossMediaClientImpl& operator=(const FlossMediaClientImpl&) = delete;

  ~FlossMediaClientImpl() override = default;

  void SetPlayerPlaybackStatus(const std::string& playback_status) override {
    dbus::MethodCall method_call(kFlossMediaInterface,
                                 cras::kSetPlayerPlaybackStatus);
    dbus::MessageWriter writer(&method_call);
    writer.AppendString(playback_status);
    floss_media_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void SetPlayerIdentity(const std::string& identity) override {
    dbus::MethodCall method_call(kFlossMediaInterface,
                                 cras::kSetPlayerIdentity);
    dbus::MessageWriter writer(&method_call);
    writer.AppendString(identity);
    floss_media_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void SetPlayerPosition(const int64_t& position) override {
    dbus::MethodCall method_call(kFlossMediaInterface,
                                 cras::kSetPlayerPosition);
    dbus::MessageWriter writer(&method_call);
    writer.AppendInt64(position);
    floss_media_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void SetPlayerDuration(const int64_t& duration) override {
    dbus::MethodCall method_call(kFlossMediaInterface,
                                 cras::kSetPlayerMetadata);
    dbus::MessageWriter writer(&method_call);
    dbus::MessageWriter array_writer(nullptr);
    dbus::MessageWriter dict_entry_writer(nullptr);

    writer.OpenArray("{sv}", &array_writer);
    array_writer.OpenDictEntry(&dict_entry_writer);
    dict_entry_writer.AppendString("length");
    dict_entry_writer.AppendVariantOfInt64(duration);
    array_writer.CloseContainer(&dict_entry_writer);
    writer.CloseContainer(&array_writer);

    floss_media_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void SetPlayerMetadata(
      const std::map<std::string, std::string>& metadata) override {
    dbus::MethodCall method_call(kFlossMediaInterface,
                                 cras::kSetPlayerMetadata);
    dbus::MessageWriter writer(&method_call);
    dbus::MessageWriter array_writer(nullptr);
    dbus::MessageWriter dict_entry_writer(nullptr);

    writer.OpenArray("{sv}", &array_writer);

    for (auto& it : metadata) {
      array_writer.OpenDictEntry(&dict_entry_writer);
      dict_entry_writer.AppendString(it.first);
      dict_entry_writer.AppendVariantOfString(it.second);
      array_writer.CloseContainer(&dict_entry_writer);
    }

    writer.CloseContainer(&array_writer);

    floss_media_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

 private:
  raw_ptr<dbus::ObjectProxy> floss_media_proxy_ = nullptr;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<FlossMediaClientImpl> weak_ptr_factory_{this};
};

}  // namespace

FlossMediaClient::FlossMediaClient() {
  DCHECK(!g_instance);
  g_instance = this;
}

FlossMediaClient::~FlossMediaClient() {
  DCHECK_EQ(this, g_instance);
  g_instance = nullptr;
}

// static
void FlossMediaClient::Initialize(dbus::Bus* bus,
                                  const dbus::ObjectPath& object_path) {
  DCHECK(bus);
  new FlossMediaClientImpl(bus, object_path);
}

// static
void FlossMediaClient::InitializeFake() {
  new FakeFlossMediaClient();
}

// static
void FlossMediaClient::Shutdown() {
  delete g_instance;
}

// static
FlossMediaClient* FlossMediaClient::Get() {
  return g_instance;
}

}  // namespace ash