chromium/chromeos/ash/components/network/managed_cellular_pref_handler.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/network/managed_cellular_pref_handler.h"

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/check.h"
#include "base/values.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/policy_util.h"
#include "components/onc/onc_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/scoped_user_pref_update.h"

namespace ash {

// static
void ManagedCellularPrefHandler::RegisterLocalStatePrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterDictionaryPref(prefs::kManagedCellularESimMetadata);
  registry->RegisterDictionaryPref(prefs::kManagedCellularIccidSmdpPair);
  registry->RegisterDictionaryPref(prefs::kApnMigratedIccids);
}

ManagedCellularPrefHandler::ManagedCellularPrefHandler() = default;
ManagedCellularPrefHandler::~ManagedCellularPrefHandler() = default;

void ManagedCellularPrefHandler::Init(
    NetworkStateHandler* network_state_handler) {
  network_state_handler_ = network_state_handler;
}

void ManagedCellularPrefHandler::SetDevicePrefs(PrefService* device_prefs) {
  device_prefs_ = device_prefs;

  if (!device_prefs_ ||
      device_prefs_->HasPrefPath(prefs::kManagedCellularESimMetadata)) {
    return;
  }

  MigrateExistingPrefs();
}

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

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

bool ManagedCellularPrefHandler::HasObserver(Observer* observer) const {
  return observer_list_.HasObserver(observer);
}

void ManagedCellularPrefHandler::NotifyManagedCellularPrefChanged() {
  for (auto& observer : observer_list_)
    observer.OnManagedCellularPrefChanged();
}

void ManagedCellularPrefHandler::AddESimMetadata(
    const std::string& iccid,
    const std::string& name,
    const policy_util::SmdxActivationCode& activation_code,
    bool sync_stub_networks) {
  DCHECK(!name.empty());
  DCHECK(!activation_code.value().empty());

  if (!device_prefs_) {
    NET_LOG(ERROR) << "Device pref not available yet";
    return;
  }

  auto esim_metadata =
      base::Value::Dict()
          .Set(::onc::network_config::kName, name)
          .Set(activation_code.type() ==
                       policy_util::SmdxActivationCode::Type::SMDP
                   ? ::onc::cellular::kSMDPAddress
                   : ::onc::cellular::kSMDSAddress,
               activation_code.value());

  const base::Value::Dict* existing_esim_metadata = GetESimMetadata(iccid);
  if (existing_esim_metadata && *existing_esim_metadata == esim_metadata) {
    return;
  }

  NET_LOG(EVENT) << "Adding eSIM metadata to device prefs, ICCID: " << iccid
                 << ", name: " << name
                 << ", activation code: " << activation_code.ToString();

  ScopedDictPrefUpdate update(device_prefs_,
                              prefs::kManagedCellularESimMetadata);
  update->Set(iccid, std::move(esim_metadata));
  if (sync_stub_networks) {
    network_state_handler_->SyncStubCellularNetworks();
  }
  NotifyManagedCellularPrefChanged();
}

const base::Value::Dict* ManagedCellularPrefHandler::GetESimMetadata(
    const std::string& iccid) {

  if (!device_prefs_) {
    NET_LOG(ERROR) << "Device pref not available yet";
    return nullptr;
  }
  const base::Value::Dict& esim_metadata =
      device_prefs_->GetDict(prefs::kManagedCellularESimMetadata);
  return esim_metadata.FindDict(iccid);
}

void ManagedCellularPrefHandler::RemoveESimMetadata(const std::string& iccid) {
  if (!device_prefs_) {
    NET_LOG(ERROR) << "Device pref not available yet";
    return;
  }

  const base::Value::Dict& esim_metadata =
      device_prefs_->GetDict(prefs::kManagedCellularESimMetadata);
  if (!esim_metadata.contains(iccid)) {
    return;
  }

  NET_LOG(EVENT) << "Removing eSIM metadata from device prefs, ICCID: "
                 << iccid;
  ScopedDictPrefUpdate update(device_prefs_,
                              prefs::kManagedCellularESimMetadata);
  update->Remove(iccid);
  network_state_handler_->SyncStubCellularNetworks();
  NotifyManagedCellularPrefChanged();
}

void ManagedCellularPrefHandler::AddApnMigratedIccid(const std::string& iccid) {
  if (!device_prefs_) {
    NET_LOG(ERROR) << "Device pref not available yet.";
    return;
  }
  if (ContainsApnMigratedIccid(iccid)) {
    NET_LOG(ERROR)
        << "AddApnMigratedIccid: Called with already migrated network, iccid: "
        << iccid;
    return;
  }

  NET_LOG(EVENT)
      << "AddApnMigratedIccid: Adding migrated network to device pref, iccid: "
      << iccid;
  ScopedDictPrefUpdate update(device_prefs_, prefs::kApnMigratedIccids);
  update->SetByDottedPath(iccid, true);
}

bool ManagedCellularPrefHandler::ContainsApnMigratedIccid(
    const std::string& iccid) const {
  if (!device_prefs_) {
    NET_LOG(ERROR) << "Device pref not available yet.";
    return false;
  }
  const base::Value::Dict& apn_migrated_iccids =
      device_prefs_->GetDict(prefs::kApnMigratedIccids);
  return apn_migrated_iccids.FindBool(iccid).value_or(false);
}

void ManagedCellularPrefHandler::MigrateExistingPrefs() {
  DCHECK(device_prefs_);

  NET_LOG(EVENT) << "Starting migration of existing ICCID and SM-DP+ pairs";

  const base::Value::Dict& existing_prefs =
      device_prefs_->GetDict(prefs::kManagedCellularIccidSmdpPair);

  for (const auto [iccid, value] : existing_prefs) {
    const std::string& smdp_activation_code = value.GetString();
    if (smdp_activation_code.empty()) {
      NET_LOG(ERROR) << "Failed to migrate ICCID and SM-DP+ pair due to "
                     << "missing activation code";
      continue;
    }

    ScopedDictPrefUpdate update(device_prefs_,
                                prefs::kManagedCellularESimMetadata);
    update->Set(iccid, base::Value::Dict().Set(::onc::cellular::kSMDPAddress,
                                               smdp_activation_code));

    NET_LOG(EVENT) << "Successfully migrated ICCID and SM-DP+ pair";
  }

  NET_LOG(EVENT) << "Finished migration of existing ICCID and SM-DP+ pairs";
}

}  // namespace ash