Commit f1b01452 authored by Saurabh Nijhara's avatar Saurabh Nijhara Committed by Commit Bot

Notifications in offline device for minimum chrome version policy

This CL introduces the handling of showing in-session notifications
when update is required as per the policy but the device is not
connected to a network for the updates to download. The notification
is to be shown when the policy is received and on the last day of the
deadline. A new WallClockTimer is introduced to keep track of the last
day for showing the notification. A new test is also included to
verify this behavior.

The subsequent CLs will introduce the handling for notifications in
case of end-of-life devices and metered network.

Bug: 1048607
Change-Id: Idecb0f976ad96af537e2ffe4faf9936b7ddfc407
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2218020
Commit-Queue: Saurabh Nijhara <snijhara@google.com>
Reviewed-by: default avatarRoman Sorokin [CET] <rsorokin@chromium.org>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#776504}
parent 128c78a7
...@@ -2784,7 +2784,7 @@ ...@@ -2784,7 +2784,7 @@
Reset Reset
</message> </message>
<!-- Obsolete versions Notification strings--> <!-- Update Required Screen Strings-->
<message name="IDS_UPDATE_REQUIRED_SCREEN_TITLE" desc="The title of the update required dialog on the login screen to inform the user that policy prevents user sign in before OS version is is updated."> <message name="IDS_UPDATE_REQUIRED_SCREEN_TITLE" desc="The title of the update required dialog on the login screen to inform the user that policy prevents user sign in before OS version is is updated.">
Immediate update required Immediate update required
</message> </message>
...@@ -2816,6 +2816,17 @@ ...@@ -2816,6 +2816,17 @@
Device will restart when updates are complete. Device will restart when updates are complete.
</message> </message>
<!-- Update Required Notiifcation Strings-->
<message name="IDS_UPDATE_REQUIRED_NO_NETWORK_TITLE_IMMEDIATE" desc="The title of the notification dialog informing the user that the device is not connected to the internet and it is last day to update the device.">
Last day to update device
</message>
<message name="IDS_UPDATE_REQUIRED_NO_NETWORK_TITLE_DAYS" desc="The title of the notification dialog informing the user that the device is not connected to the internet and an update is required within two or more days.">
Update device within <ph name="COUNT">$1<ex>2</ex></ph> days
</message>
<message name="IDS_UPDATE_REQUIRED_NO_NETWORK_MESSAGE" desc="The body text of the notification dialog informing the user that the device is not connected to the internet and an update is required.">
<ph name="DOMAIN">$1<ex>example.com</ex></ph> requires you to download an update before the deadline. The update will download automatically when you connect to the internet.
</message>
<!-- Genius App --> <!-- Genius App -->
<message name="IDS_GENIUS_APP_NAME" desc="Name of the genius app in the app shelf"> <message name="IDS_GENIUS_APP_NAME" desc="Name of the genius app in the app shelf">
Get Help Get Help
......
9ea4bcaf0a870ea64b4655fc089016241dbb3f35
\ No newline at end of file
8e54c107cee1c0dac8732954ab1becef6950dfda
\ No newline at end of file
9ea4bcaf0a870ea64b4655fc089016241dbb3f35
\ No newline at end of file
...@@ -2475,6 +2475,8 @@ source_set("chromeos") { ...@@ -2475,6 +2475,8 @@ source_set("chromeos") {
"ui/screen_capture_notification_ui_chromeos.h", "ui/screen_capture_notification_ui_chromeos.h",
"ui/tpm_auto_update_notification.cc", "ui/tpm_auto_update_notification.cc",
"ui/tpm_auto_update_notification.h", "ui/tpm_auto_update_notification.h",
"ui/update_required_notification.cc",
"ui/update_required_notification.h",
"usb/cros_usb_detector.cc", "usb/cros_usb_detector.cc",
"usb/cros_usb_detector.h", "usb/cros_usb_detector.h",
"virtual_machines/virtual_machines_util.cc", "virtual_machines/virtual_machines_util.cc",
......
...@@ -4,16 +4,24 @@ ...@@ -4,16 +4,24 @@
#include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h" #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
#include <cmath>
#include "ash/public/cpp/system_tray.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/time/default_clock.h" #include "base/time/default_clock.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h" #include "chrome/browser/chromeos/policy/minimum_version_policy_handler_delegate_impl.h"
#include "chrome/browser/chromeos/ui/update_required_notification.h"
#include "chrome/browser/upgrade_detector/build_state.h" #include "chrome/browser/upgrade_detector/build_state.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/cros_settings_provider.h" #include "chromeos/settings/cros_settings_provider.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
...@@ -26,10 +34,35 @@ namespace policy { ...@@ -26,10 +34,35 @@ namespace policy {
namespace { namespace {
const int kOneWeekEolNotificationInDays = 7;
PrefService* local_state() { PrefService* local_state() {
return g_browser_process->local_state(); return g_browser_process->local_state();
} }
MinimumVersionPolicyHandler::NetworkStatus GetCurrentNetworkStatus() {
chromeos::NetworkStateHandler* network_state_handler =
chromeos::NetworkHandler::Get()->network_state_handler();
const chromeos::NetworkState* current_network =
network_state_handler->DefaultNetwork();
if (!current_network || !current_network->IsConnectedState())
return MinimumVersionPolicyHandler::NetworkStatus::kOffline;
if (network_state_handler->default_network_is_metered())
return MinimumVersionPolicyHandler::NetworkStatus::kMetered;
return MinimumVersionPolicyHandler::NetworkStatus::kAllowed;
}
void OpenNetworkSettings() {
ash::SystemTray::Get()->ShowNetworkDetailedViewBubble(
true /* show_by_click */);
}
std::string GetEnterpriseDomainName() {
return g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetEnterpriseDisplayDomain();
}
} // namespace } // namespace
const char MinimumVersionPolicyHandler::kChromeVersion[] = "chrome_version"; const char MinimumVersionPolicyHandler::kChromeVersion[] = "chrome_version";
...@@ -93,6 +126,7 @@ MinimumVersionPolicyHandler::MinimumVersionPolicyHandler( ...@@ -93,6 +126,7 @@ MinimumVersionPolicyHandler::MinimumVersionPolicyHandler(
MinimumVersionPolicyHandler::~MinimumVersionPolicyHandler() { MinimumVersionPolicyHandler::~MinimumVersionPolicyHandler() {
g_browser_process->GetBuildState()->RemoveObserver(this); g_browser_process->GetBuildState()->RemoveObserver(this);
StopObservingNetwork();
} }
void MinimumVersionPolicyHandler::AddObserver(Observer* observer) { void MinimumVersionPolicyHandler::AddObserver(Observer* observer) {
...@@ -204,9 +238,13 @@ void MinimumVersionPolicyHandler::Reset() { ...@@ -204,9 +238,13 @@ void MinimumVersionPolicyHandler::Reset() {
deadline_reached = false; deadline_reached = false;
eol_reached_ = false; eol_reached_ = false;
update_required_deadline_timer_.Stop(); update_required_deadline_timer_.Stop();
notification_timer_.Stop();
g_browser_process->GetBuildState()->RemoveObserver(this); g_browser_process->GetBuildState()->RemoveObserver(this);
state_.reset(); state_.reset();
HideNotification();
notification_handler_.reset();
ResetLocalState(); ResetLocalState();
StopObservingNetwork();
} }
void MinimumVersionPolicyHandler::FetchEolInfo() { void MinimumVersionPolicyHandler::FetchEolInfo() {
...@@ -290,11 +328,11 @@ void MinimumVersionPolicyHandler::HandleUpdateRequired( ...@@ -290,11 +328,11 @@ void MinimumVersionPolicyHandler::HandleUpdateRequired(
// not been extended. // not been extended.
if (deadline > previous_deadline) if (deadline > previous_deadline)
UpdateLocalState(warning_time); UpdateLocalState(warning_time);
StartDeadlineTimer(deadline); StartDeadlineTimer(deadline);
if (!eol_reached_) if (!eol_reached_)
StartObservingUpdate(); StartObservingUpdate();
// TODO(https://crbug.com/1048607): Show in-session notifications in case of ShowAndScheduleNotification(deadline);
// end-of-life and network limitations.
} }
void MinimumVersionPolicyHandler::ResetLocalState() { void MinimumVersionPolicyHandler::ResetLocalState() {
...@@ -330,10 +368,94 @@ void MinimumVersionPolicyHandler::StartObservingUpdate() { ...@@ -330,10 +368,94 @@ void MinimumVersionPolicyHandler::StartObservingUpdate() {
build_state->AddObserver(this); build_state->AddObserver(this);
} }
void MinimumVersionPolicyHandler::MaybeShowNotification(
base::TimeDelta warning) {
const NetworkStatus status = GetCurrentNetworkStatus();
if (status == NetworkStatus::kAllowed || !delegate_->IsUserLoggedIn() ||
!delegate_->IsUserManaged()) {
// TODO(https://crbug.com/1048607): Show notification on managed user log in
// if it is the last day of the deadline.
return;
}
if (!notification_handler_) {
notification_handler_ =
std::make_unique<chromeos::UpdateRequiredNotification>();
}
if (status == NetworkStatus::kOffline) {
notification_handler_->Show(
NotificationType::kNoConnection, warning, GetEnterpriseDomainName(),
base::BindOnce(&OpenNetworkSettings),
base::BindOnce(&MinimumVersionPolicyHandler::StopObservingNetwork,
weak_factory_.GetWeakPtr()));
}
// TODO(https://crbug.com/1048607): Show in-session notifications in case of
// end-of-life and metered network.
chromeos::NetworkStateHandler* network_state_handler =
chromeos::NetworkHandler::Get()->network_state_handler();
if (!network_state_handler->HasObserver(this))
network_state_handler->AddObserver(this, FROM_HERE);
}
void MinimumVersionPolicyHandler::ShowAndScheduleNotification(
base::Time deadline) {
const base::Time now = clock_->Now();
if (deadline <= now)
return;
base::Time expiry;
base::TimeDelta time_remaining = deadline - now;
const int days_remaining = std::lround(
time_remaining.InSecondsF() / base::TimeDelta::FromDays(1).InSecondsF());
// Network limitation notifications are shown when policy is received and on
// the last day. End of life notifications are shown when policy is received,
// one week before EOL and on the last day. No need to schedule a notification
// if it is already the last day.
if (eol_reached_ && days_remaining > kOneWeekEolNotificationInDays) {
expiry =
deadline - base::TimeDelta::FromDays(kOneWeekEolNotificationInDays);
} else if (days_remaining > 1) {
expiry = deadline - base::TimeDelta::FromDays(1);
}
MaybeShowNotification(base::TimeDelta::FromDays(days_remaining));
if (!expiry.is_null()) {
notification_timer_.Start(
FROM_HERE, expiry,
base::BindOnce(
&MinimumVersionPolicyHandler::ShowAndScheduleNotification,
weak_factory_.GetWeakPtr(), deadline));
}
}
void MinimumVersionPolicyHandler::OnUpdate(const BuildState* build_state) { void MinimumVersionPolicyHandler::OnUpdate(const BuildState* build_state) {
// Reset the state if new version is greater or equal the required version. // Reset the state if new version is greater or equal the required version.
// Hide update required screen if it is shown.
if (build_state->installed_version()->CompareTo(state_->version()) >= 0) if (build_state->installed_version()->CompareTo(state_->version()) >= 0)
Reset(); HandleUpdateNotRequired();
}
void MinimumVersionPolicyHandler::HideNotification() const {
if (notification_handler_)
notification_handler_->Hide();
}
void MinimumVersionPolicyHandler::DefaultNetworkChanged(
const chromeos::NetworkState* network) {
// Close notification if network has switched to one that allows updates.
const NetworkStatus status = GetCurrentNetworkStatus();
if (status == NetworkStatus::kAllowed && notification_handler_) {
HideNotification();
}
}
void MinimumVersionPolicyHandler::StopObservingNetwork() {
chromeos::NetworkStateHandler* network_state_handler =
chromeos::NetworkHandler::Get()->network_state_handler();
network_state_handler->RemoveObserver(this, FROM_HERE);
} }
void MinimumVersionPolicyHandler::OnDeadlineReached() { void MinimumVersionPolicyHandler::OnDeadlineReached() {
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/upgrade_detector/build_state_observer.h" #include "chrome/browser/upgrade_detector/build_state_observer.h"
#include "chromeos/dbus/update_engine_client.h" #include "chromeos/dbus/update_engine_client.h"
#include "chromeos/network/network_state_handler_observer.h"
class PrefRegistrySimple; class PrefRegistrySimple;
...@@ -25,11 +26,17 @@ class DictionaryValue; ...@@ -25,11 +26,17 @@ class DictionaryValue;
class Time; class Time;
} }
namespace chromeos {
class UpdateRequiredNotification;
}
namespace policy { namespace policy {
// This class observes the device setting |kMinimumChromeVersionEnforced|, and // This class observes the device setting |kMinimumChromeVersionEnforced|, and
// checks if respective requirement is met. // checks if respective requirement is met.
class MinimumVersionPolicyHandler : public BuildStateObserver { class MinimumVersionPolicyHandler
: public BuildStateObserver,
public chromeos::NetworkStateHandlerObserver {
public: public:
static const char kChromeVersion[]; static const char kChromeVersion[];
static const char kWarningPeriod[]; static const char kWarningPeriod[];
...@@ -108,13 +115,24 @@ class MinimumVersionPolicyHandler : public BuildStateObserver { ...@@ -108,13 +115,24 @@ class MinimumVersionPolicyHandler : public BuildStateObserver {
base::TimeDelta eol_warning_time_; base::TimeDelta eol_warning_time_;
}; };
enum class NetworkStatus { kAllowed, kMetered, kOffline };
enum class NotificationType {
kMeteredConnection,
kNoConnection,
kEolReached
};
explicit MinimumVersionPolicyHandler(Delegate* delegate, explicit MinimumVersionPolicyHandler(Delegate* delegate,
chromeos::CrosSettings* cros_settings); chromeos::CrosSettings* cros_settings);
~MinimumVersionPolicyHandler() override; ~MinimumVersionPolicyHandler() override;
// BuildStateObserver // BuildStateObserver:
void OnUpdate(const BuildState* build_state) override; void OnUpdate(const BuildState* build_state) override;
// NetworkStateHandlerObserver:
void DefaultNetworkChanged(const chromeos::NetworkState* network) override;
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
bool RequirementsAreSatisfied() const { return requirements_met_; } bool RequirementsAreSatisfied() const { return requirements_met_; }
...@@ -168,6 +186,18 @@ class MinimumVersionPolicyHandler : public BuildStateObserver { ...@@ -168,6 +186,18 @@ class MinimumVersionPolicyHandler : public BuildStateObserver {
// state if new version satisfies the minimum version requirement. // state if new version satisfies the minimum version requirement.
void StartObservingUpdate(); void StartObservingUpdate();
// Shows notification for a managed logged in user if update is required and
// the device can not be updated due to end-of-life or network limitations.
void MaybeShowNotification(base::TimeDelta warning);
// Shows notification if required and starts a timer to expire when the next
// notification is to be shown.
void ShowAndScheduleNotification(base::Time deadline);
void HideNotification() const;
void StopObservingNetwork();
// Updates pref |kUpdateRequiredWarningPeriod| in local state to // Updates pref |kUpdateRequiredWarningPeriod| in local state to
// |warning_time|. If |kUpdateRequiredTimerStartTime| is not null, it means // |warning_time|. If |kUpdateRequiredTimerStartTime| is not null, it means
// update is already required and hence, the timer start time should not be // update is already required and hence, the timer start time should not be
...@@ -200,6 +230,9 @@ class MinimumVersionPolicyHandler : public BuildStateObserver { ...@@ -200,6 +230,9 @@ class MinimumVersionPolicyHandler : public BuildStateObserver {
// Fires when the deadline to update the device has reached or passed. // Fires when the deadline to update the device has reached or passed.
util::WallClockTimer update_required_deadline_timer_; util::WallClockTimer update_required_deadline_timer_;
// Fires when next update required notification is to be shown.
util::WallClockTimer notification_timer_;
// Non-owning reference to CrosSettings. This class have shorter lifetime than // Non-owning reference to CrosSettings. This class have shorter lifetime than
// CrosSettings. // CrosSettings.
chromeos::CrosSettings* cros_settings_; chromeos::CrosSettings* cros_settings_;
...@@ -211,6 +244,10 @@ class MinimumVersionPolicyHandler : public BuildStateObserver { ...@@ -211,6 +244,10 @@ class MinimumVersionPolicyHandler : public BuildStateObserver {
std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> std::unique_ptr<chromeos::CrosSettings::ObserverSubscription>
policy_subscription_; policy_subscription_;
// Handles showing in-session update required notifications on the basis of
// current network and time to reach the deadline.
std::unique_ptr<chromeos::UpdateRequiredNotification> notification_handler_;
// List of registered observers. // List of registered observers.
base::ObserverList<Observer>::Unchecked observers_; base::ObserverList<Observer>::Unchecked observers_;
......
...@@ -5,7 +5,9 @@ ...@@ -5,7 +5,9 @@
#include <string> #include <string>
#include "ash/public/cpp/login_screen_test_api.h" #include "ash/public/cpp/login_screen_test_api.h"
#include "ash/public/cpp/system_tray_test_api.h"
#include "base/json/json_writer.h" #include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/browser_process_platform_part.h"
...@@ -25,6 +27,7 @@ ...@@ -25,6 +27,7 @@
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h" #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/lifetime/application_lifetime.h" #include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/profile_policy_connector.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
...@@ -34,6 +37,8 @@ ...@@ -34,6 +37,8 @@
#include "chromeos/constants/chromeos_switches.h" #include "chromeos/constants/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_update_engine_client.h" #include "chromeos/dbus/fake_update_engine_client.h"
#include "chromeos/dbus/shill/shill_service_client.h"
#include "chromeos/network/network_state_test_helper.h"
#include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/cros_settings_names.h"
#include "components/policy/proto/chrome_device_policy.pb.h" #include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
...@@ -41,6 +46,7 @@ ...@@ -41,6 +46,7 @@
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h" #include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace em = enterprise_management; namespace em = enterprise_management;
...@@ -56,6 +62,8 @@ constexpr base::TimeDelta kShortWarning = ...@@ -56,6 +62,8 @@ constexpr base::TimeDelta kShortWarning =
constexpr base::TimeDelta kLongWarning = constexpr base::TimeDelta kLongWarning =
base::TimeDelta::FromDays(kLongWarningInDays); base::TimeDelta::FromDays(kLongWarningInDays);
const char kPublicSessionId[] = "demo@example.com"; const char kPublicSessionId[] = "demo@example.com";
const char kUpdateRequiredNotificationId[] = "policy.update_required";
const char kWifiServicePath[] = "/service/wifi2";
// This is a randomly chosen long delay in milliseconds to make sure that the // This is a randomly chosen long delay in milliseconds to make sure that the
// timer keeps running for a long time in case it is started. // timer keeps running for a long time in case it is started.
const int kAutoLoginLoginDelayMilliseconds = 500000; const int kAutoLoginLoginDelayMilliseconds = 500000;
...@@ -146,11 +154,36 @@ class MinimumVersionPolicyTest : public MinimumVersionPolicyTestBase { ...@@ -146,11 +154,36 @@ class MinimumVersionPolicyTest : public MinimumVersionPolicyTestBase {
MinimumVersionPolicyTest() { login_manager_.AppendRegularUsers(1); } MinimumVersionPolicyTest() { login_manager_.AppendRegularUsers(1); }
~MinimumVersionPolicyTest() override = default; ~MinimumVersionPolicyTest() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
MinimumVersionPolicyTestBase::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(chromeos::switches::kShillStub,
"clear=1,cellular=0,wifi=0");
}
void SetUpOnMainThread() override {
MinimumVersionPolicyTestBase::SetUpOnMainThread();
display_service_tester_ =
std::make_unique<NotificationDisplayServiceTester>(nullptr /*profile*/);
network_state_test_helper_ =
std::make_unique<chromeos::NetworkStateTestHelper>(
false /*use_default_devices_and_services*/);
network_state_test_helper_->manager_test()->SetupDefaultEnvironment();
tray_test_api_ = ash::SystemTrayTestApi::Create();
}
void TearDownOnMainThread() override {
network_state_test_helper_.reset();
MinimumVersionPolicyTestBase::TearDownOnMainThread();
}
void Login(); void Login();
void MarkUserManaged(); void MarkUserManaged();
protected: protected:
chromeos::LoginManagerMixin login_manager_{&mixin_host_}; chromeos::LoginManagerMixin login_manager_{&mixin_host_};
std::unique_ptr<chromeos::NetworkStateTestHelper> network_state_test_helper_;
std::unique_ptr<NotificationDisplayServiceTester> display_service_tester_;
std::unique_ptr<ash::SystemTrayTestApi> tray_test_api_;
}; };
void MinimumVersionPolicyTest::Login() { void MinimumVersionPolicyTest::Login() {
...@@ -254,6 +287,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) { ...@@ -254,6 +287,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) {
kShortWarning); kShortWarning);
EXPECT_TRUE( EXPECT_TRUE(
GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting()); GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
// Create and set policy value with long warning time. // Create and set policy value with long warning time.
base::Value requirement_long_warning(base::Value::Type::LIST); base::Value requirement_long_warning(base::Value::Type::LIST);
...@@ -266,6 +301,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) { ...@@ -266,6 +301,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) {
timer_start_time); timer_start_time);
EXPECT_EQ(prefs->GetTimeDelta(prefs::kUpdateRequiredWarningPeriod), EXPECT_EQ(prefs->GetTimeDelta(prefs::kUpdateRequiredWarningPeriod),
kLongWarning); kLongWarning);
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
// Create and set policy value with no warning time. // Create and set policy value with no warning time.
base::Value requirement_no_warning(base::Value::Type::LIST); base::Value requirement_no_warning(base::Value::Type::LIST);
...@@ -278,6 +315,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) { ...@@ -278,6 +315,8 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NonCriticalUpdateGoodNetwork) {
timer_start_time); timer_start_time);
EXPECT_EQ(prefs->GetTimeDelta(prefs::kUpdateRequiredWarningPeriod), EXPECT_EQ(prefs->GetTimeDelta(prefs::kUpdateRequiredWarningPeriod),
kLongWarning); kLongWarning);
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
EXPECT_TRUE( EXPECT_TRUE(
GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting()); GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
...@@ -313,6 +352,65 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, ...@@ -313,6 +352,65 @@ IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
EXPECT_FALSE(chrome::IsAttemptingShutdown()); EXPECT_FALSE(chrome::IsAttemptingShutdown());
} }
IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest, NoNetworkNotificationClick) {
// Login the user into the session.
Login();
MarkUserManaged();
// Create policy value as a list of requirements.
base::Value requirement_list(base::Value::Type::LIST);
requirement_list.Append(
CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
EXPECT_FALSE(tray_test_api_->IsTrayBubbleOpen());
// Set new policy value and check that update required notification is shown.
SetDevicePolicyAndWaitForSettingChange(requirement_list);
EXPECT_TRUE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
// Clicking on notification button opens the network settings and hides the
// notification.
display_service_tester_->SimulateClick(NotificationHandler::Type::TRANSIENT,
kUpdateRequiredNotificationId,
0 /*action_index*/, base::nullopt);
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
EXPECT_TRUE(tray_test_api_->IsTrayBubbleOpen());
}
IN_PROC_BROWSER_TEST_F(MinimumVersionPolicyTest,
HideNotificationOnGoodNetwork) {
// Login the user into the session.
Login();
MarkUserManaged();
// Create policy value as a list of requirements.
base::Value requirement_list(base::Value::Type::LIST);
requirement_list.Append(
CreateRequirement(kNewVersion, kShortWarningInDays, kShortWarningInDays));
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
// Set new policy value and check that update required notification is shown.
SetDevicePolicyAndWaitForSettingChange(requirement_list);
EXPECT_TRUE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
// Connecting to WiFi should hide the update required notification.
base::RunLoop run_loop;
display_service_tester_->SetNotificationClosedClosure(run_loop.QuitClosure());
network_state_test_helper_->service_test()->AddService(
kWifiServicePath, kWifiServicePath, kWifiServicePath /* name */,
shill::kTypeWifi, shill::kStateOnline, true /* visible */);
run_loop.Run();
EXPECT_FALSE(
display_service_tester_->GetNotification(kUpdateRequiredNotificationId));
}
class MinimumVersionNoUsersLoginTest : public MinimumVersionPolicyTestBase { class MinimumVersionNoUsersLoginTest : public MinimumVersionPolicyTestBase {
public: public:
MinimumVersionNoUsersLoginTest() = default; MinimumVersionNoUsersLoginTest() = default;
......
...@@ -5,18 +5,26 @@ ...@@ -5,18 +5,26 @@
#include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h" #include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind_test_util.h" #include "base/test/bind_test_util.h"
#include "base/test/task_environment.h" #include "base/test/task_environment.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h"
#include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h" #include "chrome/browser/chromeos/settings/scoped_testing_cros_settings.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/test/base/scoped_testing_local_state.h" #include "chrome/test/base/scoped_testing_local_state.h"
#include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_browser_process.h"
#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_update_engine_client.h" #include "chromeos/dbus/fake_update_engine_client.h"
#include "chromeos/dbus/shill/shill_service_client.h"
#include "chromeos/network/network_handler.h"
#include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/cros_settings_names.h"
#include "chromeos/tpm/stub_install_attributes.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using testing::_; using testing::_;
using testing::Mock; using testing::Mock;
...@@ -32,6 +40,7 @@ const char kNewVersion[] = "81.4.2"; ...@@ -32,6 +40,7 @@ const char kNewVersion[] = "81.4.2";
const char kNewerVersion[] = "81.5.4"; const char kNewerVersion[] = "81.5.4";
const char kNewestVersion[] = "82"; const char kNewestVersion[] = "82";
const char kOldVersion[] = "78.1.5"; const char kOldVersion[] = "78.1.5";
const char kUpdateRequiredNotificationId[] = "policy.update_required";
const int kLongWarning = 10; const int kLongWarning = 10;
const int kShortWarning = 2; const int kShortWarning = 2;
...@@ -78,15 +87,21 @@ class MinimumVersionPolicyHandlerTest ...@@ -78,15 +87,21 @@ class MinimumVersionPolicyHandlerTest
return minimum_version_policy_handler_.get(); return minimum_version_policy_handler_.get();
} }
NotificationDisplayServiceTester* display_service() {
return notification_service_.get();
}
void SetUserManaged(bool managed) { user_managed_ = managed; } void SetUserManaged(bool managed) { user_managed_ = managed; }
base::test::TaskEnvironment task_environment{ content::BrowserTaskEnvironment task_environment{
base::test::TaskEnvironment::TimeSource::MOCK_TIME}; base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private: private:
bool user_managed_ = true; bool user_managed_ = true;
ScopedTestingLocalState local_state_; ScopedTestingLocalState local_state_;
chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_; chromeos::ScopedTestingCrosSettings scoped_testing_cros_settings_;
std::unique_ptr<NotificationDisplayServiceTester> notification_service_;
chromeos::ScopedStubInstallAttributes scoped_stub_install_attributes_;
std::unique_ptr<base::Version> current_version_; std::unique_ptr<base::Version> current_version_;
std::unique_ptr<MinimumVersionPolicyHandler> minimum_version_policy_handler_; std::unique_ptr<MinimumVersionPolicyHandler> minimum_version_policy_handler_;
}; };
...@@ -99,12 +114,33 @@ void MinimumVersionPolicyHandlerTest::SetUp() { ...@@ -99,12 +114,33 @@ void MinimumVersionPolicyHandlerTest::SetUp() {
std::make_unique<chromeos::FakeUpdateEngineClient>(); std::make_unique<chromeos::FakeUpdateEngineClient>();
chromeos::DBusThreadManager::GetSetterForTesting()->SetUpdateEngineClient( chromeos::DBusThreadManager::GetSetterForTesting()->SetUpdateEngineClient(
std::move(fake_update_engine_client)); std::move(fake_update_engine_client));
chromeos::NetworkHandler::Initialize();
chromeos::ShillServiceClient::TestInterface* service_test =
chromeos::DBusThreadManager::Get()
->GetShillServiceClient()
->GetTestInterface();
service_test->ClearServices();
service_test->AddService("/service/eth", "eth" /* guid */, "eth",
shill::kTypeEthernet, shill::kStateOnline,
true /* visible */);
base::RunLoop().RunUntilIdle();
scoped_stub_install_attributes_.Get()->SetCloudManaged("managed.com",
"device_id");
TestingBrowserProcess::GetGlobal()->SetSystemNotificationHelper(
std::make_unique<SystemNotificationHelper>());
notification_service_ =
std::make_unique<NotificationDisplayServiceTester>(nullptr /*profile*/);
CreateMinimumVersionHandler(); CreateMinimumVersionHandler();
SetCurrentVersionString(kFakeCurrentVersion); SetCurrentVersionString(kFakeCurrentVersion);
} }
void MinimumVersionPolicyHandlerTest::TearDown() {} void MinimumVersionPolicyHandlerTest::TearDown() {
minimum_version_policy_handler_.reset();
chromeos::NetworkHandler::Shutdown();
}
void MinimumVersionPolicyHandlerTest::CreateMinimumVersionHandler() { void MinimumVersionPolicyHandlerTest::CreateMinimumVersionHandler() {
minimum_version_policy_handler_.reset( minimum_version_policy_handler_.reset(
...@@ -340,4 +376,55 @@ TEST_F(MinimumVersionPolicyHandlerTest, DeadlineTimerExpired) { ...@@ -340,4 +376,55 @@ TEST_F(MinimumVersionPolicyHandlerTest, DeadlineTimerExpired) {
EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied()); EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
} }
TEST_F(MinimumVersionPolicyHandlerTest, NoNetworkNotifications) {
EXPECT_TRUE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
// Disconnect all networks
chromeos::ShillServiceClient::TestInterface* service_test =
chromeos::DBusThreadManager::Get()
->GetShillServiceClient()
->GetTestInterface();
service_test->ClearServices();
// This is needed to wait till EOL status is fetched from the update_engine.
base::RunLoop run_loop;
GetMinimumVersionPolicyHandler()->set_fetch_eol_callback_for_testing(
run_loop.QuitClosure());
// Create and set pref value to invoke policy handler.
base::Value requirement_list(base::Value::Type::LIST);
requirement_list.Append(
CreateRequirement(kNewVersion, kLongWarning, kLongWarning));
SetPolicyPref(std::move(requirement_list));
run_loop.Run();
EXPECT_TRUE(
GetMinimumVersionPolicyHandler()->IsDeadlineTimerRunningForTesting());
EXPECT_FALSE(GetMinimumVersionPolicyHandler()->RequirementsAreSatisfied());
// Check notification is shown for offline devices with the warning time.
base::string16 expected_title =
base::ASCIIToUTF16("Update device within 10 days");
base::string16 expected_message = base::ASCIIToUTF16(
"managed.com requires you to download an update before the deadline. The "
"update will download automatically when you connect to the internet.");
auto notification_long_waiting =
display_service()->GetNotification(kUpdateRequiredNotificationId);
ASSERT_TRUE(notification_long_waiting);
EXPECT_EQ(notification_long_waiting->title(), expected_title);
EXPECT_EQ(notification_long_waiting->message(), expected_message);
// Expire the notification timer to show new notification on the last day.
const base::TimeDelta warning = base::TimeDelta::FromDays(kLongWarning - 1);
task_environment.FastForwardBy(warning);
base::string16 expected_title_last_day =
base::ASCIIToUTF16("Last day to update device");
auto notification_last_day =
display_service()->GetNotification(kUpdateRequiredNotificationId);
ASSERT_TRUE(notification_long_waiting);
EXPECT_EQ(notification_last_day->title(), expected_title_last_day);
EXPECT_EQ(notification_last_day->message(), expected_message);
}
} // namespace policy } // 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.
#include "chrome/browser/chromeos/ui/update_required_notification.h"
#include "ash/public/cpp/notification_utils.h"
#include "base/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/notifications/system_notification_helper.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
using NotificationType = policy::MinimumVersionPolicyHandler::NotificationType;
namespace chromeos {
namespace {
const char kUpdateRequiredNotificationId[] = "policy.update_required";
base::string16 GetTitle(NotificationType type, int days_remaining) {
// TODO(https://crbug.com/1048607): Add and use title strings for weeks if
// |days_remaining| >= 7.
switch (type) {
case NotificationType::kNoConnection:
return days_remaining > 1
? l10n_util::GetStringFUTF16Int(
IDS_UPDATE_REQUIRED_NO_NETWORK_TITLE_DAYS,
days_remaining)
: l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_NO_NETWORK_TITLE_IMMEDIATE);
case NotificationType::kMeteredConnection:
case NotificationType::kEolReached:
return base::string16();
}
}
base::string16 GetMessage(NotificationType type,
const std::string& domain_name) {
switch (type) {
case NotificationType::kNoConnection:
return l10n_util::GetStringFUTF16(IDS_UPDATE_REQUIRED_NO_NETWORK_MESSAGE,
base::UTF8ToUTF16(domain_name));
case NotificationType::kMeteredConnection:
case NotificationType::kEolReached:
return base::string16();
}
}
base::string16 GetButtonText(NotificationType type) {
switch (type) {
case NotificationType::kNoConnection:
return l10n_util::GetStringUTF16(
IDS_UPDATE_REQUIRED_SCREEN_OPEN_NETWORK_SETTINGS);
case NotificationType::kMeteredConnection:
case NotificationType::kEolReached:
return base::string16();
}
}
message_center::NotificationPriority GetNotificationPriority(
int days_remaining) {
return days_remaining > 1 ? message_center::HIGH_PRIORITY
: message_center::SYSTEM_PRIORITY;
}
message_center::SystemNotificationWarningLevel GetWarningLevel(
int days_remaining) {
return days_remaining > 1
? message_center::SystemNotificationWarningLevel::WARNING
: message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
}
} // namespace
UpdateRequiredNotification::UpdateRequiredNotification() = default;
UpdateRequiredNotification::~UpdateRequiredNotification() = default;
void UpdateRequiredNotification::Show(NotificationType type,
base::TimeDelta warning_time,
const std::string& domain_name,
base::OnceClosure button_click_callback,
base::OnceClosure close_callback) {
const int days_remaining = warning_time.InDays();
notification_button_click_callback_ = std::move(button_click_callback);
notification_close_callback_ = std::move(close_callback);
base::string16 title = GetTitle(type, days_remaining);
base::string16 body = GetMessage(type, domain_name);
base::string16 button = GetButtonText(type);
if (title.empty() || body.empty() || button.empty()) {
NOTREACHED();
return;
}
DisplayNotification(title, body, button, GetWarningLevel(days_remaining),
GetNotificationPriority(days_remaining));
}
void UpdateRequiredNotification::DisplayNotification(
const base::string16& title,
const base::string16& message,
const base::string16& button_text,
message_center::SystemNotificationWarningLevel color_type,
message_center::NotificationPriority priority) {
message_center::RichNotificationData data;
data.buttons.push_back(message_center::ButtonInfo(button_text));
std::unique_ptr<message_center::Notification> notification =
ash::CreateSystemNotification(
message_center::NOTIFICATION_TYPE_SIMPLE,
kUpdateRequiredNotificationId, title, message,
base::string16() /*display_source*/, GURL(),
message_center::NotifierId(
message_center::NotifierType::SYSTEM_COMPONENT,
kUpdateRequiredNotificationId),
data,
base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
weak_factory_.GetWeakPtr()),
vector_icons::kBusinessIcon, color_type);
notification->set_priority(priority);
SystemNotificationHelper::GetInstance()->Display(*notification);
}
void UpdateRequiredNotification::Hide() {
SystemNotificationHelper::GetInstance()->Close(kUpdateRequiredNotificationId);
}
void UpdateRequiredNotification::Close(bool by_user) {
if (!notification_close_callback_.is_null())
std::move(notification_close_callback_).Run();
}
void UpdateRequiredNotification::Click(
const base::Optional<int>& button_index,
const base::Optional<base::string16>& reply) {
// |button_index| may be empty if the notification body was clicked.
if (!button_index)
return;
Hide();
if (!notification_button_click_callback_.is_null())
std::move(notification_button_click_callback_).Run();
}
} // namespace chromeos
// 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_UI_UPDATE_REQUIRED_NOTIFICATION_H_
#define CHROME_BROWSER_CHROMEOS_UI_UPDATE_REQUIRED_NOTIFICATION_H_
#include "base/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/policy/minimum_version_policy_handler.h"
#include "ui/message_center/public/cpp/notification.h"
#include "ui/message_center/public/cpp/notification_delegate.h"
namespace chromeos {
// UpdateRequiredNotification manages in-session notifications informing the
// user that update is required as per admin policy but it cannot be initiated
// either due to network limitations or because of the device having reached its
// end-of-life state.
class UpdateRequiredNotification : public message_center::NotificationObserver {
public:
UpdateRequiredNotification();
UpdateRequiredNotification(const UpdateRequiredNotification&) = delete;
UpdateRequiredNotification& operator=(const UpdateRequiredNotification&) =
delete;
virtual ~UpdateRequiredNotification();
// message_center::NotificationObserver:
void Close(bool by_user) override;
void Click(const base::Optional<int>& button_index,
const base::Optional<base::string16>& reply) override;
// Collects notification data like title, body, button text, priority on the
// basis of |type| and |warning_time|. Sets the |button_click_callback| to be
// invoked when the notification button is clicked and |close_callback| to be
// invoked when the notification is closed.
void Show(policy::MinimumVersionPolicyHandler::NotificationType type,
base::TimeDelta warning_time,
const std::string& domain_name,
base::OnceClosure button_click_callback,
base::OnceClosure close_callback);
void Hide();
private:
// Creates and displays a new notification.
void DisplayNotification(
const base::string16& title,
const base::string16& message,
const base::string16& button_text,
const message_center::SystemNotificationWarningLevel color_type,
const message_center::NotificationPriority priority);
// Callback to be invoked when the notification button is clicked.
base::OnceClosure notification_button_click_callback_;
// Callback to be invoked when the notification is closed.
base::OnceClosure notification_close_callback_;
base::WeakPtrFactory<UpdateRequiredNotification> weak_factory_{this};
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_UI_UPDATE_REQUIRED_NOTIFICATION_H_
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