Skip to content

Commit

Permalink
Add trigger for Borealis games HaTS.
Browse files Browse the repository at this point in the history
The trigger logic is the same as for the current feedback form. There's
a lot of repeated code but the feedback form will be removed in a follow
up CL once HaTS actually starts being rolled out. The HaTS survey will
not actually show until a finch config is created and rolled out.

BUG=b:279378344
TEST=go/cros-hats-client-setup#testing-client-setup

Change-Id: Id80ff8dcc71fcf12287d122af7da1c0a6b081fb3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4495757
Commit-Queue: Chloe Pelling <cpelling@google.com>
Reviewed-by: Chloe Pelling <cpelling@google.com>
Cr-Commit-Position: refs/heads/main@{#1150199}
  • Loading branch information
Lucy Qu authored and Chromium LUCI CQ committed May 29, 2023
1 parent 157bef5 commit 673118a
Show file tree
Hide file tree
Showing 15 changed files with 321 additions and 10 deletions.
7 changes: 6 additions & 1 deletion chrome/app/chromeos_strings.grdp
Original file line number Diff line number Diff line change
Expand Up @@ -6184,7 +6184,12 @@ Permissions you've already given to websites and apps may apply to this account.
<message name="IDS_BOREALIS_SPLASHSCREEN_MESSAGE" desc="Message on splashscreen when starting Steam on Chrome OS.">
starting up...
</message>

<message name="IDS_BOREALIS_HATS_TITLE" desc="Title for HaTS notification requesting feedback. It will appear occasionally when a user closes a Steam game. Clicking it will show a satisfaction survey to the user.">
Help us improve Steam on Chromebooks
</message>
<message name="IDS_BOREALIS_HATS_BODY" desc="Message on HaTS notification requesting feedback. It will appear occasionally when a user closes a Steam game. Clicking it will show a satisfaction survey to the user.">
Tell us about your recent gameplay
</message>
<!-- TPM firmware auto-update notifications -->
<message name="IDS_TPM_AUTO_UPDATE_PLANNED_NOTIFICATION_TITLE" desc="Title for the notification informing the user that local data will be deleted.">
Your local data will soon be deleted
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
32a8c59d2595993a2ac2aeaf6447c1f50447b520
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
32a8c59d2595993a2ac2aeaf6447c1f50447b520
3 changes: 3 additions & 0 deletions chrome/browser/ash/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,8 @@ source_set("ash") {
"borealis/borealis_service_impl.h",
"borealis/borealis_shutdown_monitor.cc",
"borealis/borealis_shutdown_monitor.h",
"borealis/borealis_survey_handler.cc",
"borealis/borealis_survey_handler.h",
"borealis/borealis_switches.cc",
"borealis/borealis_switches.h",
"borealis/borealis_task.cc",
Expand Down Expand Up @@ -5038,6 +5040,7 @@ source_set("unit_tests") {
"borealis/borealis_power_controller_unittest.cc",
"borealis/borealis_security_delegate_unittest.cc",
"borealis/borealis_shutdown_monitor_unittest.cc",
"borealis/borealis_survey_handler_unittest.cc",
"borealis/borealis_task_unittest.cc",
"borealis/borealis_token_hardware_checker_unittest.cc",
"borealis/borealis_util_unittest.cc",
Expand Down
2 changes: 2 additions & 0 deletions chrome/browser/ash/borealis/borealis_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class BorealisInstallUrlHandler;
class BorealisLaunchOptions;
class BorealisShutdownMonitor;
class BorealisWindowManager;
class BorealisSurveyHandler;

// A common location for all the interdependant components of borealis.
class BorealisService : public KeyedService {
Expand All @@ -40,6 +41,7 @@ class BorealisService : public KeyedService {
virtual BorealisLaunchOptions& LaunchOptions() = 0;
virtual BorealisShutdownMonitor& ShutdownMonitor() = 0;
virtual BorealisWindowManager& WindowManager() = 0;
virtual BorealisSurveyHandler& SurveyHandler() = 0;
};

} // namespace borealis
Expand Down
10 changes: 10 additions & 0 deletions chrome/browser/ash/borealis/borealis_service_fake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ BorealisWindowManager& BorealisServiceFake::WindowManager() {
return *window_manager_;
}

BorealisSurveyHandler& BorealisServiceFake::SurveyHandler() {
CHECK(survey_handler_);
return *survey_handler_;
}

void BorealisServiceFake::SetAppLauncherForTesting(
BorealisAppLauncher* app_launcher) {
app_launcher_ = app_launcher;
Expand Down Expand Up @@ -117,4 +122,9 @@ void BorealisServiceFake::SetWindowManagerForTesting(
window_manager_ = window_manager;
}

void BorealisServiceFake::SetSurveyHandlerForTesting(
BorealisSurveyHandler* survey_handler) {
survey_handler_ = survey_handler;
}

} // namespace borealis
3 changes: 3 additions & 0 deletions chrome/browser/ash/borealis/borealis_service_fake.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class BorealisServiceFake : public BorealisService {
BorealisLaunchOptions& LaunchOptions() override;
BorealisShutdownMonitor& ShutdownMonitor() override;
BorealisWindowManager& WindowManager() override;
BorealisSurveyHandler& SurveyHandler() override;

void SetAppLauncherForTesting(BorealisAppLauncher* app_launcher);
void SetAppUninstallerForTesting(BorealisAppUninstaller* app_uninstaller);
Expand All @@ -48,6 +49,7 @@ class BorealisServiceFake : public BorealisService {
void SetLaunchOptionsForTesting(BorealisLaunchOptions* launch_options);
void SetShutdownMonitorForTesting(BorealisShutdownMonitor* shutdown_monitor);
void SetWindowManagerForTesting(BorealisWindowManager* window_manager);
void SetSurveyHandlerForTesting(BorealisSurveyHandler* survey_handler);

private:
raw_ptr<BorealisAppLauncher, ExperimentalAsh> app_launcher_ = nullptr;
Expand All @@ -62,6 +64,7 @@ class BorealisServiceFake : public BorealisService {
raw_ptr<BorealisLaunchOptions, ExperimentalAsh> launch_options_ = nullptr;
raw_ptr<BorealisShutdownMonitor, ExperimentalAsh> shutdown_monitor_ = nullptr;
raw_ptr<BorealisWindowManager, ExperimentalAsh> window_manager_ = nullptr;
raw_ptr<BorealisSurveyHandler, ExperimentalAsh> survey_handler_ = nullptr;
};

} // namespace borealis
Expand Down
7 changes: 6 additions & 1 deletion chrome/browser/ash/borealis/borealis_service_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ BorealisServiceImpl::BorealisServiceImpl(Profile* profile)
install_url_handler_(profile_),
launch_options_(profile_),
shutdown_monitor_(profile_),
window_manager_(profile_) {}
window_manager_(profile_),
survey_handler_(profile_, &window_manager_) {}

BorealisServiceImpl::~BorealisServiceImpl() = default;

Expand Down Expand Up @@ -60,4 +61,8 @@ BorealisWindowManager& BorealisServiceImpl::WindowManager() {
return window_manager_;
}

BorealisSurveyHandler& BorealisServiceImpl::SurveyHandler() {
return survey_handler_;
}

} // namespace borealis
3 changes: 3 additions & 0 deletions chrome/browser/ash/borealis/borealis_service_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "chrome/browser/ash/borealis/borealis_launch_options.h"
#include "chrome/browser/ash/borealis/borealis_service.h"
#include "chrome/browser/ash/borealis/borealis_shutdown_monitor.h"
#include "chrome/browser/ash/borealis/borealis_survey_handler.h"
#include "chrome/browser/ash/borealis/borealis_window_manager.h"

namespace borealis {
Expand All @@ -38,6 +39,7 @@ class BorealisServiceImpl : public BorealisService {
BorealisLaunchOptions& LaunchOptions() override;
BorealisShutdownMonitor& ShutdownMonitor() override;
BorealisWindowManager& WindowManager() override;
BorealisSurveyHandler& SurveyHandler() override;

const raw_ptr<Profile, ExperimentalAsh> profile_;

Expand All @@ -51,6 +53,7 @@ class BorealisServiceImpl : public BorealisService {
BorealisLaunchOptions launch_options_;
BorealisShutdownMonitor shutdown_monitor_;
BorealisWindowManager window_manager_;
BorealisSurveyHandler survey_handler_;
};

} // namespace borealis
Expand Down
138 changes: 138 additions & 0 deletions chrome/browser/ash/borealis/borealis_survey_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright 2023 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/ash/borealis/borealis_survey_handler.h"

#include "base/containers/flat_map.h"
#include "base/feature_list.h"
#include "base/functional/bind_internal.h"
#include "base/logging.h"
#include "base/system/sys_info.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/borealis/borealis_service.h"
#include "chrome/browser/ash/borealis/borealis_util.h"
#include "chrome/browser/ash/borealis/borealis_window_manager.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service.h"
#include "chrome/browser/ash/guest_os/guest_os_registry_service_factory.h"
#include "chrome/browser/ash/hats/hats_config.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/screen.h"

namespace borealis {

BorealisSurveyHandler::BorealisSurveyHandler(
Profile* profile,
BorealisWindowManager* window_manager)
: profile_(profile) {
if (!base::FeatureList::IsEnabled(ash::kHatsBorealisGamesSurvey.feature)) {
VLOG(1) << "Borealis survey feature is not enabled";
return;
}
lifetime_observation_.Observe(window_manager);
}
BorealisSurveyHandler::~BorealisSurveyHandler() = default;

absl::optional<int> BorealisSurveyHandler::GetGameId(
const std::string& app_id) {
// Attempt to get the Borealis app ID.
// TODO(b/173977876): Implement this in a more reliable way.
absl::optional<int> game_id;
absl::optional<guest_os::GuestOsRegistryService::Registration> registration =
guest_os::GuestOsRegistryServiceFactory::GetForProfile(profile_)
->GetRegistration(app_id);
if (registration.has_value()) {
game_id = borealis::GetBorealisAppId(registration->Exec());
}
return game_id;
}

base::flat_map<std::string, std::string> BorealisSurveyHandler::GetSurveyData(
std::string owner_id,
const std::string app_id,
std::string window_title,
absl::optional<int> game_id) {
// Number of monitors
int internal_displays = 0;
int external_displays = 0;
for (const display::Display& d :
display::Screen::GetScreen()->GetAllDisplays()) {
if (d.IsInternal()) {
internal_displays++;
} else {
external_displays++;
}
}

// Proton/SLR versions
borealis::CompatToolInfo compat_tool_info;
std::string output;
if (borealis::GetCompatToolInfo(owner_id, &output)) {
compat_tool_info = borealis::ParseCompatToolInfo(game_id, output);
} else {
LOG(WARNING) << "Failed to get compat tool version info:";
LOG(WARNING) << output;
}

// Steam GameID
if (!game_id.has_value() && compat_tool_info.game_id.has_value()) {
game_id = compat_tool_info.game_id.value();
}
std::string game_id_value = "";
if (game_id.has_value()) {
game_id_value = base::StringPrintf("%d", game_id.value());
}

base::flat_map<std::string, std::string> survey_data = {
{"appName", window_title},
{"board", base::SysInfo::HardwareModelName()},
{"specs",
base::StringPrintf("%ldGB; %s",
(long)(base::SysInfo::AmountOfPhysicalMemory() /
(1000 * 1000 * 1000)),
base::SysInfo::CPUModelName().c_str())},
{"monitorsInternal", base::NumberToString(internal_displays)},
{"monitorsExternal", base::NumberToString(external_displays)},
{"proton", compat_tool_info.proton},
{"steam", compat_tool_info.slr},
{"gameId", game_id_value}};
return survey_data;
}

void BorealisSurveyHandler::OnAppFinished(const std::string& app_id,
aura::Window* last_window) {
if (IsNonGameBorealisApp(app_id)) {
return;
}
if (ash::HatsNotificationController::ShouldShowSurveyToProfile(
profile_, ash::kHatsBorealisGamesSurvey)) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, base::MayBlock(),
base::BindOnce(&GetSurveyData,
ash::ProfileHelper::GetUserIdHashFromProfile(profile_),
std::string(app_id),
base::UTF16ToUTF8(last_window->GetTitle()),
GetGameId(app_id)),
base::BindOnce(&BorealisSurveyHandler::CreateNotification,
weak_factory_.GetWeakPtr()));
}
}

void BorealisSurveyHandler::CreateNotification(
base::flat_map<std::string, std::string> survey_data) {
hats_notification_controller_ = new ash::HatsNotificationController(
profile_, ash::kHatsBorealisGamesSurvey, survey_data,
l10n_util::GetStringUTF16(IDS_BOREALIS_HATS_TITLE),
l10n_util::GetStringUTF16(IDS_BOREALIS_HATS_BODY));
}

void BorealisSurveyHandler::OnWindowManagerDeleted(
BorealisWindowManager* window_manager) {
lifetime_observation_.Reset();
}

} // namespace borealis
55 changes: 55 additions & 0 deletions chrome/browser/ash/borealis/borealis_survey_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_BOREALIS_BOREALIS_SURVEY_HANDLER_H_
#define CHROME_BROWSER_ASH_BOREALIS_BOREALIS_SURVEY_HANDLER_H_

#include "chrome/browser/ash/borealis/borealis_window_manager.h"
#include "chrome/browser/ash/hats/hats_notification_controller.h"

namespace borealis {

// Used to show a Happiness Tracking Survey when a game is closed.
class BorealisSurveyHandler
: public BorealisWindowManager::AppWindowLifetimeObserver {
public:
BorealisSurveyHandler(Profile* profile,
BorealisWindowManager* window_manager);

BorealisSurveyHandler(const BorealisSurveyHandler&) = delete;
BorealisSurveyHandler& operator=(const BorealisSurveyHandler&) = delete;

~BorealisSurveyHandler() override;

// BorealisWindowManager::AppWindowLifetimeObserver overrides
void OnAppFinished(const std::string& app_id,
aura::Window* last_window) override;
void OnWindowManagerDeleted(BorealisWindowManager* window_manager) override;

private:
friend class AshTestBase;
friend class BorealisSurveyHandlerTest;
FRIEND_TEST_ALL_PREFIXES(BorealisSurveyHandlerTest,
GetGameIdReturnsCorrectId);
FRIEND_TEST_ALL_PREFIXES(BorealisSurveyHandlerTest,
GetSurveyDataReturnsCorrectData);

scoped_refptr<ash::HatsNotificationController> hats_notification_controller_;
Profile* profile_;
base::ScopedObservation<BorealisWindowManager,
BorealisWindowManager::AppWindowLifetimeObserver>
lifetime_observation_{this};
base::WeakPtrFactory<BorealisSurveyHandler> weak_factory_{this};

void CreateNotification(base::flat_map<std::string, std::string> survey_data);
absl::optional<int> GetGameId(const std::string& app_id);
static base::flat_map<std::string, std::string> GetSurveyData(
std::string owner_id,
const std::string app_id,
std::string window_title,
absl::optional<int> game_id);
};
} // namespace borealis

#endif // CHROME_BROWSER_ASH_BOREALIS_BOREALIS_SURVEY_HANDLER_H_

0 comments on commit 673118a

Please sign in to comment.