chromium/chromeos/ash/components/dbus/cec_service/cec_service_client.cc

// Copyright 2018 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/cec_service/cec_service_client.h"

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "chromeos/ash/components/dbus/cec_service/fake_cec_service_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace ash {

namespace {

CecServiceClient* g_instance = nullptr;

// Translates a power state from a D-Bus response to the types exposed by
// CecServiceClient.
CecServiceClient::PowerState ConvertDBusPowerState(int32_t power_state) {
  switch (power_state) {
    case cecservice::kTvPowerStatusError:
      return CecServiceClient::PowerState::kError;
    case cecservice::kTvPowerStatusAdapterNotConfigured:
      return CecServiceClient::PowerState::kAdapterNotConfigured;
    case cecservice::kTvPowerStatusNoTv:
      return CecServiceClient::PowerState::kNoDevice;
    case cecservice::kTvPowerStatusOn:
      return CecServiceClient::PowerState::kOn;
    case cecservice::kTvPowerStatusStandBy:
      return CecServiceClient::PowerState::kStandBy;
    case cecservice::kTvPowerStatusToOn:
      return CecServiceClient::PowerState::kTransitioningToOn;
    case cecservice::kTvPowerStatusToStandBy:
      return CecServiceClient::PowerState::kTransitioningToStandBy;
    case cecservice::kTvPowerStatusUnknown:
      return CecServiceClient::PowerState::kUnknown;
    default:
      NOTREACHED_IN_MIGRATION() << "Received unknown state " << power_state;
      return CecServiceClient::PowerState::kUnknown;
  }
}

void OnGetTvsPowerStatus(CecServiceClient::PowerStateCallback callback,
                         dbus::Response* response) {
  if (!response) {
    LOG(ERROR) << "QueryDisplayCecPowerState call failed, no response";
    std::move(callback).Run({});
    return;
  }

  dbus::MessageReader reader(response);

  dbus::MessageReader array_reader(nullptr);
  if (!reader.PopArray(&array_reader)) {
    LOG(ERROR) << "Unable to read back array for QueryDisplayCecPowerState";
    std::move(callback).Run({});
    return;
  }

  std::vector<CecServiceClient::PowerState> result;
  while (array_reader.HasMoreData()) {
    int32_t low_level_power_state = -1;
    if (!array_reader.PopInt32(&low_level_power_state)) {
      LOG(ERROR) << "Unable to pop state for QueryDisplayCecPowerState";
      std::move(callback).Run({});
      return;
    }

    result.push_back(ConvertDBusPowerState(low_level_power_state));
  }

  std::move(callback).Run(result);
}

// Real implementation of CecServiceClient.
class CecServiceClientImpl : public CecServiceClient {
 public:
  CecServiceClientImpl() = default;

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

  ~CecServiceClientImpl() override = default;

  void SendStandBy() override {
    dbus::MethodCall method_call(cecservice::kCecServiceInterface,
                                 cecservice::kSendStandByToAllDevicesMethod);
    cec_service_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void SendWakeUp() override {
    dbus::MethodCall method_call(cecservice::kCecServiceInterface,
                                 cecservice::kSendWakeUpToAllDevicesMethod);
    cec_service_proxy_->CallMethod(&method_call,
                                   dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
                                   base::DoNothing());
  }

  void QueryDisplayCecPowerState(PowerStateCallback callback) override {
    dbus::MethodCall query_method(cecservice::kCecServiceInterface,
                                  cecservice::kGetTvsPowerStatus);
    cec_service_proxy_->CallMethod(
        &query_method, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&OnGetTvsPowerStatus, std::move(callback)));
  }

  void Init(dbus::Bus* bus) override {
    cec_service_proxy_ =
        bus->GetObjectProxy(cecservice::kCecServiceName,
                            dbus::ObjectPath(cecservice::kCecServicePath));
  }

 private:
  scoped_refptr<dbus::ObjectProxy> cec_service_proxy_;
};

}  // namespace

////////////////////////////////////////////////////////////////////////////////
// CecServiceClient

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

// static
void CecServiceClient::Initialize(dbus::Bus* bus) {
  CHECK(bus);
  (new CecServiceClientImpl())->Init(bus);
}

// static
void CecServiceClient::InitializeFake() {
  (new FakeCecServiceClient())->Init(nullptr);
}

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

CecServiceClient::CecServiceClient() {
  CHECK(!g_instance);
  g_instance = this;
}

CecServiceClient::~CecServiceClient() {
  CHECK_EQ(g_instance, this);
  g_instance = nullptr;
}

}  // namespace ash