chromium/chromeos/ash/components/dbus/typecd/typecd_client.cc

// Copyright 2021 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/typecd/typecd_client.h"

#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/ash/components/dbus/typecd/fake_typecd_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/typecd/dbus-constants.h"

namespace ash {

namespace {
TypecdClient* g_instance = nullptr;
}  // namespace

class TypecdClientImpl : public TypecdClient {
 public:
  TypecdClientImpl() = default;
  TypecdClientImpl(const TypecdClientImpl&) = delete;
  TypecdClientImpl& operator=(const TypecdClientImpl&) = delete;
  ~TypecdClientImpl() override = default;

  // TypecdClient overrides
  void SetPeripheralDataAccessPermissionState(bool permitted) override;
  void SetTypeCPortsUsingDisplays(
      const std::vector<uint32_t>& port_nums) override;

  void Init(dbus::Bus* bus);

 private:
  void ThunderboltDeviceConnectedReceived(dbus::Signal* signal);
  void CableWarningReceived(dbus::Signal* signal);
  void OnSignalConnected(const std::string& interface_name,
                         const std::string& signal_name,
                         bool success);

  raw_ptr<dbus::ObjectProxy> typecd_proxy_ = nullptr;
  base::WeakPtrFactory<TypecdClientImpl> weak_ptr_factory_{this};
};

// TypecdClientImpl
void TypecdClientImpl::Init(dbus::Bus* bus) {
  typecd_proxy_ = bus->GetObjectProxy(
      typecd::kTypecdServiceName, dbus::ObjectPath(typecd::kTypecdServicePath));

  // Listen to D-Bus signals emitted by typecd.
  typedef void (TypecdClientImpl::*SignalMethod)(dbus::Signal*);
  const std::pair<const char*, SignalMethod> kSignalMethods[] = {
      {typecd::kTypecdDeviceConnected,
       &TypecdClientImpl::ThunderboltDeviceConnectedReceived},
      {typecd::kTypecdCableWarning, &TypecdClientImpl::CableWarningReceived}};

  auto on_connected_callback = base::BindRepeating(
      &TypecdClientImpl::OnSignalConnected, weak_ptr_factory_.GetWeakPtr());

  for (const auto& signal : kSignalMethods) {
    typecd_proxy_->ConnectToSignal(
        typecd::kTypecdServiceInterface, signal.first,
        base::BindRepeating(signal.second, weak_ptr_factory_.GetWeakPtr()),
        on_connected_callback);
  }
}

void TypecdClientImpl::ThunderboltDeviceConnectedReceived(
    dbus::Signal* signal) {
  dbus::MessageReader reader(signal);
  uint32_t device_connected_type = 0u;
  if (!reader.PopUint32(&device_connected_type)) {
    LOG(ERROR) << "Typecd: Unable to decode connected device type from"
               << typecd::kTypecdDeviceConnected << " signal.";
    return;
  }

  VLOG(1) << "Typecd: Received device connected signal with "
          << "DeviceConnectedType: " << device_connected_type;
  NotifyOnThunderboltDeviceConnected(
      device_connected_type ==
      static_cast<uint32_t>(typecd::DeviceConnectedType::kThunderboltOnly));
}

void TypecdClientImpl::CableWarningReceived(dbus::Signal* signal) {
  dbus::MessageReader reader(signal);
  uint32_t cable_warning_signal = 0u;
  if (!reader.PopUint32(&cable_warning_signal)) {
    LOG(ERROR) << "Typecd: Unable to decode cable warning type from"
               << typecd::kTypecdCableWarning << " signal.";
    return;
  }
  typecd::CableWarningType cable_warning_type =
      static_cast<typecd::CableWarningType>(cable_warning_signal);
  VLOG(1) << "Typecd: Received cable warning signal with "
          << "CableWarningType: " << cable_warning_signal;
  NotifyOnCableWarning(cable_warning_type);
}

void TypecdClientImpl::OnSignalConnected(const std::string& interface_name,
                                         const std::string& signal_name,
                                         bool success) {
  if (!success) {
    LOG(ERROR) << "Typecd: Failed to connect to signal " << signal_name << ".";
    return;
  }
  VLOG(1) << "Typecd: Successfully connected to signal " << signal_name << ".";
}

void TypecdClientImpl::SetPeripheralDataAccessPermissionState(bool permitted) {
  dbus::MethodCall method_call(typecd::kTypecdServiceInterface,
                               typecd::kTypecdSetPeripheralDataAccessMethod);
  VLOG(1) << "Typecd: Sending peripheral data access enabled state: "
          << permitted;

  dbus::MessageWriter writer(&method_call);
  writer.AppendBool(permitted);

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

void TypecdClientImpl::SetTypeCPortsUsingDisplays(
    const std::vector<uint32_t>& port_nums) {
  dbus::MethodCall method_call(typecd::kTypecdServiceInterface,
                               typecd::kTypecdSetPortsUsingDisplaysMethod);
  dbus::MessageWriter writer(&method_call);
  writer.AppendArrayOfUint32s(port_nums);

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

// TypecdClient
void TypecdClient::AddObserver(TypecdClient::Observer* observer) {
  observer_list_.AddObserver(observer);
}

void TypecdClient::RemoveObserver(TypecdClient::Observer* observer) {
  observer_list_.RemoveObserver(observer);
}

void TypecdClient::NotifyOnThunderboltDeviceConnected(
    bool is_thunderbolt_only) {
  for (auto& observer : observer_list_)
    observer.OnThunderboltDeviceConnected(is_thunderbolt_only);
}

void TypecdClient::NotifyOnCableWarning(
    typecd::CableWarningType cable_warning_type) {
  for (auto& observer : observer_list_)
    observer.OnCableWarning(cable_warning_type);
}

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

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

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

// static
void TypecdClient::InitializeFake() {
  new FakeTypecdClient();
}

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

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

}  // namespace ash