chromium/chrome/browser/chromeos/extensions/telemetry/api/telemetry/telemetry_api.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 "chrome/browser/chromeos/extensions/telemetry/api/telemetry/telemetry_api.h"

#include <inttypes.h>

#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/common/remote_probe_service_strategy.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/telemetry/telemetry_api_converters.h"
#include "chrome/common/chromeos/extensions/api/telemetry.h"
#include "chromeos/crosapi/mojom/probe_service.mojom.h"
#include "extensions/common/permissions/permissions_data.h"

namespace chromeos {

namespace {

namespace cx_telem = api::os_telemetry;
namespace crosapi = ::crosapi::mojom;

}  // namespace

// TelemetryApiFunctionBase ----------------------------------------------------

TelemetryApiFunctionBase::TelemetryApiFunctionBase() {}

TelemetryApiFunctionBase::~TelemetryApiFunctionBase() = default;

crosapi::TelemetryProbeService* TelemetryApiFunctionBase::GetRemoteService() {
  DCHECK(RemoteProbeServiceStrategy::Get()->GetRemoteProbeService());
  return RemoteProbeServiceStrategy::Get()->GetRemoteProbeService();
}

#if BUILDFLAG(IS_CHROMEOS_LACROS)
bool TelemetryApiFunctionBase::IsCrosApiAvailable() {
  return RemoteProbeServiceStrategy::Get()->GetRemoteProbeService() != nullptr;
}
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

// OsTelemetryGetAudioInfoFunction ---------------------------------------------

void OsTelemetryGetAudioInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetAudioInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kAudio},
                                         std::move(cb));
}

void OsTelemetryGetAudioInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->audio_result || !ptr->audio_result->is_audio_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& audio_info = ptr->audio_result->get_audio_info();

  auto result = converters::telemetry::ConvertPtr(std::move(audio_info));

  Respond(ArgumentList(cx_telem::GetAudioInfo::Results::Create(result)));
}

// OsTelemetryGetBatteryInfoFunction -------------------------------------------

void OsTelemetryGetBatteryInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetBatteryInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kBattery},
                                         std::move(cb));
}

void OsTelemetryGetBatteryInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->battery_result || !ptr->battery_result->is_battery_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& battery_info = ptr->battery_result->get_battery_info();

  const bool has_permission = extension()->permissions_data()->HasAPIPermission(
      extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber);

  cx_telem::BatteryInfo result = converters::telemetry::ConvertPtr(
      std::move(battery_info), has_permission);

  Respond(ArgumentList(cx_telem::GetBatteryInfo::Results::Create(result)));
}

// OsTelemetryGetNonRemovableBlockDevicesInfoFunction --------------------------

void OsTelemetryGetNonRemovableBlockDevicesInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(
      &OsTelemetryGetNonRemovableBlockDevicesInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo(
      {crosapi::ProbeCategoryEnum::kNonRemovableBlockDevices}, std::move(cb));
}

void OsTelemetryGetNonRemovableBlockDevicesInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->block_device_result ||
      !ptr->block_device_result->is_block_device_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& block_device_info = ptr->block_device_result->get_block_device_info();

  auto infos = converters::telemetry::ConvertPtrVector<
      cx_telem::NonRemovableBlockDeviceInfo>(std::move(block_device_info));
  cx_telem::NonRemovableBlockDeviceInfoResponse result;
  result.device_infos = std::move(infos);

  Respond(ArgumentList(
      cx_telem::GetNonRemovableBlockDevicesInfo::Results::Create(result)));
}

// OsTelemetryGetCpuInfoFunction -----------------------------------------------

void OsTelemetryGetCpuInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetCpuInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kCpu},
                                         std::move(cb));
}

void OsTelemetryGetCpuInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->cpu_result || !ptr->cpu_result->is_cpu_info()) {
    Respond(Error("API internal error"));
    return;
  }

  const auto& cpu_info = ptr->cpu_result->get_cpu_info();

  cx_telem::CpuInfo result;
  if (cpu_info->num_total_threads) {
    result.num_total_threads = cpu_info->num_total_threads->value;
  }
  result.architecture = converters::telemetry::Convert(cpu_info->architecture);
  result.physical_cpus =
      converters::telemetry::ConvertPtrVector<cx_telem::PhysicalCpuInfo>(
          std::move(cpu_info->physical_cpus));

  Respond(ArgumentList(cx_telem::GetCpuInfo::Results::Create(result)));
}

// OsTelemetryGetDisplayInfoFunction
// -----------------------------------------------

void OsTelemetryGetDisplayInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetDisplayInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kDisplay},
                                         std::move(cb));
}

void OsTelemetryGetDisplayInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->display_result || !ptr->display_result->is_display_info()) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::DisplayInfo result;
  result = converters::telemetry::ConvertPtr(
      std::move(ptr->display_result->get_display_info()));

  Respond(ArgumentList(cx_telem::GetDisplayInfo::Results::Create(result)));
}

// OsTelemetryGetInternetConnectivityInfoFunction ------------------------------

void OsTelemetryGetInternetConnectivityInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(
      &OsTelemetryGetInternetConnectivityInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kNetwork},
                                         std::move(cb));
}

void OsTelemetryGetInternetConnectivityInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->network_result ||
      !ptr->network_result->is_network_health()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& network_info = ptr->network_result->get_network_health();

  const bool has_permission = extension()->permissions_data()->HasAPIPermission(
      extensions::mojom::APIPermissionID::kChromeOSTelemetryNetworkInformation);
  auto result = converters::telemetry::ConvertPtr(std::move(network_info),
                                                  has_permission);

  Respond(ArgumentList(
      cx_telem::GetInternetConnectivityInfo::Results::Create(result)));
}

// OsTelemetryGetMarketingInfoFunction -----------------------------------------

void OsTelemetryGetMarketingInfoFunction::RunIfAllowed() {
  auto cb =
      base::BindOnce(&OsTelemetryGetMarketingInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kSystem},
                                         std::move(cb));
}

void OsTelemetryGetMarketingInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->system_result || !ptr->system_result->is_system_info()) {
    Respond(Error("API internal error"));
    return;
  }

  auto& system_info = ptr->system_result->get_system_info();

  if (!system_info->os_info) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::MarketingInfo result;
  result.marketing_name = system_info->os_info->marketing_name;

  Respond(ArgumentList(cx_telem::GetMarketingInfo::Results::Create(result)));
}

// OsTelemetryGetMemoryInfoFunction --------------------------------------------

void OsTelemetryGetMemoryInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetMemoryInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kMemory},
                                         std::move(cb));
}

void OsTelemetryGetMemoryInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->memory_result || !ptr->memory_result->is_memory_info()) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::MemoryInfo result;

  const auto& memory_info = ptr->memory_result->get_memory_info();
  if (memory_info->total_memory_kib) {
    result.total_memory_ki_b = memory_info->total_memory_kib->value;
  }
  if (memory_info->free_memory_kib) {
    result.free_memory_ki_b = memory_info->free_memory_kib->value;
  }
  if (memory_info->available_memory_kib) {
    result.available_memory_ki_b = memory_info->available_memory_kib->value;
  }
  if (memory_info->page_faults_since_last_boot) {
    result.page_faults_since_last_boot =
        memory_info->page_faults_since_last_boot->value;
  }

  Respond(ArgumentList(cx_telem::GetMemoryInfo::Results::Create(result)));
}

// OsTelemetryGetOemDataFunction -----------------------------------------------

void OsTelemetryGetOemDataFunction::RunIfAllowed() {
  // Protect accessing the serial number by a permission.
  if (!extension()->permissions_data()->HasAPIPermission(
          extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber)) {
    Respond(
        Error("Unauthorized access to chrome.os.telemetry.getOemData. Extension"
              " doesn't have the permission."));
    return;
  }

  auto cb = base::BindOnce(&OsTelemetryGetOemDataFunction::OnResult, this);

  GetRemoteService()->GetOemData(std::move(cb));
}

void OsTelemetryGetOemDataFunction::OnResult(crosapi::ProbeOemDataPtr ptr) {
  if (!ptr || !ptr->oem_data.has_value()) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::OemData result;
  result.oem_data = std::move(ptr->oem_data);

  Respond(ArgumentList(cx_telem::GetOemData::Results::Create(result)));
}

// OsTelemetryGetOsVersionInfoFunction -----------------------------------------

void OsTelemetryGetOsVersionInfoFunction::RunIfAllowed() {
  auto cb =
      base::BindOnce(&OsTelemetryGetOsVersionInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kSystem},
                                         std::move(cb));
}

void OsTelemetryGetOsVersionInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->system_result || !ptr->system_result->is_system_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& system_info = ptr->system_result->get_system_info();

  // os_version is an optional value and might not be present.
  // TODO(b/234338704): check how to test this.
  if (!system_info->os_info || !system_info->os_info->os_version) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::OsVersionInfo result = converters::telemetry::ConvertPtr(
      std::move(system_info->os_info->os_version));

  Respond(ArgumentList(cx_telem::GetOsVersionInfo::Results::Create(result)));
}

