Commit 10969d85 authored by Paul Moy's avatar Paul Moy Committed by Commit Bot

remote_commands: Add DeviceCommandGetAvailableRoutinesJob

Add a new device command to get a list of available
diagnostics routines from the device.

--gtest_filter=DeviceCommandGetAvailableRoutinesJobTest.*

Bug: chromium:1049762
Test: unit_tests
Change-Id: Ia2299e21f84db5f8504538eee549296ae3e1fb79
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2042141Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Commit-Queue: Paul Moy <pmoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#741483}
parent 27bd0555
......@@ -1877,6 +1877,8 @@ source_set("chromeos") {
"policy/remote_commands/crd_host_delegate.h",
"policy/remote_commands/device_command_fetch_status_job.cc",
"policy/remote_commands/device_command_fetch_status_job.h",
"policy/remote_commands/device_command_get_available_routines_job.cc",
"policy/remote_commands/device_command_get_available_routines_job.h",
"policy/remote_commands/device_command_reboot_job.cc",
"policy/remote_commands/device_command_reboot_job.h",
"policy/remote_commands/device_command_refresh_machine_certificate_job.cc",
......@@ -2940,6 +2942,7 @@ source_set("unit_tests") {
"policy/off_hours/off_hours_policy_applier_unittest.cc",
"policy/off_hours/off_hours_proto_parser_unittest.cc",
"policy/pre_signin_policy_fetcher_unittest.cc",
"policy/remote_commands/device_command_get_available_routines_job_unittest.cc",
"policy/remote_commands/device_command_remote_powerwash_job_unittest.cc",
"policy/remote_commands/device_command_screenshot_job_unittest.cc",
"policy/remote_commands/device_command_set_volume_job_unittest.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/remote_commands/device_command_get_available_routines_job.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/json/json_writer.h"
#include "base/syslog_logging.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chromeos/services/cros_healthd/public/cpp/service_connection.h"
#include "components/policy/proto/device_management_backend.pb.h"
namespace policy {
namespace em = enterprise_management;
namespace {
// String constant identifying the routines field in the result payload.
constexpr char kRoutinesFieldName[] = "routines";
} // namespace
class DeviceCommandGetAvailableRoutinesJob::Payload
: public RemoteCommandJob::ResultPayload {
public:
explicit Payload(
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines);
Payload(const Payload&) = delete;
Payload& operator=(const Payload&) = delete;
~Payload() override = default;
// RemoteCommandJob::ResultPayload:
std::unique_ptr<std::string> Serialize() override;
private:
std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>
available_routines_;
};
DeviceCommandGetAvailableRoutinesJob::Payload::Payload(
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines)
: available_routines_(available_routines) {}
std::unique_ptr<std::string>
DeviceCommandGetAvailableRoutinesJob::Payload::Serialize() {
std::string payload;
base::Value root_dict(base::Value::Type::DICTIONARY);
base::Value routine_list(base::Value::Type::LIST);
for (const auto& routine : available_routines_)
routine_list.Append(static_cast<int>(routine));
root_dict.SetPath(kRoutinesFieldName, std::move(routine_list));
base::JSONWriter::Write(root_dict, &payload);
return std::make_unique<std::string>(std::move(payload));
}
DeviceCommandGetAvailableRoutinesJob::DeviceCommandGetAvailableRoutinesJob() =
default;
DeviceCommandGetAvailableRoutinesJob::~DeviceCommandGetAvailableRoutinesJob() =
default;
em::RemoteCommand_Type DeviceCommandGetAvailableRoutinesJob::GetType() const {
return em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES;
}
void DeviceCommandGetAvailableRoutinesJob::RunImpl(
CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback) {
SYSLOG(INFO) << "Executing GetAvailableRoutines command.";
chromeos::cros_healthd::ServiceConnection::GetInstance()
->GetAvailableRoutines(base::BindOnce(
&DeviceCommandGetAvailableRoutinesJob::OnCrosHealthdResponseReceived,
weak_ptr_factory_.GetWeakPtr(), std::move(succeeded_callback),
std::move(failed_callback)));
}
void DeviceCommandGetAvailableRoutinesJob::OnCrosHealthdResponseReceived(
CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback,
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines) {
if (available_routines.empty()) {
SYSLOG(ERROR) << "No routines received from cros_healthd.";
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(failed_callback), nullptr));
return;
}
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(succeeded_callback),
std::make_unique<Payload>(available_routines)));
}
} // namespace policy
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_GET_AVAILABLE_ROUTINES_JOB_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_GET_AVAILABLE_ROUTINES_JOB_H_
#include <vector>
#include "base/memory/weak_ptr.h"
#include "chromeos/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom.h"
#include "components/policy/core/common/remote_commands/remote_command_job.h"
namespace policy {
// This class implements a RemoteCommandJob that retrieves a list of diagnostic
// routines supported by the platform. The RemoteCommandsQueue owns all
// instances of this class.
class DeviceCommandGetAvailableRoutinesJob : public RemoteCommandJob {
public:
DeviceCommandGetAvailableRoutinesJob();
DeviceCommandGetAvailableRoutinesJob(
const DeviceCommandGetAvailableRoutinesJob&) = delete;
DeviceCommandGetAvailableRoutinesJob& operator=(
const DeviceCommandGetAvailableRoutinesJob&) = delete;
~DeviceCommandGetAvailableRoutinesJob() override;
// RemoteCommandJob:
enterprise_management::RemoteCommand_Type GetType() const override;
private:
class Payload;
// RemoteCommandJob:
void RunImpl(CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback) override;
void OnCrosHealthdResponseReceived(
CallbackWithResult succeeded_callback,
CallbackWithResult failed_callback,
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines);
base::WeakPtrFactory<DeviceCommandGetAvailableRoutinesJob> weak_ptr_factory_{
this};
};
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_GET_AVAILABLE_ROUTINES_JOB_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/policy/remote_commands/device_command_get_available_routines_job.h"
#include <memory>
#include <vector>
#include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chromeos/dbus/cros_healthd/cros_healthd_client.h"
#include "chromeos/dbus/cros_healthd/fake_cros_healthd_client.h"
#include "chromeos/services/cros_healthd/public/mojom/cros_healthd_diagnostics.mojom.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
namespace em = enterprise_management;
namespace {
// String constant identifying the routines field in the result payload.
const char* const kRoutinesFieldName = "routines";
constexpr RemoteCommandJob::UniqueIDType kUniqueID = 987123;
em::RemoteCommand GenerateCommandProto(RemoteCommandJob::UniqueIDType unique_id,
base::TimeDelta age_of_command,
base::TimeDelta idleness_cutoff,
bool terminate_upon_input) {
em::RemoteCommand command_proto;
command_proto.set_type(
em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES);
command_proto.set_command_id(unique_id);
command_proto.set_age_of_command(age_of_command.InMilliseconds());
return command_proto;
}
} // namespace
class DeviceCommandGetAvailableRoutinesJobTest : public testing::Test {
protected:
DeviceCommandGetAvailableRoutinesJobTest();
DeviceCommandGetAvailableRoutinesJobTest(
const DeviceCommandGetAvailableRoutinesJobTest&) = delete;
DeviceCommandGetAvailableRoutinesJobTest& operator=(
const DeviceCommandGetAvailableRoutinesJobTest&) = delete;
~DeviceCommandGetAvailableRoutinesJobTest() override;
void InitializeJob(RemoteCommandJob* job,
RemoteCommandJob::UniqueIDType unique_id,
base::TimeTicks issued_time,
base::TimeDelta idleness_cutoff,
bool terminate_upon_input);
std::string CreateSuccessPayload(
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines);
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
base::TimeTicks test_start_time_;
};
DeviceCommandGetAvailableRoutinesJobTest::
DeviceCommandGetAvailableRoutinesJobTest() {
chromeos::CrosHealthdClient::InitializeFake();
test_start_time_ = base::TimeTicks::Now();
}
DeviceCommandGetAvailableRoutinesJobTest::
~DeviceCommandGetAvailableRoutinesJobTest() {
chromeos::CrosHealthdClient::Shutdown();
// Wait for DeviceCommandGetAvailableRoutinesJobTest to observe the
// destruction of the client.
base::RunLoop().RunUntilIdle();
}
void DeviceCommandGetAvailableRoutinesJobTest::InitializeJob(
RemoteCommandJob* job,
RemoteCommandJob::UniqueIDType unique_id,
base::TimeTicks issued_time,
base::TimeDelta idleness_cutoff,
bool terminate_upon_input) {
EXPECT_TRUE(job->Init(
base::TimeTicks::Now(),
GenerateCommandProto(unique_id, base::TimeTicks::Now() - issued_time,
idleness_cutoff, terminate_upon_input),
nullptr));
EXPECT_EQ(unique_id, job->unique_id());
EXPECT_EQ(RemoteCommandJob::NOT_STARTED, job->status());
}
std::string DeviceCommandGetAvailableRoutinesJobTest::CreateSuccessPayload(
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>&
available_routines) {
std::string payload;
base::Value root_dict(base::Value::Type::DICTIONARY);
base::Value routine_list(base::Value::Type::LIST);
for (const auto& routine : available_routines)
routine_list.Append(static_cast<int>(routine));
root_dict.SetPath(kRoutinesFieldName, std::move(routine_list));
base::JSONWriter::Write(root_dict, &payload);
return payload;
}
TEST_F(DeviceCommandGetAvailableRoutinesJobTest, Success) {
const std::vector<chromeos::cros_healthd::mojom::DiagnosticRoutineEnum>
kAvailableRoutines = {
chromeos::cros_healthd::mojom::DiagnosticRoutineEnum::kUrandom,
chromeos::cros_healthd::mojom::DiagnosticRoutineEnum::
kBatteryCapacity};
chromeos::cros_healthd::FakeCrosHealthdClient::Get()
->SetAvailableRoutinesForTesting(kAvailableRoutines);
std::unique_ptr<RemoteCommandJob> job =
std::make_unique<DeviceCommandGetAvailableRoutinesJob>();
InitializeJob(job.get(), kUniqueID, test_start_time_,
base::TimeDelta::FromSeconds(30),
/*terminate_upon_input=*/false);
base::RunLoop run_loop;
bool success =
job->Run(base::Time::Now(), base::TimeTicks::Now(),
base::BindLambdaForTesting([&]() {
EXPECT_EQ(job->status(), RemoteCommandJob::SUCCEEDED);
std::unique_ptr<std::string> payload = job->GetResultPayload();
EXPECT_TRUE(payload);
EXPECT_EQ(CreateSuccessPayload(kAvailableRoutines), *payload);
run_loop.Quit();
}));
EXPECT_TRUE(success);
run_loop.Run();
}
TEST_F(DeviceCommandGetAvailableRoutinesJobTest, Failure) {
chromeos::cros_healthd::FakeCrosHealthdClient::Get()
->SetAvailableRoutinesForTesting({});
std::unique_ptr<RemoteCommandJob> job =
std::make_unique<DeviceCommandGetAvailableRoutinesJob>();
InitializeJob(job.get(), kUniqueID, test_start_time_,
base::TimeDelta::FromSeconds(30),
/*terminate_upon_input=*/false);
base::RunLoop run_loop;
bool success = job->Run(base::Time::Now(), base::TimeTicks::Now(),
base::BindLambdaForTesting([&]() {
EXPECT_EQ(job->status(), RemoteCommandJob::FAILED);
std::unique_ptr<std::string> payload =
job->GetResultPayload();
EXPECT_FALSE(payload);
run_loop.Quit();
}));
EXPECT_TRUE(success);
run_loop.Run();
}
} // namespace policy
......@@ -8,6 +8,7 @@
#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
#include "chrome/browser/chromeos/policy/remote_commands/crd_host_delegate.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_fetch_status_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_get_available_routines_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_refresh_machine_certificate_job.h"
#include "chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job.h"
......@@ -54,6 +55,8 @@ DeviceCommandsFactoryChromeOS::BuildJobForType(em::RemoteCommand_Type type,
policy_manager_->GetMachineCertificateUploader());
case em::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH:
return std::make_unique<DeviceCommandRemotePowerwashJob>(service);
case em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES:
return std::make_unique<DeviceCommandGetAvailableRoutinesJob>();
default:
// Other types of commands should be sent to UserCommandsFactoryChromeOS
// instead of here.
......
......@@ -2082,6 +2082,9 @@ message RemoteCommand {
// Refresh the device machine certificate and re-upload it.
DEVICE_REFRESH_ENTERPRISE_MACHINE_CERTIFICATE = 8;
// Retrieve a list of available diagnostics routines.
DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES = 9;
}
// The command type.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment