Commit 0909cc52 authored by Roman Aleksandrov's avatar Roman Aleksandrov Committed by Commit Bot

Relaunch Notification: Adjust deadline

Change schedule of the notification so the deadline takes place between
2am and 4am local time.

Bug: 968188
Change-Id: Ie55e9f50cf365ef384fd3c7d9faaef6a0886ab91
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1635252
Commit-Queue: Roman Aleksandrov <raleksandrov@google.com>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#666030}
parent 59b7346e
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <stdint.h> #include <stdint.h>
#include <algorithm>
#include <utility> #include <utility>
#include "base/bind.h" #include "base/bind.h"
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "base/rand_util.h"
#include "base/time/clock.h" #include "base/time/clock.h"
#include "base/time/default_clock.h" #include "base/time/default_clock.h"
#include "base/time/default_tick_clock.h" #include "base/time/default_tick_clock.h"
...@@ -22,8 +24,10 @@ ...@@ -22,8 +24,10 @@
#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/dbus/update_engine_client.h" #include "chromeos/dbus/update_engine_client.h"
#include "chromeos/settings/timezone_settings.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "third_party/icu/source/i18n/unicode/timezone.h"
using chromeos::DBusThreadManager; using chromeos::DBusThreadManager;
using chromeos::UpdateEngineClient; using chromeos::UpdateEngineClient;
...@@ -118,7 +122,6 @@ UpgradeDetectorChromeos::UpgradeDetectorChromeos( ...@@ -118,7 +122,6 @@ UpgradeDetectorChromeos::UpgradeDetectorChromeos(
upgrade_notification_timer_(tick_clock), upgrade_notification_timer_(tick_clock),
initialized_(false), initialized_(false),
weak_factory_(this) { weak_factory_(this) {
CalculateThresholds();
// Not all tests provide a PrefService for local_state(). // Not all tests provide a PrefService for local_state().
PrefService* local_state = g_browser_process->local_state(); PrefService* local_state = g_browser_process->local_state();
if (local_state) { if (local_state) {
...@@ -157,14 +160,11 @@ void UpgradeDetectorChromeos::Shutdown() { ...@@ -157,14 +160,11 @@ void UpgradeDetectorChromeos::Shutdown() {
} }
base::TimeDelta UpgradeDetectorChromeos::GetHighAnnoyanceLevelDelta() { base::TimeDelta UpgradeDetectorChromeos::GetHighAnnoyanceLevelDelta() {
return high_threshold_ - elevated_threshold_; return high_deadline_ - elevated_deadline_;
} }
base::Time UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() { base::Time UpgradeDetectorChromeos::GetHighAnnoyanceDeadline() {
const base::Time detected_time = upgrade_detected_time(); return high_deadline_;
if (detected_time.is_null())
return detected_time;
return detected_time + high_threshold_;
} }
// static // static
...@@ -183,15 +183,63 @@ base::TimeDelta UpgradeDetectorChromeos::GetRelaunchHeadsUpPeriod() { ...@@ -183,15 +183,63 @@ base::TimeDelta UpgradeDetectorChromeos::GetRelaunchHeadsUpPeriod() {
return base::TimeDelta::FromMilliseconds(value); return base::TimeDelta::FromMilliseconds(value);
} }
void UpgradeDetectorChromeos::CalculateThresholds() { // static
base::TimeDelta UpgradeDetectorChromeos::GenRandomTimeDelta(
base::TimeDelta max) {
return max * base::RandDouble();
}
// static
base::Time UpgradeDetectorChromeos::AdjustDeadline(base::Time deadline) {
// Compute the offset applied to GMT to get local time at |deadline|.
const icu::TimeZone& time_zone =
chromeos::system::TimezoneSettings::GetInstance()->GetTimezone();
UErrorCode status = U_ZERO_ERROR;
int32_t raw_offset, dst_offset;
time_zone.getOffset(deadline.ToDoubleT() * base::Time::kMillisecondsPerSecond,
true /* local */, raw_offset, dst_offset, status);
base::TimeDelta time_zone_offset;
if (U_FAILURE(status)) {
LOG(ERROR) << "Failed to get time zone offset, error code: " << status;
// The fallback case is to get the raw timezone offset ignoring the daylight
// saving time.
time_zone_offset =
base::TimeDelta::FromMilliseconds(time_zone.getRawOffset());
} else {
time_zone_offset =
base::TimeDelta::FromMilliseconds(raw_offset + dst_offset);
}
// To get local midnight add timezone offset to deadline and treat this time
// as UTC based to use UTCMidnight(), then subtract timezone offset.
auto midnight =
(deadline + time_zone_offset).UTCMidnight() - time_zone_offset;
const auto day_time = deadline - midnight;
// Return the exact deadline if it naturally falls between 2am and 4am.
if (day_time >= base::TimeDelta::FromHours(2) &&
day_time <= base::TimeDelta::FromHours(4)) {
return deadline;
}
// Advance to the next day if the deadline falls after 4am.
if (day_time > base::TimeDelta::FromHours(4))
midnight += base::TimeDelta::FromDays(1);
return midnight + base::TimeDelta::FromHours(2) +
GenRandomTimeDelta(base::TimeDelta::FromHours(2));
}
void UpgradeDetectorChromeos::CalculateDeadlines() {
base::TimeDelta notification_period = GetRelaunchNotificationPeriod(); base::TimeDelta notification_period = GetRelaunchNotificationPeriod();
high_threshold_ = notification_period.is_zero() ? kDefaultHighThreshold if (notification_period.is_zero())
: notification_period; notification_period = kDefaultHighThreshold;
high_deadline_ =
AdjustDeadline(upgrade_detected_time() + notification_period);
base::TimeDelta heads_up_period = GetRelaunchHeadsUpPeriod(); base::TimeDelta heads_up_period = GetRelaunchHeadsUpPeriod();
if (heads_up_period.is_zero()) if (heads_up_period.is_zero())
heads_up_period = kDefaultHeadsUpPeriod; heads_up_period = kDefaultHeadsUpPeriod;
elevated_threshold_ = elevated_deadline_ =
high_threshold_ - std::min(heads_up_period, high_threshold_); std::max(high_deadline_ - heads_up_period, upgrade_detected_time());
} }
void UpgradeDetectorChromeos::OnRelaunchHeadsUpPeriodPrefChanged() { void UpgradeDetectorChromeos::OnRelaunchHeadsUpPeriodPrefChanged() {
...@@ -215,8 +263,10 @@ void UpgradeDetectorChromeos::OnRelaunchNotificationPeriodPrefChanged() { ...@@ -215,8 +263,10 @@ void UpgradeDetectorChromeos::OnRelaunchNotificationPeriodPrefChanged() {
void UpgradeDetectorChromeos::UpdateStatusChanged( void UpgradeDetectorChromeos::UpdateStatusChanged(
const UpdateEngineClient::Status& status) { const UpdateEngineClient::Status& status) {
if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) { if (status.status == UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT) {
if (upgrade_detected_time().is_null()) if (upgrade_detected_time().is_null()) {
set_upgrade_detected_time(clock()->Now()); set_upgrade_detected_time(clock()->Now());
CalculateDeadlines();
}
if (status.is_rollback) { if (status.is_rollback) {
// Powerwash will be required, determine what kind of notification to show // Powerwash will be required, determine what kind of notification to show
...@@ -246,32 +296,33 @@ void UpgradeDetectorChromeos::OnUpdateOverCellularOneTimePermissionGranted() { ...@@ -246,32 +296,33 @@ void UpgradeDetectorChromeos::OnUpdateOverCellularOneTimePermissionGranted() {
void UpgradeDetectorChromeos::OnThresholdPrefChanged() { void UpgradeDetectorChromeos::OnThresholdPrefChanged() {
// Check the current stage and potentially notify observers now if a change to // Check the current stage and potentially notify observers now if a change to
// the observed policies results in changes to the thresholds. // the observed policies results in changes to the thresholds.
const base::TimeDelta old_elevated_threshold = elevated_threshold_; if (upgrade_detected_time().is_null())
const base::TimeDelta old_high_threshold = high_threshold_; return;
CalculateThresholds(); const base::Time old_elevated_deadline = elevated_deadline_;
if (!upgrade_detected_time().is_null() && const base::Time old_high_deadline = high_deadline_;
(elevated_threshold_ != old_elevated_threshold || CalculateDeadlines();
high_threshold_ != old_high_threshold)) { if (elevated_deadline_ != old_elevated_deadline ||
high_deadline_ != old_high_deadline) {
NotifyOnUpgrade(); NotifyOnUpgrade();
} }
} }
void UpgradeDetectorChromeos::NotifyOnUpgrade() { void UpgradeDetectorChromeos::NotifyOnUpgrade() {
const base::TimeDelta delta = clock()->Now() - upgrade_detected_time(); const base::Time current_time = clock()->Now();
// The delay from now until the next highest notification stage is reached, or // The delay from now until the next highest notification stage is reached, or
// zero if the highest notification stage has been reached. // zero if the highest notification stage has been reached.
base::TimeDelta next_delay; base::TimeDelta next_delay;
const auto last_stage = upgrade_notification_stage(); const auto last_stage = upgrade_notification_stage();
// These if statements must be sorted (highest interval first). // These if statements must be sorted (highest interval first).
if (delta >= high_threshold_) { if (current_time >= high_deadline_) {
set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH); set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
} else if (delta >= elevated_threshold_) { } else if (current_time >= elevated_deadline_) {
set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED); set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
next_delay = high_threshold_ - delta; next_delay = high_deadline_ - current_time;
} else { } else {
set_upgrade_notification_stage(UPGRADE_ANNOYANCE_NONE); set_upgrade_notification_stage(UPGRADE_ANNOYANCE_NONE);
next_delay = elevated_threshold_ - delta; next_delay = elevated_deadline_ - current_time;
} }
const auto new_stage = upgrade_notification_stage(); const auto new_stage = upgrade_notification_stage();
......
...@@ -49,24 +49,29 @@ class UpgradeDetectorChromeos : public UpgradeDetector, ...@@ -49,24 +49,29 @@ class UpgradeDetectorChromeos : public UpgradeDetector,
UpgradeDetectorChromeos(const base::Clock* clock, UpgradeDetectorChromeos(const base::Clock* clock,
const base::TickClock* tick_clock); const base::TickClock* tick_clock);
// Return adjusted high annoyance deadline which takes place at night between
// 2am and 4am. If |deadline| takes place after 4am it is prolonged for the
// next day night between 2am and 4am.
static base::Time AdjustDeadline(base::Time deadline);
private: private:
friend class base::NoDestructor<UpgradeDetectorChromeos>; friend class base::NoDestructor<UpgradeDetectorChromeos>;
// Return random TimeDelta uniformly selected between zero and |max|.
static base::TimeDelta GenRandomTimeDelta(base::TimeDelta max);
// Returns the period between first notification and Recommended / Required // Returns the period between first notification and Recommended / Required
// deadline specified via the RelaunchHeadsUpPeriod policy setting, or a // deadline specified via the RelaunchHeadsUpPeriod policy setting, or a
// zero delta if unset or out of range. // zero delta if unset or out of range.
static base::TimeDelta GetRelaunchHeadsUpPeriod(); static base::TimeDelta GetRelaunchHeadsUpPeriod();
// Calculates |elevated_threshold_| and |high_threshold_|. // Calculates |elevated_deadline_| and |high_deadline_|.
void CalculateThresholds(); void CalculateDeadlines();
// Handles a change to the browser.relaunch_heads_up_period Local State // Handles a change to the browser.relaunch_heads_up_period Local State
// preference. Calls NotifyUpgrade if an upgrade is available. // preference. Calls NotifyUpgrade if an upgrade is available.
void OnRelaunchHeadsUpPeriodPrefChanged(); void OnRelaunchHeadsUpPeriodPrefChanged();
// Returns the threshold to reach high annoyance level.
static base::TimeDelta DetermineHighThreshold();
// UpgradeDetector: // UpgradeDetector:
void OnRelaunchNotificationPeriodPrefChanged() override; void OnRelaunchNotificationPeriodPrefChanged() override;
...@@ -86,11 +91,11 @@ class UpgradeDetectorChromeos : public UpgradeDetector, ...@@ -86,11 +91,11 @@ class UpgradeDetectorChromeos : public UpgradeDetector,
void OnChannelsReceived(std::string current_channel, void OnChannelsReceived(std::string current_channel,
std::string target_channel); std::string target_channel);
// The delta from upgrade detection until elevated annoyance level is reached. // The time when elevated annoyance deadline is reached.
base::TimeDelta elevated_threshold_; base::Time elevated_deadline_;
// The delta from upgrade detection until high annoyance level is reached. // The time when high annoyance deadline is reached.
base::TimeDelta high_threshold_; base::Time high_deadline_;
// Observes changes to the browser.relaunch_heads_up_period Local State // Observes changes to the browser.relaunch_heads_up_period Local State
// preference. // preference.
......
...@@ -20,9 +20,11 @@ ...@@ -20,9 +20,11 @@
#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/settings/timezone_settings.h"
#include "components/prefs/testing_pref_service.h" #include "components/prefs/testing_pref_service.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/icu/source/i18n/unicode/timezone.h"
namespace { namespace {
...@@ -34,6 +36,7 @@ class TestUpgradeDetectorChromeos : public UpgradeDetectorChromeos { ...@@ -34,6 +36,7 @@ class TestUpgradeDetectorChromeos : public UpgradeDetectorChromeos {
~TestUpgradeDetectorChromeos() override = default; ~TestUpgradeDetectorChromeos() override = default;
// Exposed for testing. // Exposed for testing.
using UpgradeDetectorChromeos::AdjustDeadline;
using UpgradeDetectorChromeos::UPGRADE_AVAILABLE_REGULAR; using UpgradeDetectorChromeos::UPGRADE_AVAILABLE_REGULAR;
DISALLOW_COPY_AND_ASSIGN(TestUpgradeDetectorChromeos); DISALLOW_COPY_AND_ASSIGN(TestUpgradeDetectorChromeos);
...@@ -63,7 +66,8 @@ class MockUpgradeObserver : public UpgradeObserver { ...@@ -63,7 +66,8 @@ class MockUpgradeObserver : public UpgradeObserver {
class UpgradeDetectorChromeosTest : public ::testing::Test { class UpgradeDetectorChromeosTest : public ::testing::Test {
protected: protected:
UpgradeDetectorChromeosTest() UpgradeDetectorChromeosTest()
: scoped_task_environment_( : utc_(icu::TimeZone::createTimeZone("Etc/GMT")),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME), base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME),
scoped_local_state_(TestingBrowserProcess::GetGlobal()) { scoped_local_state_(TestingBrowserProcess::GetGlobal()) {
// Disable the detector's check to see if autoupdates are inabled. // Disable the detector's check to see if autoupdates are inabled.
...@@ -78,6 +82,10 @@ class UpgradeDetectorChromeosTest : public ::testing::Test { ...@@ -78,6 +82,10 @@ class UpgradeDetectorChromeosTest : public ::testing::Test {
dbus_setter->SetUpdateEngineClient( dbus_setter->SetUpdateEngineClient(
std::unique_ptr<chromeos::UpdateEngineClient>( std::unique_ptr<chromeos::UpdateEngineClient>(
fake_update_engine_client_)); fake_update_engine_client_));
// Set UTC timezone
chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*utc_);
// Fast forward to align deadline be at 2am
FastForwardBy(base::TimeDelta::FromHours(2));
} }
const base::Clock* GetMockClock() { const base::Clock* GetMockClock() {
...@@ -132,6 +140,7 @@ class UpgradeDetectorChromeosTest : public ::testing::Test { ...@@ -132,6 +140,7 @@ class UpgradeDetectorChromeosTest : public ::testing::Test {
} }
private: private:
std::unique_ptr<icu::TimeZone> utc_;
base::test::ScopedTaskEnvironment scoped_task_environment_; base::test::ScopedTaskEnvironment scoped_task_environment_;
ScopedTestingLocalState scoped_local_state_; ScopedTestingLocalState scoped_local_state_;
...@@ -359,3 +368,59 @@ TEST_F(UpgradeDetectorChromeosTest, OnUpgradeRecommendedCalledOnce) { ...@@ -359,3 +368,59 @@ TEST_F(UpgradeDetectorChromeosTest, OnUpgradeRecommendedCalledOnce) {
upgrade_detector.Shutdown(); upgrade_detector.Shutdown();
RunUntilIdle(); RunUntilIdle();
} }
TEST_F(UpgradeDetectorChromeosTest, TimezoneAdjustment) {
TestUpgradeDetectorChromeos upgrade_detector(GetMockClock(),
GetMockTickClock());
upgrade_detector.Init();
const auto delta = base::TimeDelta::FromDays(7);
// Europe/Moscow timezone
std::unique_ptr<icu::TimeZone> msk_timezone(
icu::TimeZone::createTimeZone("Europe/Moscow"));
chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(*msk_timezone);
base::Time detect_time;
ASSERT_TRUE(
base::Time::FromString("1 Jan 2018 06:00 UTC+0300", &detect_time));
base::Time deadline, deadline_lower_border, deadline_upper_border;
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+0300",
&deadline_lower_border));
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+0300",
&deadline_upper_border));
deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
EXPECT_GE(deadline, deadline_lower_border);
EXPECT_LE(deadline, deadline_upper_border);
// Pacific/Midway timezone
std::unique_ptr<icu::TimeZone> midway_timezone(
icu::TimeZone::createTimeZone("Pacific/Midway"));
chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(
*midway_timezone);
ASSERT_TRUE(
base::Time::FromString("1 Jan 2018 23:00 UTC-1100", &detect_time));
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC-1100",
&deadline_lower_border));
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC-1100",
&deadline_upper_border));
deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
EXPECT_GE(deadline, deadline_lower_border);
EXPECT_LE(deadline, deadline_upper_border);
// Pacific/Kiritimati timezone
std::unique_ptr<icu::TimeZone> kiritimati_timezone(
icu::TimeZone::createTimeZone("Pacific/Kiritimati"));
chromeos::system::TimezoneSettings::GetInstance()->SetTimezone(
*kiritimati_timezone);
ASSERT_TRUE(
base::Time::FromString("1 Jan 2018 16:30 UTC+1400", &detect_time));
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 02:00 UTC+1400",
&deadline_lower_border));
ASSERT_TRUE(base::Time::FromString("9 Jan 2018 04:00 UTC+1400",
&deadline_upper_border));
deadline = upgrade_detector.AdjustDeadline(detect_time + delta);
EXPECT_GE(deadline, deadline_lower_border);
EXPECT_LE(deadline, deadline_upper_border);
upgrade_detector.Shutdown();
RunUntilIdle();
}
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