// OsTelemetryGetStatefulPartitionInfoFunction ---------------------------------

void OsTelemetryGetStatefulPartitionInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(
      &OsTelemetryGetStatefulPartitionInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo(
      {crosapi::ProbeCategoryEnum::kStatefulPartition}, std::move(cb));
}

void OsTelemetryGetStatefulPartitionInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->stateful_partition_result ||
      !ptr->stateful_partition_result->is_partition_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& stateful_part_info =
      ptr->stateful_partition_result->get_partition_info();

  cx_telem::StatefulPartitionInfo result =
      converters::telemetry::ConvertPtr(std::move(stateful_part_info));

  Respond(ArgumentList(
      cx_telem::GetStatefulPartitionInfo::Results::Create(result)));
}

// OsTelemetryGetThermalInfoFunction
// -----------------------------------------------

void OsTelemetryGetThermalInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetThermalInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kThermal},
                                         std::move(cb));
}

void OsTelemetryGetThermalInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->thermal_result || !ptr->thermal_result->is_thermal_info()) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::ThermalInfo result;
  result = converters::telemetry::ConvertPtr(
      std::move(ptr->thermal_result->get_thermal_info()));

  Respond(ArgumentList(cx_telem::GetThermalInfo::Results::Create(result)));
}

// OsTelemetryGetTpmInfoFunction -----------------------------------------------

void OsTelemetryGetTpmInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetTpmInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kTpm},
                                         std::move(cb));
}

void OsTelemetryGetTpmInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->tpm_result || !ptr->tpm_result->is_tpm_info()) {
    Respond(Error("API internal error"));
    return;
  }
  auto& tpm_info = ptr->tpm_result->get_tpm_info();

  cx_telem::TpmInfo result =
      converters::telemetry::ConvertPtr(std::move(tpm_info));

  Respond(ArgumentList(cx_telem::GetTpmInfo::Results::Create(result)));
}

// OsTelemetryGetUsbBusInfoFunction --------------------------------------------

void OsTelemetryGetUsbBusInfoFunction::RunIfAllowed() {
  // USB info is guarded by the `os.attached_device_info` permission.
  if (!extension()->permissions_data()->HasAPIPermission(
          extensions::mojom::APIPermissionID::kChromeOSAttachedDeviceInfo)) {
    Respond(Error(
        "Unauthorized access to chrome.os.telemetry.getUsbBusInfo. Extension"
        " doesn't have the permission."));
    return;
  }

  auto cb = base::BindOnce(&OsTelemetryGetUsbBusInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo({crosapi::ProbeCategoryEnum::kBus},
                                         std::move(cb));
}

void OsTelemetryGetUsbBusInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->bus_result || !ptr->bus_result->is_bus_devices_info()) {
    Respond(Error("API internal error"));
    return;
  }

  cx_telem::UsbBusDevices result;
  auto bus_infos = std::move(ptr->bus_result->get_bus_devices_info());
  for (auto& info : bus_infos) {
    if (info->is_usb_bus_info()) {
      result.devices.push_back(converters::telemetry::ConvertPtr(
          std::move(info->get_usb_bus_info())));
    }
  }

  Respond(ArgumentList(cx_telem::GetUsbBusInfo::Results::Create(result)));
}

// OsTelemetryGetVpdInfoFunction -----------------------------------------------

void OsTelemetryGetVpdInfoFunction::RunIfAllowed() {
  auto cb = base::BindOnce(&OsTelemetryGetVpdInfoFunction::OnResult, this);

  GetRemoteService()->ProbeTelemetryInfo(
      {crosapi::ProbeCategoryEnum::kCachedVpdData}, std::move(cb));
}

void OsTelemetryGetVpdInfoFunction::OnResult(
    crosapi::ProbeTelemetryInfoPtr ptr) {
  if (!ptr || !ptr->vpd_result || !ptr->vpd_result->is_vpd_info()) {
    Respond(Error("API internal error"));
    return;
  }

  const bool has_permission = extension()->permissions_data()->HasAPIPermission(
      extensions::mojom::APIPermissionID::kChromeOSTelemetrySerialNumber);
  auto result = converters::telemetry::ConvertPtr(
      std::move(ptr->vpd_result->get_vpd_info()), has_permission);

  Respond(ArgumentList(cx_telem::GetVpdInfo::Results::Create(result)));
}

}  // namespace chromeos