blob: 6af5197bcd0d02ae943ad3e1d2c3df52e896bd33 [file] [log] [blame]
// Copyright 2022 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/ui/webui/chromeos/human_presence_internals_ui.h"
#include <memory>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/files/file_util.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "base/task/thread_pool.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chromeos/ash/components/human_presence/human_presence_configuration.h"
#include "chromeos/ash/components/human_presence/human_presence_internals.h"
#include "chromeos/ash/grit/ash_resources.h"
#include "chromeos/dbus/hps/hps_service.pb.h"
#include "chromeos/dbus/human_presence/human_presence_dbus_client.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_controller.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
namespace {
// Class acting as a controller of the chrome://hps-internals WebUI.
class HumanPresenceInternalsUIMessageHandler
: public content::WebUIMessageHandler,
public chromeos::HumanPresenceDBusClient::Observer {
public:
HumanPresenceInternalsUIMessageHandler();
HumanPresenceInternalsUIMessageHandler(
const HumanPresenceInternalsUIMessageHandler&) = delete;
HumanPresenceInternalsUIMessageHandler& operator=(
const HumanPresenceInternalsUIMessageHandler&) = delete;
~HumanPresenceInternalsUIMessageHandler() override;
// WebUIMessageHandler implementation.
void RegisterMessages() override;
void OnJavascriptAllowed() override;
void OnJavascriptDisallowed() override;
// chromeos::HumanPresenceDBusClient::Observer implementation.
void OnHpsSenseChanged(const hps::HpsResultProto&) override;
void OnHpsNotifyChanged(const hps::HpsResultProto&) override;
void OnRestart() override;
void OnShutdown() override;
private:
void Connect(const base::Value::List& args);
void EnableLockOnLeave(const base::Value::List& args);
void DisableLockOnLeave(const base::Value::List& args);
void QueryLockOnLeave(const base::Value::List& args);
void EnableSnoopingProtection(const base::Value::List& args);
void DisableSnoopingProtection(const base::Value::List& args);
void QuerySnoopingProtection(const base::Value::List& args);
void OnConnected(bool connected);
void OnLockOnLeaveResult(absl::optional<hps::HpsResultProto>);
void OnSnoopingProtectionResult(absl::optional<hps::HpsResultProto>);
static absl::optional<std::string> ReadManifest();
void UpdateManifest(absl::optional<std::string> manifest);
base::ScopedObservation<chromeos::HumanPresenceDBusClient,
chromeos::HumanPresenceDBusClient::Observer>
human_presence_observation_{this};
base::WeakPtrFactory<HumanPresenceInternalsUIMessageHandler>
msg_weak_ptr_factory_{this};
base::WeakPtrFactory<HumanPresenceInternalsUIMessageHandler>
weak_ptr_factory_{this};
};
HumanPresenceInternalsUIMessageHandler::
HumanPresenceInternalsUIMessageHandler() = default;
HumanPresenceInternalsUIMessageHandler::
~HumanPresenceInternalsUIMessageHandler() = default;
void HumanPresenceInternalsUIMessageHandler::OnHpsSenseChanged(
const hps::HpsResultProto& state) {
OnLockOnLeaveResult(state);
}
void HumanPresenceInternalsUIMessageHandler::OnHpsNotifyChanged(
const hps::HpsResultProto& state) {
OnSnoopingProtectionResult(state);
}
void HumanPresenceInternalsUIMessageHandler::OnLockOnLeaveResult(
absl::optional<hps::HpsResultProto> state) {
base::DictionaryValue value;
if (state.has_value()) {
value.SetInteger("state", state->value());
value.SetInteger("inference_result", state->inference_result());
value.SetInteger("inference_result_valid", state->inference_result_valid());
} else {
value.SetBoolean("disabled", true);
}
FireWebUIListener(hps::kHumanPresenceInternalsLockOnLeaveChangedEvent, value);
}
void HumanPresenceInternalsUIMessageHandler::OnSnoopingProtectionResult(
absl::optional<hps::HpsResultProto> state) {
base::DictionaryValue value;
if (state.has_value()) {
value.SetInteger("state", state->value());
value.SetInteger("inference_result", state->inference_result());
value.SetInteger("inference_result_valid", state->inference_result_valid());
} else {
value.SetBoolean("disabled", true);
}
FireWebUIListener(hps::kHumanPresenceInternalsSnoopingProtectionChangedEvent,
value);
}
void HumanPresenceInternalsUIMessageHandler::OnRestart() {
OnConnected(true);
}
void HumanPresenceInternalsUIMessageHandler::OnShutdown() {
OnConnected(false);
}
void HumanPresenceInternalsUIMessageHandler::Connect(
const base::Value::List& args) {
if (!chromeos::HumanPresenceDBusClient::Get()) {
LOG(ERROR) << "HPS dbus client not available";
return;
}
AllowJavascript();
chromeos::HumanPresenceDBusClient::Get()->WaitForServiceToBeAvailable(
base::BindOnce(&HumanPresenceInternalsUIMessageHandler::OnConnected,
weak_ptr_factory_.GetWeakPtr()));
}
void HumanPresenceInternalsUIMessageHandler::OnConnected(bool connected) {
base::DictionaryValue value;
value.SetBoolean("connected", connected);
FireWebUIListener(hps::kHumanPresenceInternalsConnectedEvent, value);
if (connected) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&HumanPresenceInternalsUIMessageHandler::ReadManifest),
base::BindOnce(&HumanPresenceInternalsUIMessageHandler::UpdateManifest,
weak_ptr_factory_.GetWeakPtr()));
}
}
// static
absl::optional<std::string>
HumanPresenceInternalsUIMessageHandler::ReadManifest() {
std::string manifest;
const base::FilePath::CharType kManifestPath[] =
FILE_PATH_LITERAL("/usr/lib/firmware/hps/manifest.txt");
if (!base::ReadFileToString(base::FilePath(kManifestPath), &manifest))
return absl::nullopt;
return manifest;
}
void HumanPresenceInternalsUIMessageHandler::UpdateManifest(
absl::optional<std::string> manifest) {
if (!manifest.has_value()) {
FireWebUIListener(hps::kHumanPresenceInternalsManifestEvent,
base::Value("(Failed to read manifest)"));
return;
}
FireWebUIListener(hps::kHumanPresenceInternalsManifestEvent,
base::Value(*manifest));
}
void HumanPresenceInternalsUIMessageHandler::EnableLockOnLeave(
const base::Value::List& args) {
if (!chromeos::HumanPresenceDBusClient::Get() ||
!hps::GetEnableLockOnLeaveConfig().has_value()) {
FireWebUIListener(hps::kHumanPresenceInternalsEnableErrorEvent);
return;
}
hps::FeatureConfig config(*hps::GetEnableLockOnLeaveConfig());
config.set_report_raw_results(true);
chromeos::HumanPresenceDBusClient::Get()->EnableHpsSense(config);
}
void HumanPresenceInternalsUIMessageHandler::DisableLockOnLeave(
const base::Value::List& args) {
if (chromeos::HumanPresenceDBusClient::Get())
chromeos::HumanPresenceDBusClient::Get()->DisableHpsSense();
}
void HumanPresenceInternalsUIMessageHandler::QueryLockOnLeave(
const base::Value::List& args) {
if (!chromeos::HumanPresenceDBusClient::Get())
return;
chromeos::HumanPresenceDBusClient::Get()->GetResultHpsSense(base::BindOnce(
&HumanPresenceInternalsUIMessageHandler::OnLockOnLeaveResult,
weak_ptr_factory_.GetWeakPtr()));
}
void HumanPresenceInternalsUIMessageHandler::EnableSnoopingProtection(
const base::Value::List& args) {
if (!chromeos::HumanPresenceDBusClient::Get() ||
!hps::GetEnableSnoopingProtectionConfig().has_value()) {
FireWebUIListener(hps::kHumanPresenceInternalsEnableErrorEvent);
return;
}
hps::FeatureConfig config(*hps::GetEnableSnoopingProtectionConfig());
config.set_report_raw_results(true);
chromeos::HumanPresenceDBusClient::Get()->EnableHpsNotify(config);
}
void HumanPresenceInternalsUIMessageHandler::DisableSnoopingProtection(
const base::Value::List& args) {
if (chromeos::HumanPresenceDBusClient::Get())
chromeos::HumanPresenceDBusClient::Get()->DisableHpsNotify();
}
void HumanPresenceInternalsUIMessageHandler::QuerySnoopingProtection(
const base::Value::List& args) {
if (!chromeos::HumanPresenceDBusClient::Get())
return;
chromeos::HumanPresenceDBusClient::Get()->GetResultHpsNotify(base::BindOnce(
&HumanPresenceInternalsUIMessageHandler::OnSnoopingProtectionResult,
weak_ptr_factory_.GetWeakPtr()));
}
void HumanPresenceInternalsUIMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsConnectCmd,
base::BindRepeating(&HumanPresenceInternalsUIMessageHandler::Connect,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsEnableLockOnLeaveCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::EnableLockOnLeave,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsDisableLockOnLeaveCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::DisableLockOnLeave,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsQueryLockOnLeaveCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::QueryLockOnLeave,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsEnableSnoopingProtectionCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::EnableSnoopingProtection,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsDisableSnoopingProtectionCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::DisableSnoopingProtection,
msg_weak_ptr_factory_.GetWeakPtr()));
web_ui()->RegisterMessageCallback(
hps::kHumanPresenceInternalsQuerySnoopingProtectionCmd,
base::BindRepeating(
&HumanPresenceInternalsUIMessageHandler::QuerySnoopingProtection,
msg_weak_ptr_factory_.GetWeakPtr()));
}
void HumanPresenceInternalsUIMessageHandler::OnJavascriptAllowed() {
if (chromeos::HumanPresenceDBusClient::Get())
human_presence_observation_.Observe(
chromeos::HumanPresenceDBusClient::Get());
}
void HumanPresenceInternalsUIMessageHandler::OnJavascriptDisallowed() {
// Invalidate weak ptrs in order to cancel any pending callbacks.
weak_ptr_factory_.InvalidateWeakPtrs();
human_presence_observation_.Reset();
}
} // namespace
namespace chromeos {
HumanPresenceInternalsUI::HumanPresenceInternalsUI(content::WebUI* web_ui)
: content::WebUIController(web_ui) {
// Set up the chrome://gcm-internals source.
content::WebUIDataSource* html_source = content::WebUIDataSource::Create(
chrome::kChromeUIHumanPresenceInternalsHost);
html_source->UseStringsJs();
// Add required resources.
html_source->AddResourcePath(hps::kHumanPresenceInternalsCSS,
IDR_HUMAN_PRESENCE_INTERNALS_CSS);
html_source->AddResourcePath(hps::kHumanPresenceInternalsJS,
IDR_HUMAN_PRESENCE_INTERNALS_JS);
html_source->AddResourcePath(hps::kHumanPresenceInternalsIcon,
IDR_HUMAN_PRESENCE_INTERNALS_ICON);
html_source->SetDefaultResource(IDR_HUMAN_PRESENCE_INTERNALS_HTML);
Profile* profile = Profile::FromWebUI(web_ui);
content::WebUIDataSource::Add(profile, html_source);
web_ui->AddMessageHandler(
std::make_unique<HumanPresenceInternalsUIMessageHandler>());
}
HumanPresenceInternalsUI::~HumanPresenceInternalsUI() = default;
} // namespace chromeos