chromium/chromeos/ash/components/network/cellular_esim_profile_waiter.cc

// Copyright 2023 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/network/cellular_esim_profile_waiter.h"

#include <string>
#include "base/check.h"
#include "base/containers/flat_map.h"
#include "base/functional/callback_forward.h"
#include "base/scoped_observation.h"
#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
#include "components/device_event_log/device_event_log.h"
#include "dbus/object_path.h"
#include "dbus/property.h"

namespace ash {

CellularESimProfileWaiter::CellularESimProfileWaiter() = default;

CellularESimProfileWaiter::~CellularESimProfileWaiter() {
  hermes_manager_client_observer_.Reset();
  hermes_profile_client_observer_.Reset();
  if (!on_success_.is_null()) {
    NET_LOG(EVENT)
        << "CellularESimProfileWaiter was destructed while waiting for "
        << "all of the conditions to be satisfied";
    std::move(on_success_).Run();
  }
  on_shutdown_.Reset();
}

void CellularESimProfileWaiter::RequirePendingProfile(
    const dbus::ObjectPath& profile_path) {
  AddCondition(
      profile_path,
      base::BindRepeating(
          [](HermesProfileClient::Properties* profile_properties) {
            return profile_properties &&
                   profile_properties->state().value() ==
                       hermes::profile::State::kPending &&
                   !profile_properties->name().value().empty() &&
                   !profile_properties->activation_code().value().empty();
          }));
}

void CellularESimProfileWaiter::Wait(base::OnceCallback<void()> on_success,
                                     base::OnceCallback<void()> on_shutdown) {
  DCHECK(on_success_.is_null() && !on_success.is_null());
  DCHECK(on_shutdown_.is_null() && !on_shutdown.is_null());
  on_success_ = std::move(on_success);
  on_shutdown_ = std::move(on_shutdown);

  if (profile_path_to_condition_.empty()) {
    std::move(on_success_).Run();
    on_shutdown_.Reset();
    return;
  }

  hermes_manager_client_observer_.Observe(HermesManagerClient::Get());
  hermes_profile_client_observer_.Observe(HermesProfileClient::Get());

  for (auto entry = profile_path_to_condition_.begin();
       entry != profile_path_to_condition_.end();) {
    if (EvaluateCondition(entry->first, entry->second)) {
      entry = profile_path_to_condition_.erase(entry);
    } else {
      entry++;
    }
  }
  MaybeFinish();
}

bool CellularESimProfileWaiter::waiting() const {
  return !profile_path_to_condition_.empty();
}

void CellularESimProfileWaiter::OnShutdown() {
  NET_LOG(EVENT) << "Hermes clients shut down while waiting for all of the "
                 << "conditions to be satisfied";

  hermes_manager_client_observer_.Reset();
  hermes_profile_client_observer_.Reset();

  profile_path_to_condition_.clear();

  DCHECK(!on_shutdown_.is_null());

  on_success_.Reset();
  std::move(on_shutdown_).Run();
}

void CellularESimProfileWaiter::OnCarrierProfilePropertyChanged(
    const dbus::ObjectPath& object_path,
    const std::string& property_name) {
  auto entry = profile_path_to_condition_.find(object_path);
  if (entry == profile_path_to_condition_.end()) {
    return;
  }
  if (EvaluateCondition(/*profile_path=*/entry->first,
                        /*condition=*/entry->second)) {
    profile_path_to_condition_.erase(entry->first);
  }
  MaybeFinish();
}

void CellularESimProfileWaiter::AddCondition(
    const dbus::ObjectPath& profile_path,
    Condition condition) {
  DCHECK(!condition.is_null());
  profile_path_to_condition_.insert_or_assign(profile_path,
                                              std::move(condition));
}

bool CellularESimProfileWaiter::EvaluateCondition(
    const dbus::ObjectPath& profile_path,
    const Condition& condition) {
  HermesProfileClient::Properties* profile_properties =
      HermesProfileClient::Get()->GetProperties(profile_path);
  if (!profile_properties) {
    return false;
  }
  return condition.Run(profile_properties);
}

void CellularESimProfileWaiter::MaybeFinish() {
  if (!profile_path_to_condition_.empty()) {
    return;
  }
  hermes_manager_client_observer_.Reset();
  hermes_profile_client_observer_.Reset();

  if (on_success_.is_null()) {
    return;
  }

  NET_LOG(EVENT) << "All conditions have been satisfied";

  std::move(on_success_).Run();
  on_shutdown_.Reset();
}

}  // namespace ash