Commit 893ca920 authored by Karthikeyan Ramasubramanian's avatar Karthikeyan Ramasubramanian Committed by Commit Bot

Add support to notify user about slow boot after an update

When the system firmware update includes MRC training update it can
cause the system to boot slower after the update is applied. Also the
graphics module is not available to notify the user about the MRC
training. So notify the user about slow reboot beforehand. Here are the
screenshots of the user notification with and without a slow boot user
notification.
With slow boot user notification:
https://screenshot.googleplex.com/w5GJ3VYpnbP
Without slow boot user notification:
https://screenshot.googleplex.com/pv28UdisnXS

Bug: b/129056373
Change-Id: I3c70007688ad8f4ab727721a696edfbe06535f7c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1831005
Commit-Queue: Karthikeyan Ramasubramanian <kramasub@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#736463}
parent 94ea5a19
...@@ -423,6 +423,9 @@ This file contains the strings for ash. ...@@ -423,6 +423,9 @@ This file contains the strings for ash.
<message name="IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK" desc="The notification message used in the system notification update when the device is rolled back and data will be deleted."> <message name="IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK" desc="The notification message used in the system notification update when the device is rolled back and data will be deleted.">
Your administrator is rolling back your device. All data will be deleted when the device is restarted. Your administrator is rolling back your device. All data will be deleted when the device is restarted.
</message> </message>
<message name="IDS_UPDATE_NOTIFICATION_MESSAGE_SLOW_BOOT" desc="The additional body of the notification to notify the user that the next reboot is slow.">
<ph name="UPDATE_TEXT">$1<ex>Learn more about the latest Chromium OS update</ex></ph>. This Chromebook needs to restart to apply an update. This can take up to 1 minute.
</message>
<message name="IDS_ASH_STATUS_TRAY_VOLUME" desc="The accessible text for the toggle volume muted button in the tray."> <message name="IDS_ASH_STATUS_TRAY_VOLUME" desc="The accessible text for the toggle volume muted button in the tray.">
Toggle Volume. <ph name="STATE_TEXT">$1<ex>Volume is muted</ex></ph> Toggle Volume. <ph name="STATE_TEXT">$1<ex>Volume is muted</ex></ph>
......
...@@ -31,6 +31,7 @@ class SystemNotificationController { ...@@ -31,6 +31,7 @@ class SystemNotificationController {
private: private:
friend class AutoConnectNotifierTest; friend class AutoConnectNotifierTest;
friend class UpdateNotificationControllerTest;
const std::unique_ptr<AutoConnectNotifier> auto_connect_; const std::unique_ptr<AutoConnectNotifier> auto_connect_;
const std::unique_ptr<CapsLockNotificationController> caps_lock_; const std::unique_ptr<CapsLockNotificationController> caps_lock_;
const std::unique_ptr<CastNotificationController> cast_; const std::unique_ptr<CastNotificationController> cast_;
......
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
#include "ash/strings/grit/ash_strings.h" #include "ash/strings/grit/ash_strings.h"
#include "ash/system/model/system_tray_model.h" #include "ash/system/model/system_tray_model.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "build/branding_buildflags.h" #include "build/branding_buildflags.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -26,13 +30,21 @@ namespace { ...@@ -26,13 +30,21 @@ namespace {
const char kNotifierId[] = "ash.update"; const char kNotifierId[] = "ash.update";
bool CheckForSlowBoot(const base::FilePath& slow_boot_file_path) {
if (base::PathExists(slow_boot_file_path)) {
return true;
}
return false;
}
} // namespace } // namespace
// static // static
const char UpdateNotificationController::kNotificationId[] = "chrome://update"; const char UpdateNotificationController::kNotificationId[] = "chrome://update";
UpdateNotificationController::UpdateNotificationController() UpdateNotificationController::UpdateNotificationController()
: model_(Shell::Get()->system_tray_model()->update_model()) { : model_(Shell::Get()->system_tray_model()->update_model()),
slow_boot_file_path_("/mnt/stateful_partition/etc/slow_boot_required") {
model_->AddObserver(this); model_->AddObserver(this);
OnUpdateAvailable(); OnUpdateAvailable();
} }
...@@ -41,13 +53,18 @@ UpdateNotificationController::~UpdateNotificationController() { ...@@ -41,13 +53,18 @@ UpdateNotificationController::~UpdateNotificationController() {
model_->RemoveObserver(this); model_->RemoveObserver(this);
} }
void UpdateNotificationController::OnUpdateAvailable() { void UpdateNotificationController::GenerateUpdateNotification(
base::Optional<bool> slow_boot_file_path_exists) {
if (!ShouldShowUpdate()) { if (!ShouldShowUpdate()) {
message_center::MessageCenter::Get()->RemoveNotification( message_center::MessageCenter::Get()->RemoveNotification(
kNotificationId, false /* by_user */); kNotificationId, false /* by_user */);
return; return;
} }
if (slow_boot_file_path_exists != base::nullopt) {
slow_boot_file_path_exists_ = slow_boot_file_path_exists.value();
}
message_center::SystemNotificationWarningLevel warning_level = message_center::SystemNotificationWarningLevel warning_level =
(model_->rollback() || (model_->rollback() ||
model_->notification_style() == NotificationStyle::kAdminRequired) model_->notification_style() == NotificationStyle::kAdminRequired)
...@@ -87,6 +104,16 @@ void UpdateNotificationController::OnUpdateAvailable() { ...@@ -87,6 +104,16 @@ void UpdateNotificationController::OnUpdateAvailable() {
MessageCenter::Get()->AddNotification(std::move(notification)); MessageCenter::Get()->AddNotification(std::move(notification));
} }
void UpdateNotificationController::OnUpdateAvailable() {
base::PostTaskAndReplyWithResult(
FROM_HERE,
base::TaskTraits{base::ThreadPool(), base::MayBlock(),
base::TaskPriority::USER_BLOCKING},
base::BindOnce(&CheckForSlowBoot, slow_boot_file_path_),
base::BindOnce(&UpdateNotificationController::GenerateUpdateNotification,
weak_ptr_factory_.GetWeakPtr()));
}
bool UpdateNotificationController::ShouldShowUpdate() const { bool UpdateNotificationController::ShouldShowUpdate() const {
return model_->update_required() || model_->update_over_cellular_available(); return model_->update_required() || model_->update_over_cellular_available();
} }
...@@ -103,13 +130,20 @@ base::string16 UpdateNotificationController::GetNotificationMessage() const { ...@@ -103,13 +130,20 @@ base::string16 UpdateNotificationController::GetNotificationMessage() const {
} }
const base::string16 notification_body = model_->notification_body(); const base::string16 notification_body = model_->notification_body();
base::string16 update_text;
if (model_->update_type() == UpdateType::kSystem && if (model_->update_type() == UpdateType::kSystem &&
!notification_body.empty()) { !notification_body.empty()) {
return notification_body; update_text = notification_body;
} else {
update_text = l10n_util::GetStringFUTF16(
IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE, system_app_name);
} }
return l10n_util::GetStringFUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE, if (slow_boot_file_path_exists_) {
system_app_name); return l10n_util::GetStringFUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_SLOW_BOOT,
update_text);
}
return update_text;
} }
base::string16 UpdateNotificationController::GetNotificationTitle() const { base::string16 UpdateNotificationController::GetNotificationTitle() const {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include "ash/ash_export.h" #include "ash/ash_export.h"
#include "ash/system/model/update_model.h" #include "ash/system/model/update_model.h"
#include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
namespace ash { namespace ash {
...@@ -27,11 +28,16 @@ class ASH_EXPORT UpdateNotificationController : public UpdateObserver { ...@@ -27,11 +28,16 @@ class ASH_EXPORT UpdateNotificationController : public UpdateObserver {
base::string16 GetNotificationTitle() const; base::string16 GetNotificationTitle() const;
base::string16 GetNotificationMessage() const; base::string16 GetNotificationMessage() const;
void HandleNotificationClick(base::Optional<int> index); void HandleNotificationClick(base::Optional<int> index);
void GenerateUpdateNotification(
base::Optional<bool> slow_boot_file_path_exists);
static const char kNotificationId[]; static const char kNotificationId[];
UpdateModel* const model_; UpdateModel* const model_;
base::FilePath slow_boot_file_path_;
bool slow_boot_file_path_exists_ = false;
base::WeakPtrFactory<UpdateNotificationController> weak_ptr_factory_{this}; base::WeakPtrFactory<UpdateNotificationController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(UpdateNotificationController); DISALLOW_COPY_AND_ASSIGN(UpdateNotificationController);
......
...@@ -7,7 +7,11 @@ ...@@ -7,7 +7,11 @@
#include "ash/public/cpp/ash_features.h" #include "ash/public/cpp/ash_features.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/system/model/system_tray_model.h" #include "ash/system/model/system_tray_model.h"
#include "ash/system/system_notification_controller.h"
#include "ash/test/ash_test_base.h" #include "ash/test/ash_test_base.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/branding_buildflags.h" #include "build/branding_buildflags.h"
#include "ui/message_center/message_center.h" #include "ui/message_center/message_center.h"
...@@ -72,6 +76,14 @@ class UpdateNotificationControllerTest : public AshTestBase { ...@@ -72,6 +76,14 @@ class UpdateNotificationControllerTest : public AshTestBase {
->priority(); ->priority();
} }
void AddSlowBootFilePath(const base::FilePath& file_path) {
int bytes_written = base::WriteFile(file_path, "1\n", 2);
EXPECT_TRUE(bytes_written == 2);
Shell::Get()
->system_notification_controller()
->update_->slow_boot_file_path_ = file_path;
}
private: private:
DISALLOW_COPY_AND_ASSIGN(UpdateNotificationControllerTest); DISALLOW_COPY_AND_ASSIGN(UpdateNotificationControllerTest);
}; };
...@@ -87,6 +99,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) { ...@@ -87,6 +99,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) {
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
false, UpdateType::kSystem); false, UpdateType::kSystem);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Update available", GetNotificationTitle());
...@@ -95,6 +112,37 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) { ...@@ -95,6 +112,37 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdate) {
EXPECT_EQ("Restart to update", GetNotificationButton(0)); EXPECT_EQ("Restart to update", GetNotificationButton(0));
} }
// Tests that the update icon becomes visible when an update becomes
// available.
TEST_F(UpdateNotificationControllerTest, VisibilityAfterUpdateWithSlowReboot) {
// The system starts with no update pending, so the notification isn't
// visible.
EXPECT_FALSE(HasNotification());
// Add a slow boot file.
base::ScopedTempDir tmp_dir;
ASSERT_TRUE(tmp_dir.CreateUniqueTempDir());
AddSlowBootFilePath(tmp_dir.GetPath().Append("slow_boot_required"));
// Simulate an update.
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
false, UpdateType::kSystem);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible.
ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle());
EXPECT_EQ("Learn more about the latest " SYSTEM_APP_NAME
" update. This Chromebook needs to restart to apply an update. "
"This can take up to 1 minute.",
GetNotificationMessage());
EXPECT_EQ("Restart to update", GetNotificationButton(0));
}
#if BUILDFLAG(GOOGLE_CHROME_BRANDING) #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) { TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) {
// The system starts with no update pending, so the notification isn't // The system starts with no update pending, so the notification isn't
...@@ -105,6 +153,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) { ...@@ -105,6 +153,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterFlashUpdate) {
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
false, UpdateType::kFlash); false, UpdateType::kFlash);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Adobe Flash Player update available", GetNotificationTitle()); EXPECT_EQ("Adobe Flash Player update available", GetNotificationTitle());
...@@ -126,6 +179,11 @@ TEST_F(UpdateNotificationControllerTest, ...@@ -126,6 +179,11 @@ TEST_F(UpdateNotificationControllerTest,
Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible( Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible(
true); true);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Update available", GetNotificationTitle());
...@@ -138,6 +196,11 @@ TEST_F(UpdateNotificationControllerTest, ...@@ -138,6 +196,11 @@ TEST_F(UpdateNotificationControllerTest,
Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible( Shell::Get()->system_tray_model()->SetUpdateOverCellularAvailableIconVisible(
false); false);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification disappears. // The notification disappears.
EXPECT_FALSE(HasNotification()); EXPECT_FALSE(HasNotification());
} }
...@@ -152,6 +215,11 @@ TEST_F(UpdateNotificationControllerTest, ...@@ -152,6 +215,11 @@ TEST_F(UpdateNotificationControllerTest,
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true, Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, true,
false, UpdateType::kSystem); false, UpdateType::kSystem);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Update available", GetNotificationTitle());
...@@ -171,6 +239,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterRollback) { ...@@ -171,6 +239,11 @@ TEST_F(UpdateNotificationControllerTest, VisibilityAfterRollback) {
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
true, UpdateType::kSystem); true, UpdateType::kSystem);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Device will be rolled back", GetNotificationTitle()); EXPECT_EQ("Device will be rolled back", GetNotificationTitle());
...@@ -190,6 +263,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) { ...@@ -190,6 +263,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) {
Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false, Shell::Get()->system_tray_model()->ShowUpdateIcon(UpdateSeverity::kLow, false,
false, UpdateType::kSystem); false, UpdateType::kSystem);
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification is now visible. // The notification is now visible.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Update available", GetNotificationTitle());
...@@ -209,6 +287,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) { ...@@ -209,6 +287,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) {
base::UTF8ToUTF16(recommended_notification_title), base::UTF8ToUTF16(recommended_notification_title),
base::UTF8ToUTF16(recommended_notification_body)); base::UTF8ToUTF16(recommended_notification_body));
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification's title and body have changed. // The notification's title and body have changed.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ(recommended_notification_title, GetNotificationTitle()); EXPECT_EQ(recommended_notification_title, GetNotificationTitle());
...@@ -229,6 +312,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) { ...@@ -229,6 +312,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) {
base::UTF8ToUTF16(required_notification_title), base::UTF8ToUTF16(required_notification_title),
base::UTF8ToUTF16(required_notification_body)); base::UTF8ToUTF16(required_notification_body));
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification's title and body have changed. // The notification's title and body have changed.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ(required_notification_title, GetNotificationTitle()); EXPECT_EQ(required_notification_title, GetNotificationTitle());
...@@ -242,6 +330,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) { ...@@ -242,6 +330,11 @@ TEST_F(UpdateNotificationControllerTest, SetUpdateNotificationStateTest) {
Shell::Get()->system_tray_model()->SetUpdateNotificationState( Shell::Get()->system_tray_model()->SetUpdateNotificationState(
NotificationStyle::kDefault, base::string16(), base::string16()); NotificationStyle::kDefault, base::string16(), base::string16());
// Showing Update Notification posts a task to check for slow boot request
// and use the result of that check to generate appropriate notification. Wait
// until everything is complete and then check if the notification is visible.
task_environment_->RunUntilIdle();
// The notification has the default text. // The notification has the default text.
ASSERT_TRUE(HasNotification()); ASSERT_TRUE(HasNotification());
EXPECT_EQ("Update available", GetNotificationTitle()); EXPECT_EQ("Update available", GetNotificationTitle());
......
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