910e62b5创建于 1月15日历史提交
// 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/network/cellular_utils.h"

#include <algorithm>

#include "ash/constants/ash_features.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/logging.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/ash/components/cryptohome/system_salt_getter.h"
#include "chromeos/ash/components/dbus/hermes/hermes_euicc_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_manager_client.h"
#include "chromeos/ash/components/dbus/hermes/hermes_profile_client.h"
#include "chromeos/ash/components/network/cellular_esim_profile.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_profile.h"
#include "chromeos/ash/components/network/network_profile_handler.h"
#include "crypto/sha2.h"

namespace ash {

namespace cellular_utils {

const char kSmdsGsma[] = "1$lpa.ds.gsma.com$";
const char kSmdsStork[] = "1$prod.smds.rsp.goog$";
const char kSmdsAndroidProduction[] = "1$lpa.live.esimdiscovery.com$";
const char kSmdsAndroidStaging[] = "1$lpa.live.esimdiscovery.dev$";

}  // namespace cellular_utils

namespace {
const char kNonShillCellularNetworkPathPrefix[] = "/non-shill-cellular/";

std::string GetLogSafeEid(const std::string& eid) {
  const SystemSaltGetter::RawSalt* salt = SystemSaltGetter::Get()->GetRawSalt();
  if (!salt) {
    return std::string();
  }
  return crypto::SHA256HashString(
      eid + SystemSaltGetter::ConvertRawSaltToHexString(*salt));
}

}  // namespace

base::flat_set<dbus::ObjectPath> GetProfilePathsFromEuicc(
    HermesEuiccClient::Properties* euicc_properties) {
  base::flat_set<dbus::ObjectPath> profile_paths;

  for (const dbus::ObjectPath& path : euicc_properties->profiles().value()) {
    profile_paths.insert(path);
  }

  return profile_paths;
}

CellularESimProfile::State FromProfileState(hermes::profile::State state) {
  switch (state) {
    case hermes::profile::State::kPending:
      return CellularESimProfile::State::kPending;
    case hermes::profile::State::kInactive:
      return CellularESimProfile::State::kInactive;
    case hermes::profile::State::kActive:
      return CellularESimProfile::State::kActive;
    default:
      NOTREACHED() << "Unexpected Hermes profile state: " << state;
  }
}

std::vector<CellularESimProfile> GenerateProfilesFromEuicc(
    const dbus::ObjectPath& euicc_path) {
  std::vector<CellularESimProfile> profiles;

  HermesEuiccClient::Properties* euicc_properties =
      HermesEuiccClient::Get()->GetProperties(euicc_path);
  std::string eid = euicc_properties->eid().value();

  for (const dbus::ObjectPath& profile_path :
       GetProfilePathsFromEuicc(euicc_properties)) {
    HermesProfileClient::Properties* profile_properties =
        HermesProfileClient::Get()->GetProperties(profile_path);

    // Hermes only exposes eSIM profiles with relevant profile class. e.g.
    // Test profiles are exposed only when Hermes is put into test mode.
    // No additional profile filtering is done on Chrome side.
    profiles.emplace_back(
        FromProfileState(profile_properties->state().value()), profile_path,
        eid, profile_properties->iccid().value(),
        base::UTF8ToUTF16(profile_properties->name().value()),
        base::UTF8ToUTF16(profile_properties->nick_name().value()),
        base::UTF8ToUTF16(profile_properties->service_provider().value()),
        profile_properties->activation_code().value());
  }

  return profiles;
}

const base::flat_map<int32_t, std::string> GetESimSlotToEidMap() {
  base::flat_map<int32_t, std::string> esim_slot_to_eid;
  const std::vector<dbus::ObjectPath>& available_euiccs =
      HermesManagerClient::Get()->GetAvailableEuiccs();
  VLOG(1) << "GetESimSlotToEidMap(): Num available EUICCs: "
          << available_euiccs.size();
  for (auto& euicc_path : available_euiccs) {
    HermesEuiccClient::Properties* properties =
        HermesEuiccClient::Get()->GetProperties(euicc_path);
    int32_t slot_id = properties->physical_slot().value();
    std::string eid = properties->eid().value();
    esim_slot_to_eid.emplace(slot_id, eid);
    VLOG(1) << "EUICC: " << euicc_path.value() << ", slot id: " << slot_id
            << ", eid: " << GetLogSafeEid(eid);
  }
  return esim_slot_to_eid;
}

namespace cellular_utils {

std::vector<CellularESimProfile> GenerateProfilesFromHermes() {
  std::vector<CellularESimProfile> profiles;

  for (const dbus::ObjectPath& euicc_path :
       HermesManagerClient::Get()->GetAvailableEuiccs()) {
    std::vector<CellularESimProfile> profiles_from_euicc =
        GenerateProfilesFromEuicc(euicc_path);
    std::ranges::copy(profiles_from_euicc, std::back_inserter(profiles));
  }

  return profiles;
}

const DeviceState::CellularSIMSlotInfos GetSimSlotInfosWithUpdatedEid(
    const DeviceState* device) {
  const base::flat_map<int32_t, std::string> esim_slot_to_eid =
      GetESimSlotToEidMap();

  DeviceState::CellularSIMSlotInfos sim_slot_infos = device->GetSimSlotInfos();
  VLOG(1) << "GetSimSlotInfosWithUpdatedEid(): Num SIM slot infos: "
          << sim_slot_infos.size();
  for (auto& sim_slot_info : sim_slot_infos) {
    const std::string shill_provided_eid = sim_slot_info.eid;
    VLOG(1) << "SIM slot id: " << sim_slot_info.slot_id
            << ", Shill provided eid: " << GetLogSafeEid(shill_provided_eid);

    // If there is no associated |slot_id| in the map, the SIM slot info refers
    // to a pSIM, and the Hermes provided data is irrelevant.
    auto it = esim_slot_to_eid.find(sim_slot_info.slot_id);
    if (it == esim_slot_to_eid.end())
      continue;

    const std::string hermes_provided_eid = it->second;
    if (!shill_provided_eid.empty() &&
        hermes_provided_eid != shill_provided_eid) {
      LOG(ERROR) << "Hermes provided EID of " << hermes_provided_eid
                 << " does not match Shill provided non-empty EID of "
                 << shill_provided_eid << ". Defaulting to Shill provided EID.";
    } else {
      sim_slot_info.eid = hermes_provided_eid;
    }
  }
  return sim_slot_infos;
}

bool IsSimPrimary(const std::string& iccid, const DeviceState* device) {
  for (const auto& sim_slot_info : device->GetSimSlotInfos()) {
    if (sim_slot_info.iccid == iccid && sim_slot_info.primary) {
      return true;
    }
  }
  return false;
}

std::string GenerateStubCellularServicePath(const std::string& iccid) {
  return base::StrCat({kNonShillCellularNetworkPathPrefix, iccid});
}

const NetworkProfile* GetCellularProfile(
    const NetworkProfileHandler* network_profile_handler) {
  DCHECK(network_profile_handler);
  return network_profile_handler->GetProfileForUserhash(
      /*userhash=*/std::string());
}

bool IsStubCellularServicePath(const std::string& service_path) {
  return base::StartsWith(service_path, kNonShillCellularNetworkPathPrefix);
}

std::optional<dbus::ObjectPath> GetCurrentEuiccPath() {
  // Always use the first Euicc if Hermes only exposes one Euicc.
  // If useSecondEuicc flag is set and there are two Euicc available,
  // use the second available Euicc.
  const std::vector<dbus::ObjectPath>& euicc_paths =
      HermesManagerClient::Get()->GetAvailableEuiccs();
  if (euicc_paths.empty())
    return std::nullopt;

  if (euicc_paths.size() == 1)
    return euicc_paths[0];

  bool use_second_euicc =
      base::FeatureList::IsEnabled(features::kCellularUseSecondEuicc);
  return use_second_euicc ? euicc_paths[1] : euicc_paths[0];
}

std::vector<std::string> GetSmdsActivationCodes() {
  std::vector<std::string> activation_codes;
  if (features::ShouldUseStorkSmds()) {
    activation_codes.emplace_back(kSmdsStork);
  }
  if (features::ShouldUseAndroidStagingSmds()) {
    activation_codes.emplace_back(kSmdsAndroidStaging);
  }
  if (activation_codes.empty()) {
    activation_codes = {
        kSmdsAndroidProduction,
        kSmdsGsma,
    };
  }
  return activation_codes;
}

}  // namespace cellular_utils
}  // namespace ash