// 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/diagnostics/diagnostics_api.h"
#include <cstddef>
#include <string>
#include <utility>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/uuid.h"
#include "base/values.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/diagnostics/diagnostics_api_converters.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/diagnostics/diagnostics_api_metrics.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/diagnostics/remote_diagnostics_service_strategy.h"
#include "chrome/browser/chromeos/extensions/telemetry/api/routines/diagnostic_routine_manager.h"
#include "chrome/common/chromeos/extensions/api/diagnostics.h"
#include "chromeos/crosapi/mojom/diagnostics_service.mojom.h"
#include "chromeos/crosapi/mojom/nullable_primitives.mojom.h"
#include "chromeos/crosapi/mojom/telemetry_diagnostic_routine_service.mojom.h"
#include "chromeos/crosapi/mojom/telemetry_extension_exception.mojom.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/permissions/permissions_data.h"
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "base/strings/stringprintf.h"
#include "chromeos/lacros/lacros_service.h"
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
namespace chromeos {
namespace {
namespace cx_diag = api::os_diagnostics;
base::expected<cx_diag::RoutineSupportStatusInfo, std::string>
ParseRoutineArgumentSupportResult(
crosapi::mojom::TelemetryExtensionSupportStatusPtr result) {
switch (result->which()) {
case crosapi::mojom::TelemetryExtensionSupportStatus::Tag::
kUnmappedUnionField:
return base::unexpected("API internal error.");
case crosapi::mojom::TelemetryExtensionSupportStatus::Tag::kException:
return base::unexpected(result->get_exception()->debug_message);
case crosapi::mojom::TelemetryExtensionSupportStatus::Tag::kSupported: {
cx_diag::RoutineSupportStatusInfo info;
info.status = cx_diag::RoutineSupportStatus::kSupported;
return base::ok(std::move(info));
}
case crosapi::mojom::TelemetryExtensionSupportStatus::Tag::kUnsupported: {
cx_diag::RoutineSupportStatusInfo info;
info.status = cx_diag::RoutineSupportStatus::kUnsupported;
return base::ok(std::move(info));
}
}
NOTREACHED();
}
bool IsPendingApprovalRoutine(
const crosapi::mojom::TelemetryDiagnosticRoutineArgumentPtr& arg) {
return false;
}
} // namespace
// DiagnosticsApiFunctionV1AndV2Base -------------------------------------------
template <class Params>
std::optional<Params> DiagnosticsApiFunctionV1AndV2Base::GetParams() {
auto params = Params::Create(args());
if (!params) {
SetBadMessage();
Respond(BadMessage());
}
return params;
}
// DiagnosticsApiFunctionBase --------------------------------------------------
DiagnosticsApiFunctionBase::DiagnosticsApiFunctionBase()
: remote_diagnostics_service_strategy_(
RemoteDiagnosticsServiceStrategy::Create()) {}
DiagnosticsApiFunctionBase::~DiagnosticsApiFunctionBase() = default;
mojo::Remote<crosapi::mojom::DiagnosticsService>&
DiagnosticsApiFunctionBase::GetRemoteService() {
DCHECK(remote_diagnostics_service_strategy_);
return remote_diagnostics_service_strategy_->GetRemoteService();
}
#if BUILDFLAG(IS_CHROMEOS_LACROS)
bool DiagnosticsApiFunctionBase::IsCrosApiAvailable() {
return remote_diagnostics_service_strategy_ != nullptr;
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
// DiagnosticsApiFunctionBaseV2 ------------------------------------------------
#if BUILDFLAG(IS_CHROMEOS_LACROS)
bool DiagnosticsApiFunctionBaseV2::IsCrosApiAvailable() {
return LacrosService::Get() &&
LacrosService::Get()
->IsAvailable<
crosapi::mojom::TelemetryDiagnosticRoutinesService>();
}
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
// OsDiagnosticsGetAvailableRoutinesFunction -----------------------------------
void OsDiagnosticsGetAvailableRoutinesFunction::RunIfAllowed() {
auto cb = base::BindOnce(&OsDiagnosticsGetAvailableRoutinesFunction::OnResult,
this);
GetRemoteService()->GetAvailableRoutines(std::move(cb));
}
void OsDiagnosticsGetAvailableRoutinesFunction::OnResult(
const std::vector<crosapi::mojom::DiagnosticsRoutineEnum>& routines) {
cx_diag::GetAvailableRoutinesResponse result;
for (const auto in : routines) {
cx_diag::RoutineType out;
if (converters::diagnostics::ConvertMojoRoutine(in, &out)) {
result.routines.push_back(out);
}
}
Respond(ArgumentList(cx_diag::GetAvailableRoutines::Results::Create(result)));
}
// OsDiagnosticsGetRoutineUpdateFunction ---------------------------------------
void OsDiagnosticsGetRoutineUpdateFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::GetRoutineUpdate::Params>();
if (!params) {
return;
}
auto cb =
base::BindOnce(&OsDiagnosticsGetRoutineUpdateFunction::OnResult, this);
GetRemoteService()->GetRoutineUpdate(
params->request.id,
converters::diagnostics::ConvertRoutineCommand(params->request.command),
/* include_output= */ true, std::move(cb));
}
void OsDiagnosticsGetRoutineUpdateFunction::OnResult(
crosapi::mojom::DiagnosticsRoutineUpdatePtr ptr) {
if (!ptr) {
// |ptr| should never be null, otherwise Mojo validation will fail.
// However it's safer to handle it in case of API changes.
Respond(Error("API internal error"));
return;
}
cx_diag::GetRoutineUpdateResponse result;
result.progress_percent = ptr->progress_percent;
if (ptr->output.has_value() && !ptr->output.value().empty()) {
result.output = std::move(ptr->output);
}
switch (ptr->routine_update_union->which()) {
case crosapi::mojom::DiagnosticsRoutineUpdateUnion::Tag::
kNoninteractiveUpdate: {
auto& routine_update =
ptr->routine_update_union->get_noninteractive_update();
result.status =
converters::diagnostics::ConvertRoutineStatus(routine_update->status);
result.status_message = std::move(routine_update->status_message);
break;
}
case crosapi::mojom::DiagnosticsRoutineUpdateUnion::Tag::kInteractiveUpdate:
// Routine is waiting for user action. Set the status to waiting.
result.status = cx_diag::RoutineStatus::kWaitingUserAction;
result.status_message = "Waiting for user action. See user_message";
result.user_message = converters::diagnostics::ConvertRoutineUserMessage(
ptr->routine_update_union->get_interactive_update()->user_message);
break;
}
Respond(ArgumentList(cx_diag::GetRoutineUpdate::Results::Create(result)));
}
// DiagnosticsApiRunRoutineFunctionBase ----------------------------------------
void DiagnosticsApiRunRoutineFunctionBase::OnResult(
crosapi::mojom::DiagnosticsRunRoutineResponsePtr ptr) {
if (!ptr) {
// |ptr| should never be null, otherwise Mojo validation will fail.
// However it's safer to handle it in case of API changes.
Respond(Error("API internal error"));
return;
}
cx_diag::RunRoutineResponse result;
result.id = ptr->id;
result.status = converters::diagnostics::ConvertRoutineStatus(ptr->status);
Respond(WithArguments(result.ToValue()));
}
base::OnceCallback<void(crosapi::mojom::DiagnosticsRunRoutineResponsePtr)>
DiagnosticsApiRunRoutineFunctionBase::GetOnResult() {
return base::BindOnce(&DiagnosticsApiRunRoutineFunctionBase::OnResult, this);
}
// OsDiagnosticsRunAcPowerRoutineFunction ------------------------------
void OsDiagnosticsRunAcPowerRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunAcPowerRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunAcPowerRoutine(
converters::diagnostics::ConvertAcPowerStatusRoutineType(
params->request.expected_status),
params->request.expected_power_type, GetOnResult());
}
// OsDiagnosticsRunBatteryCapacityRoutineFunction ------------------------------
void OsDiagnosticsRunBatteryCapacityRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunBatteryCapacityRoutine(GetOnResult());
}
// OsDiagnosticsRunBatteryChargeRoutineFunction --------------------------------
void OsDiagnosticsRunBatteryChargeRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunBatteryChargeRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunBatteryChargeRoutine(
params->request.length_seconds,
params->request.minimum_charge_percent_required, GetOnResult());
}
// OsDiagnosticsRunBatteryDischargeRoutineFunction -----------------------------
void OsDiagnosticsRunBatteryDischargeRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunBatteryDischargeRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunBatteryDischargeRoutine(
params->request.length_seconds,
params->request.maximum_discharge_percent_allowed, GetOnResult());
}
// OsDiagnosticsRunBatteryHealthRoutineFunction --------------------------------
void OsDiagnosticsRunBatteryHealthRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunBatteryHealthRoutine(GetOnResult());
}
// OsDiagnosticsRunBluetoothDiscoveryRoutineFunction ---------------------------
void OsDiagnosticsRunBluetoothDiscoveryRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunBluetoothDiscoveryRoutine(GetOnResult());
}
// OsDiagnosticsRunBluetoothPairingRoutineFunction -----------------------------
void OsDiagnosticsRunBluetoothPairingRoutineFunction::RunIfAllowed() {
// Pairing Routine is guarded by `os.bluetooth_peripherals_info` permission.
if (!extension()->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::
kChromeOSBluetoothPeripheralsInfo)) {
Respond(
Error("Unauthorized access to "
"chrome.os.diagnostics.runBluetoothPairingRoutine. Extension "
"doesn't have the permission."));
return;
}
const auto params = GetParams<cx_diag::RunBluetoothPairingRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunBluetoothPairingRoutine(params->request.peripheral_id,
GetOnResult());
}
// OsDiagnosticsRunBluetoothPowerRoutineFunction -------------------------------
void OsDiagnosticsRunBluetoothPowerRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunBluetoothPowerRoutine(GetOnResult());
}
// OsDiagnosticsRunBluetoothScanningRoutineFunction ----------------------------
void OsDiagnosticsRunBluetoothScanningRoutineFunction::RunIfAllowed() {
// Scanning Routine is guarded by `os.bluetooth_peripherals_info` permission.
if (!extension()->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::
kChromeOSBluetoothPeripheralsInfo)) {
Respond(
Error("Unauthorized access to "
"chrome.os.diagnostics.runBluetoothScanningRoutine. Extension"
" doesn't have the permission."));
return;
}
const auto params = GetParams<cx_diag::RunBluetoothScanningRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunBluetoothScanningRoutine(
params->request.length_seconds, GetOnResult());
}
// OsDiagnosticsRunCpuCacheRoutineFunction -------------------------------------
void OsDiagnosticsRunCpuCacheRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunCpuCacheRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunCpuCacheRoutine(params->request.length_seconds,
GetOnResult());
}
// OsDiagnosticsRunCpuFloatingPointAccuracyRoutineFunction ---------------------
void OsDiagnosticsRunCpuFloatingPointAccuracyRoutineFunction::RunIfAllowed() {
const auto params =
GetParams<cx_diag::RunCpuFloatingPointAccuracyRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunFloatingPointAccuracyRoutine(
params->request.length_seconds, GetOnResult());
}
// OsDiagnosticsRunCpuPrimeSearchRoutineFunction -------------------------------
void OsDiagnosticsRunCpuPrimeSearchRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunCpuPrimeSearchRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunPrimeSearchRoutine(params->request.length_seconds,
GetOnResult());
}
// OsDiagnosticsRunCpuStressRoutineFunction ------------------------------------
void OsDiagnosticsRunCpuStressRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunCpuStressRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunCpuStressRoutine(params->request.length_seconds,
GetOnResult());
}
// OsDiagnosticsRunDiskReadRoutineFunction -------------------------------------
void OsDiagnosticsRunDiskReadRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunDiskReadRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunDiskReadRoutine(
converters::diagnostics::ConvertDiskReadRoutineType(params->request.type),
params->request.length_seconds, params->request.file_size_mb,
GetOnResult());
}
// OsDiagnosticsRunDnsResolutionRoutineFunction --------------------------------
void OsDiagnosticsRunDnsResolutionRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunDnsResolutionRoutine(GetOnResult());
}
// OsDiagnosticsRunDnsResolverPresentRoutineFunction ---------------------------
void OsDiagnosticsRunDnsResolverPresentRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunDnsResolverPresentRoutine(GetOnResult());
}
// OsDiagnosticsRunEmmcLifetimeRoutineFunction ---------------------------
void OsDiagnosticsRunEmmcLifetimeRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunEmmcLifetimeRoutine(GetOnResult());
}
// OsDiagnosticsRunGatewayCanBePingedRoutineFunction ---------------------------
void OsDiagnosticsRunGatewayCanBePingedRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunGatewayCanBePingedRoutine(GetOnResult());
}
// OsDiagnosticsRunFingerprintAliveRoutineFunction -----------------------------
void OsDiagnosticsRunFingerprintAliveRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunFingerprintAliveRoutine(GetOnResult());
}
// OsDiagnosticsRunLanConnectivityRoutineFunction ------------------------------
void OsDiagnosticsRunLanConnectivityRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunLanConnectivityRoutine(GetOnResult());
}
// OsDiagnosticsRunMemoryRoutineFunction ---------------------------------------
void OsDiagnosticsRunMemoryRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunMemoryRoutine(GetOnResult());
}
// OsDiagnosticsRunNvmeSelfTestRoutineFunction ---------------------------------
void OsDiagnosticsRunNvmeSelfTestRoutineFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::RunNvmeSelfTestRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunNvmeSelfTestRoutine(
converters::diagnostics::ConvertNvmeSelfTestRoutineType(
std::move(params->request)),
GetOnResult());
}
// OsDiagnosticsRunSensitiveSensorRoutineFunction -----------------------------
void OsDiagnosticsRunSensitiveSensorRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunSensitiveSensorRoutine(GetOnResult());
}
// OsDiagnosticsRunSignalStrengthRoutineFunction -------------------------------
void OsDiagnosticsRunSignalStrengthRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunSignalStrengthRoutine(GetOnResult());
}
// OsDiagnosticsRunSmartctlCheckRoutineFunction --------------------------------
void OsDiagnosticsRunSmartctlCheckRoutineFunction::RunIfAllowed() {
std::optional<cx_diag::RunSmartctlCheckRoutine::Params> params(
cx_diag::RunSmartctlCheckRoutine::Params::Create(args()));
crosapi::mojom::UInt32ValuePtr percentage_used;
if (params && params->request && params->request->percentage_used_threshold) {
percentage_used = crosapi::mojom::UInt32Value::New(
params->request->percentage_used_threshold.value());
}
// Backwards compatibility: Calling the routine with an null parameter
// results in the same behaviour as the former `RunSmartctlCheckRoutine`
// without any parameters.
GetRemoteService()->RunSmartctlCheckRoutine(std::move(percentage_used),
GetOnResult());
}
// OsDiagnosticsRunUfsLifetimeRoutineFunction -------------------------------
void OsDiagnosticsRunUfsLifetimeRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunUfsLifetimeRoutine(GetOnResult());
}
// OsDiagnosticsRunPowerButtonRoutineFunction -----------------------------
void OsDiagnosticsRunPowerButtonRoutineFunction::RunIfAllowed() {
const auto params = GetParams<cx_diag::RunPowerButtonRoutine::Params>();
if (!params) {
return;
}
GetRemoteService()->RunPowerButtonRoutine(params->request.timeout_seconds,
GetOnResult());
}
// OsDiagnosticsRunAudioDriverRoutineFunction -------------------------------
void OsDiagnosticsRunAudioDriverRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunAudioDriverRoutine(GetOnResult());
}
// OsDiagnosticsRunFanRoutineFunction -------------------------------
void OsDiagnosticsRunFanRoutineFunction::RunIfAllowed() {
GetRemoteService()->RunFanRoutine(GetOnResult());
}
// OsDiagnosticsCreateRoutineFunction ------------------------------------
void OsDiagnosticsCreateRoutineFunction::RunIfAllowed() {
std::optional<cx_diag::CreateRoutine::Params> params(
cx_diag::CreateRoutine::Params::Create(args()));
if (!params.has_value()) {
Respond(BadMessage());
return;
}
std::optional<crosapi::mojom::TelemetryDiagnosticRoutineArgumentPtr>
mojo_arg = converters::diagnostics::ConvertRoutineArgumentsUnion(
std::move(params->args));
if (!mojo_arg.has_value()) {
RespondWithError("Routine arguments are invalid.");
return;
}
// Block unreleased features behind the feature flag.
if (IsPendingApprovalRoutine(mojo_arg.value()) &&
!base::FeatureList::IsEnabled(
extensions_features::kTelemetryExtensionPendingApprovalApi)) {
mojo_arg = crosapi::mojom::TelemetryDiagnosticRoutineArgument::
NewUnrecognizedArgument(false);
}
RecordRoutineCreation(mojo_arg.value()->which());
// Network bandwidth routine is guarded by `os.diagnostics.network_info_mlab`
// permission.
if (mojo_arg.value()->is_network_bandwidth() &&
!extension()->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::
kChromeOSDiagnosticsNetworkInfoForMlab)) {
RespondWithError(
"Unauthorized access to chrome.os.diagnostics.CreateRoutine with "
"networkBandwidth argument. Extension doesn't have the permission.");
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto result = routines_manager->CreateRoutine(extension_id(),
std::move(mojo_arg.value()));
if (!result.has_value()) {
switch (result.error()) {
case DiagnosticRoutineManager::kAppUiClosed:
Respond(Error("Companion app UI is not open."));
break;
case DiagnosticRoutineManager::kExtensionUnloaded:
Respond(Error("Extension has been unloaded."));
break;
}
return;
}
cx_diag::CreateRoutineResponse response;
response.uuid = result->AsLowercaseString();
Respond(ArgumentList(cx_diag::CreateRoutine::Results::Create(response)));
}
// OsDiagnosticsCreateMemoryRoutineFunction ------------------------------------
void OsDiagnosticsCreateMemoryRoutineFunction::RunIfAllowed() {
std::optional<cx_diag::CreateMemoryRoutine::Params> params(
cx_diag::CreateMemoryRoutine::Params::Create(args()));
if (!params.has_value() ||
(params.value().args.max_testing_mem_kib.has_value() &&
params.value().args.max_testing_mem_kib < 0)) {
SetBadMessage();
Respond(BadMessage());
return;
}
auto memory_arg =
crosapi::mojom::TelemetryDiagnosticMemoryRoutineArgument::New();
if (params.value().args.max_testing_mem_kib.has_value()) {
memory_arg->max_testing_mem_kib = params.value().args.max_testing_mem_kib;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto result = routines_manager->CreateRoutine(
extension_id(),
crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewMemory(
std::move(memory_arg)));
if (!result.has_value()) {
switch (result.error()) {
case DiagnosticRoutineManager::kAppUiClosed:
Respond(Error("Companion app UI is not open."));
break;
case DiagnosticRoutineManager::kExtensionUnloaded:
Respond(Error("Extension has been unloaded."));
break;
}
return;
}
cx_diag::CreateRoutineResponse response;
response.uuid = result->AsLowercaseString();
Respond(
ArgumentList(cx_diag::CreateMemoryRoutine::Results::Create(response)));
}
// OsDiagnosticsCreateVolumeButtonRoutineFunction
// ------------------------------------
void OsDiagnosticsCreateVolumeButtonRoutineFunction::RunIfAllowed() {
std::optional<cx_diag::CreateVolumeButtonRoutine::Params> params(
cx_diag::CreateVolumeButtonRoutine::Params::Create(args()));
if (!params.has_value() || params.value().args.timeout_seconds <= 0 ||
params.value().args.button_type == cx_diag::VolumeButtonType::kNone) {
SetBadMessage();
Respond(BadMessage());
return;
}
auto volume_button_arg =
crosapi::mojom::TelemetryDiagnosticVolumeButtonRoutineArgument::New();
volume_button_arg->type =
converters::diagnostics::ConvertVolumeButtonRoutineButtonType(
params.value().args.button_type);
volume_button_arg->timeout =
base::Seconds(params.value().args.timeout_seconds);
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto result = routines_manager->CreateRoutine(
extension_id(),
crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewVolumeButton(
std::move(volume_button_arg)));
if (!result.has_value()) {
switch (result.error()) {
case DiagnosticRoutineManager::kAppUiClosed:
Respond(Error("Companion app UI is not open."));
break;
case DiagnosticRoutineManager::kExtensionUnloaded:
Respond(Error("Extension has been unloaded."));
break;
}
return;
}
cx_diag::CreateRoutineResponse response;
response.uuid = result->AsLowercaseString();
Respond(ArgumentList(
cx_diag::CreateVolumeButtonRoutine::Results::Create(response)));
}
// OsDiagnosticsCreateFanRoutineFunction ------------------------------------
void OsDiagnosticsCreateFanRoutineFunction::RunIfAllowed() {
std::optional<cx_diag::CreateFanRoutine::Params> params(
cx_diag::CreateFanRoutine::Params::Create(args()));
if (!params.has_value()) {
SetBadMessage();
Respond(BadMessage());
return;
}
auto fan_arg = crosapi::mojom::TelemetryDiagnosticFanRoutineArgument::New();
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto result = routines_manager->CreateRoutine(
extension_id(),
crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewFan(
std::move(fan_arg)));
if (!result.has_value()) {
switch (result.error()) {
case DiagnosticRoutineManager::kAppUiClosed:
Respond(Error("Companion app UI is not open."));
break;
case DiagnosticRoutineManager::kExtensionUnloaded:
Respond(Error("Extension has been unloaded."));
break;
}
return;
}
cx_diag::CreateRoutineResponse response;
response.uuid = result->AsLowercaseString();
Respond(ArgumentList(cx_diag::CreateFanRoutine::Results::Create(response)));
}
// OsDiagnosticsStartRoutineFunction -------------------------------------------
void OsDiagnosticsStartRoutineFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::StartRoutine::Params>();
if (!params.has_value()) {
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
bool result = routines_manager->StartRoutineForExtension(
extension_id(), base::Uuid::ParseLowercase(params.value().request.uuid));
if (!result) {
RespondWithError("Unknown routine id.");
return;
}
Respond(NoArguments());
}
// OsDiagnosticsCancelRoutineFunction ------------------------------------------
void OsDiagnosticsCancelRoutineFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::CancelRoutine::Params>();
if (!params.has_value()) {
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
routines_manager->CancelRoutineForExtension(
extension_id(), base::Uuid::ParseLowercase(params.value().request.uuid));
Respond(NoArguments());
}
// OsDiagnosticsReplyToRoutineInquiryFunction ----------------------------------
void OsDiagnosticsReplyToRoutineInquiryFunction::RunIfAllowed() {
auto params = cx_diag::ReplyToRoutineInquiry::Params::Create(args());
if (!params.has_value()) {
Respond(BadMessage());
return;
}
std::optional<crosapi::mojom::TelemetryDiagnosticRoutineInquiryReplyPtr>
mojo_reply = converters::diagnostics::ConvertRoutineInquiryReplyUnion(
std::move(params->request.reply));
if (!mojo_reply.has_value()) {
RespondWithError("Inquiry reply is invalid.");
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
bool result = routines_manager->ReplyToRoutineInquiryForExtension(
extension_id(), base::Uuid::ParseLowercase(params.value().request.uuid),
std::move(mojo_reply.value()));
if (!result) {
RespondWithError("Unknown routine id.");
return;
}
Respond(NoArguments());
}
// OsDiagnosticsIsRoutineArgumentSupportedFunction -----------------------
void OsDiagnosticsIsRoutineArgumentSupportedFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::IsRoutineArgumentSupported::Params>();
if (!params.has_value()) {
return;
}
std::optional<crosapi::mojom::TelemetryDiagnosticRoutineArgumentPtr>
mojo_arg = converters::diagnostics::ConvertRoutineArgumentsUnion(
std::move(params->args));
if (!mojo_arg.has_value()) {
RespondWithError("Routine arguments are invalid.");
return;
}
// Block unreleased features behind the feature flag.
if (IsPendingApprovalRoutine(mojo_arg.value()) &&
!base::FeatureList::IsEnabled(
extensions_features::kTelemetryExtensionPendingApprovalApi)) {
mojo_arg = crosapi::mojom::TelemetryDiagnosticRoutineArgument::
NewUnrecognizedArgument(false);
}
RecordRoutineSupportedStatusQuery(mojo_arg.value()->which());
// Network bandwidth routine is guarded by `os.diagnostics.network_info_mlab`
// permission.
if (mojo_arg.value()->is_network_bandwidth() &&
!extension()->permissions_data()->HasAPIPermission(
extensions::mojom::APIPermissionID::
kChromeOSDiagnosticsNetworkInfoForMlab)) {
RespondWithError(
"Unauthorized access to "
"chrome.os.diagnostics.isRoutineArgumentSupported with "
"networkBandwidth argument. Extension doesn't have the permission.");
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
routines_manager->IsRoutineArgumentSupported(
std::move(mojo_arg.value()),
base::BindOnce(&OsDiagnosticsIsRoutineArgumentSupportedFunction::OnResult,
this));
}
void OsDiagnosticsIsRoutineArgumentSupportedFunction::OnResult(
crosapi::mojom::TelemetryExtensionSupportStatusPtr result) {
if (result.is_null()) {
RespondWithError("API internal error.");
return;
}
auto response = ParseRoutineArgumentSupportResult(std::move(result));
if (!response.has_value()) {
RespondWithError(response.error());
return;
}
Respond(ArgumentList(
cx_diag::IsRoutineArgumentSupported::Results::Create(response.value())));
}
// OsDiagnosticsIsMemoryRoutineArgumentSupportedFunction -----------------------
void OsDiagnosticsIsMemoryRoutineArgumentSupportedFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::IsMemoryRoutineArgumentSupported::Params>();
if (!params.has_value()) {
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto mem_args =
crosapi::mojom::TelemetryDiagnosticMemoryRoutineArgument::New();
mem_args->max_testing_mem_kib = params.value().args.max_testing_mem_kib;
auto args = crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewMemory(
std::move(mem_args));
routines_manager->IsRoutineArgumentSupported(
std::move(args),
base::BindOnce(
&OsDiagnosticsIsMemoryRoutineArgumentSupportedFunction::OnResult,
this));
}
void OsDiagnosticsIsMemoryRoutineArgumentSupportedFunction::OnResult(
crosapi::mojom::TelemetryExtensionSupportStatusPtr result) {
if (result.is_null()) {
RespondWithError("API internal error.");
return;
}
auto response = ParseRoutineArgumentSupportResult(std::move(result));
if (!response.has_value()) {
RespondWithError(response.error());
return;
}
Respond(
ArgumentList(cx_diag::IsMemoryRoutineArgumentSupported::Results::Create(
response.value())));
}
// OsDiagnosticsIsVolumeButtonRoutineArgumentSupportedFunction
// -----------------------
void OsDiagnosticsIsVolumeButtonRoutineArgumentSupportedFunction::
RunIfAllowed() {
auto params =
GetParams<cx_diag::IsVolumeButtonRoutineArgumentSupported::Params>();
if (!params.has_value() || params.value().args.timeout_seconds <= 0 ||
params.value().args.button_type == cx_diag::VolumeButtonType::kNone) {
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto volume_button_args =
crosapi::mojom::TelemetryDiagnosticVolumeButtonRoutineArgument::New();
volume_button_args->type =
converters::diagnostics::ConvertVolumeButtonRoutineButtonType(
params.value().args.button_type);
volume_button_args->timeout =
base::Seconds(params.value().args.timeout_seconds);
auto args =
crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewVolumeButton(
std::move(volume_button_args));
routines_manager->IsRoutineArgumentSupported(
std::move(args),
base::BindOnce(
&OsDiagnosticsIsVolumeButtonRoutineArgumentSupportedFunction::
OnResult,
this));
}
void OsDiagnosticsIsVolumeButtonRoutineArgumentSupportedFunction::OnResult(
crosapi::mojom::TelemetryExtensionSupportStatusPtr result) {
if (result.is_null()) {
RespondWithError("API internal error.");
return;
}
auto response = ParseRoutineArgumentSupportResult(std::move(result));
if (!response.has_value()) {
RespondWithError(response.error());
return;
}
Respond(ArgumentList(
cx_diag::IsVolumeButtonRoutineArgumentSupported::Results::Create(
response.value())));
}
// OsDiagnosticsIsFanRoutineArgumentSupportedFunction -----------------------
void OsDiagnosticsIsFanRoutineArgumentSupportedFunction::RunIfAllowed() {
auto params = GetParams<cx_diag::IsFanRoutineArgumentSupported::Params>();
if (!params.has_value()) {
return;
}
auto* routines_manager = DiagnosticRoutineManager::Get(browser_context());
auto fan_args = crosapi::mojom::TelemetryDiagnosticFanRoutineArgument::New();
auto args = crosapi::mojom::TelemetryDiagnosticRoutineArgument::NewFan(
std::move(fan_args));
routines_manager->IsRoutineArgumentSupported(
std::move(args),
base::BindOnce(
&OsDiagnosticsIsFanRoutineArgumentSupportedFunction::OnResult, this));
}
void OsDiagnosticsIsFanRoutineArgumentSupportedFunction::OnResult(
crosapi::mojom::TelemetryExtensionSupportStatusPtr result) {
if (result.is_null()) {
RespondWithError("API internal error.");
return;
}
auto response = ParseRoutineArgumentSupportResult(std::move(result));
if (!response.has_value()) {
RespondWithError(response.error());
return;
}
Respond(ArgumentList(cx_diag::IsFanRoutineArgumentSupported::Results::Create(
response.value())));
}
} // namespace chromeos