chromium/chromeos/ash/components/dbus/arc/arcvm_data_migrator_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/arc/arcvm_data_migrator_client.h"

#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chromeos/ash/components/dbus/arc/fake_arcvm_data_migrator_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 {

ArcVmDataMigratorClient* g_instance = nullptr;

void OnSignalConnected(const std::string& interface_name,
                       const std::string& signal_name,
                       bool success) {
  DCHECK_EQ(interface_name, arc::data_migrator::kArcVmDataMigratorInterface);
  LOG_IF(DFATAL, !success) << "Failed to connect to D-Bus signal; interface: "
                           << interface_name << "; signal: " << signal_name;
}

class ArcVmDataMigratorClientImpl : public ArcVmDataMigratorClient {
 public:
  explicit ArcVmDataMigratorClientImpl(dbus::Bus* bus)
      : proxy_(bus->GetObjectProxy(
            arc::data_migrator::kArcVmDataMigratorServiceName,
            dbus::ObjectPath(
                arc::data_migrator::kArcVmDataMigratorServicePath))) {
    proxy_->ConnectToSignal(
        arc::data_migrator::kArcVmDataMigratorInterface,
        arc::data_migrator::kMigrationProgressSignal,
        base::BindRepeating(&ArcVmDataMigratorClientImpl::OnMigrationProgress,
                            weak_ptr_factory_.GetWeakPtr()),
        base::BindOnce(&OnSignalConnected));
  }

  ~ArcVmDataMigratorClientImpl() override = default;

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

  // ArcVmDataMigratorClient overrides:
  void HasDataToMigrate(
      const arc::data_migrator::HasDataToMigrateRequest& request,
      chromeos::DBusMethodCallback<bool> callback) override {
    dbus::MethodCall method_call(
        arc::data_migrator::kArcVmDataMigratorInterface,
        arc::data_migrator::kHasDataToMigrateMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendProtoAsArrayOfBytes(request);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&ArcVmDataMigratorClientImpl::OnBoolMethod,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  void GetAndroidDataInfo(
      const arc::data_migrator::GetAndroidDataInfoRequest& request,
      GetAndroidDataInfoCallback callback) override {
    dbus::MethodCall method_call(
        arc::data_migrator::kArcVmDataMigratorInterface,
        arc::data_migrator::kGetAndroidDataInfoMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendProtoAsArrayOfBytes(request);
    proxy_->CallMethod(
        &method_call,
        kArcVmDataMigratorGetAndroidDataInfoTimeout.InMilliseconds(),
        base::BindOnce(
            &ArcVmDataMigratorClientImpl::OnGetAndroidDataInfoResponse,
            weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  void StartMigration(const arc::data_migrator::StartMigrationRequest& request,
                      chromeos::VoidDBusMethodCallback callback) override {
    dbus::MethodCall method_call(
        arc::data_migrator::kArcVmDataMigratorInterface,
        arc::data_migrator::kStartMigrationMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendProtoAsArrayOfBytes(request);
    proxy_->CallMethod(
        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::BindOnce(&ArcVmDataMigratorClientImpl::OnVoidMethod,
                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
  }

  void AddObserver(Observer* observer) override {
    observers_.AddObserver(observer);
  }

  void RemoveObserver(Observer* observer) override {
    observers_.RemoveObserver(observer);
  }

 private:
  void OnMigrationProgress(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    arc::data_migrator::DataMigrationProgress proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Failed to parse DataMigrationProgress protobuf from "
                    "D-Bus signal";
      return;
    }
    for (Observer& observer : observers_) {
      observer.OnDataMigrationProgress(proto);
    }
  }

  void OnVoidMethod(chromeos::VoidDBusMethodCallback callback,
                    dbus::Response* response) {
    std::move(callback).Run(response);
  }

  void OnBoolMethod(chromeos::DBusMethodCallback<bool> callback,
                    dbus::Response* response) {
    if (!response) {
      std::move(callback).Run(std::nullopt);
      return;
    }
    dbus::MessageReader reader(response);
    bool result = false;
    if (!reader.PopBool(&result)) {
      LOG(ERROR) << "Invalid response: " << response->ToString();
      std::move(callback).Run(std::nullopt);
      return;
    }
    std::move(callback).Run(result);
  }

  void OnGetAndroidDataInfoResponse(GetAndroidDataInfoCallback callback,
                                    dbus::Response* response) {
    if (!response) {
      std::move(callback).Run(std::nullopt);
      return;
    }
    dbus::MessageReader reader(response);
    arc::data_migrator::GetAndroidDataInfoResponse proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Invalid response: " << response->ToString();
      std::move(callback).Run(std::nullopt);
      return;
    }
    std::move(callback).Run(std::move(proto));
  }

  base::ObserverList<Observer> observers_;
  raw_ptr<dbus::ObjectProxy> proxy_;
  base::WeakPtrFactory<ArcVmDataMigratorClientImpl> weak_ptr_factory_{this};
};

}  // namespace

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

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

// static
void ArcVmDataMigratorClient::Initialize(dbus::Bus* bus) {
  DCHECK(bus);
  new ArcVmDataMigratorClientImpl(bus);
}

// static
void ArcVmDataMigratorClient::InitializeFake() {
  // Do not create a new fake if it was initialized early in a browser test (to
  // allow test properties to be set).
  if (!FakeArcVmDataMigratorClient::Get())
    new FakeArcVmDataMigratorClient();
}

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

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

}  // namespace ash