blob: 08b69c4f586f58f8066f62a7ad12dcc7f724caf5 [file] [log] [blame]
xiaoyinhf39e3dd2016-06-18 04:50:231// Copyright 2016 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/eol_notification.h"
6
Evan Stade70e2ed42018-11-08 06:23:057#include "ash/public/cpp/notification_utils.h"
Sebastien Marchandf1349f52019-01-25 03:16:418#include "base/bind.h"
Regan Hsu62dc18f2019-10-03 20:30:449#include "base/i18n/time_formatting.h"
10#include "base/time/default_clock.h"
Evan Stade82ba4b62019-07-11 01:58:0111#include "chrome/app/vector_icons/vector_icons.h"
xiaoyinhf39e3dd2016-06-18 04:50:2312#include "chrome/browser/browser_process.h"
Hans Wennborg63344452019-10-15 10:15:2113#include "chrome/browser/browser_process_platform_part.h"
Sarah Hu4ad394b2017-11-27 19:03:0014#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
Evan Stadecc63b182017-09-26 16:05:1215#include "chrome/browser/notifications/notification_display_service.h"
16#include "chrome/browser/notifications/notification_display_service_factory.h"
xiaoyinhf39e3dd2016-06-18 04:50:2317#include "chrome/browser/ui/browser_navigator.h"
18#include "chrome/browser/ui/browser_navigator_params.h"
19#include "chrome/common/pref_names.h"
20#include "chrome/common/url_constants.h"
21#include "chrome/grit/generated_resources.h"
22#include "chromeos/dbus/dbus_thread_manager.h"
xiaoyinhf39e3dd2016-06-18 04:50:2323#include "components/prefs/pref_service.h"
dpapad943ef182018-01-20 02:58:2024#include "components/strings/grit/components_strings.h"
Regan Hsu62dc18f2019-10-03 20:30:4425#include "components/vector_icons/vector_icons.h"
xiaoyinhf39e3dd2016-06-18 04:50:2326#include "ui/base/l10n/l10n_util.h"
Vladislav Kaznacheeve9805222019-05-28 19:44:4027#include "ui/chromeos/devicetype_utils.h"
xiaoyinhaa17d8342016-06-30 22:56:0228#include "ui/gfx/color_palette.h"
29#include "ui/gfx/paint_vector_icon.h"
xiaoyinhf39e3dd2016-06-18 04:50:2330
Tetsui Ohkuboe8f95e82017-08-23 06:10:4731using l10n_util::GetStringUTF16;
xiaoyinhf39e3dd2016-06-18 04:50:2332
33namespace chromeos {
34namespace {
35
Evan Stadecc63b182017-09-26 16:05:1236const char kEolNotificationId[] = "chrome://product_eol";
xiaoyinhf39e3dd2016-06-18 04:50:2337
Regan Hsu62dc18f2019-10-03 20:30:4438constexpr int kFirstWarningDaysInAdvance = 180;
39constexpr int kSecondWarningDaysInAdvance = 90;
Evan Stadecc63b182017-09-26 16:05:1240
Regan Hsu62dc18f2019-10-03 20:30:4441base::Time FirstWarningDate(base::Time eol_date) {
42 return eol_date - base::TimeDelta::FromDays(kFirstWarningDaysInAdvance);
43}
Evan Stadecf27dae12017-11-07 23:57:5144
Regan Hsu62dc18f2019-10-03 20:30:4445base::Time SecondWarningDate(const base::Time& eol_date) {
46 return eol_date - base::TimeDelta::FromDays(kSecondWarningDaysInAdvance);
47}
Evan Stadecf27dae12017-11-07 23:57:5148
Regan Hsu62dc18f2019-10-03 20:30:4449base::string16 FormatMonthAndYearWithOffset(base::Time eol_date) {
50 // TODO(crbug/998983): This is not the ideal way to correct months shifts.
51 // A follow up CL will modify base/i18n/time_formatting.h so that
52 // setting the time can be formatted based off UTC rather than only local.
53 //
54 // If the EOL date is on the first day of the month, then notifications with
55 // different month names may be shown to different users by
56 // base::TimeFormatMonthAndYear(), depending on their time zone. There are
57 // devices in Goldeneye with EOL dates on the first and last day of the month.
58 // Since only the month is shown, the day is set to the 15th to prevent any
59 // forward or backward month shifts.
60 constexpr int kApproxMidPointDayInMonth = 15;
Evan Stadee0921092018-04-04 21:13:0961
Regan Hsu62dc18f2019-10-03 20:30:4462 base::Time adjusted_date;
63 base::Time::Exploded exploded;
64 eol_date.UTCExplode(&exploded);
65 exploded.day_of_month = kApproxMidPointDayInMonth;
66 if (!base::Time::FromUTCExploded(exploded, &adjusted_date)) {
67 return base::TimeFormatMonthAndYear(eol_date);
Evan Stadecc63b182017-09-26 16:05:1268 }
Regan Hsu62dc18f2019-10-03 20:30:4469 return base::TimeFormatMonthAndYear(adjusted_date);
70}
xiaoyinhf39e3dd2016-06-18 04:50:2371
xiaoyinhf39e3dd2016-06-18 04:50:2372} // namespace
73
Sarah Hu4ad394b2017-11-27 19:03:0074// static
75bool EolNotification::ShouldShowEolNotification() {
Sarah Hu4ad394b2017-11-27 19:03:0076 // Do not show end of life notification if this device is managed by
77 // enterprise user.
78 if (g_browser_process->platform_part()
79 ->browser_policy_connector_chromeos()
80 ->IsEnterpriseManaged()) {
81 return false;
82 }
83
84 return true;
85}
86
xiaoyinhf39e3dd2016-06-18 04:50:2387EolNotification::EolNotification(Profile* profile)
Regan Hsu62dc18f2019-10-03 20:30:4488 : clock_(base::DefaultClock::GetInstance()), profile_(profile) {}
xiaoyinhf39e3dd2016-06-18 04:50:2389
90EolNotification::~EolNotification() {}
91
Regan Hsu62dc18f2019-10-03 20:30:4492void EolNotification::CheckEolInfo() {
xiaoyinhf39e3dd2016-06-18 04:50:2393 UpdateEngineClient* update_engine_client =
94 DBusThreadManager::Get()->GetUpdateEngineClient();
95
Regan Hsu62dc18f2019-10-03 20:30:4496 // Request the Eol Info.
97 update_engine_client->GetEolInfo(base::BindOnce(
98 &EolNotification::OnEolInfo, weak_ptr_factory_.GetWeakPtr()));
xiaoyinhf39e3dd2016-06-18 04:50:2399}
100
Regan Hsu62dc18f2019-10-03 20:30:44101void EolNotification::OnEolInfo(UpdateEngineClient::EolInfo eol_info) {
102 // Do not show warning Eol notification if invalid |eol_info.eol_date|.
103 if (eol_info.eol_date.is_null())
xiaoyinhf39e3dd2016-06-18 04:50:23104 return;
xiaoyinhf39e3dd2016-06-18 04:50:23105
Regan Hsu62dc18f2019-10-03 20:30:44106 const base::Time now = clock_->Now();
107 const base::Time eol_date = eol_info.eol_date;
108 const base::Time prev_eol_date =
109 profile_->GetPrefs()->GetTime(prefs::kEndOfLifeDate);
110
111 profile_->GetPrefs()->SetTime(prefs::kEndOfLifeDate, eol_date);
112
113 if (!now.is_null() && eol_date != prev_eol_date && now < eol_date) {
114 // Reset showed warning prefs if the Eol date changed.
115 profile_->GetPrefs()->SetBoolean(prefs::kFirstEolWarningDismissed, false);
116 profile_->GetPrefs()->SetBoolean(prefs::kSecondEolWarningDismissed, false);
xiaoyinhf39e3dd2016-06-18 04:50:23117 profile_->GetPrefs()->SetBoolean(prefs::kEolNotificationDismissed, false);
118 }
119
Regan Hsu62dc18f2019-10-03 20:30:44120 if (eol_date <= now) {
121 dismiss_pref_ = prefs::kEolNotificationDismissed;
122 } else if (SecondWarningDate(eol_date) <= now) {
123 dismiss_pref_ = prefs::kSecondEolWarningDismissed;
124 } else if (FirstWarningDate(eol_date) <= now) {
125 dismiss_pref_ = prefs::kFirstEolWarningDismissed;
126 } else {
127 // |now| < FirstWarningDate() so don't show anything.
128 dismiss_pref_ = base::nullopt;
129 return;
130 }
131
132 // Do not show if notification has already been dismissed or is out of range.
133 if (!dismiss_pref_ || profile_->GetPrefs()->GetBoolean(*dismiss_pref_))
xiaoyinhf39e3dd2016-06-18 04:50:23134 return;
135
Regan Hsu62dc18f2019-10-03 20:30:44136 CreateNotification(eol_date, now);
xiaoyinhf39e3dd2016-06-18 04:50:23137}
138
Regan Hsu62dc18f2019-10-03 20:30:44139void EolNotification::CreateNotification(base::Time eol_date, base::Time now) {
140 CHECK(!eol_date.is_null());
141 CHECK(!now.is_null());
142
xiaoyinhf39e3dd2016-06-18 04:50:23143 message_center::RichNotificationData data;
Regan Hsu62dc18f2019-10-03 20:30:44144 std::unique_ptr<message_center::Notification> notification;
Evan Stadecf27dae12017-11-07 23:57:51145
146 DCHECK_EQ(BUTTON_MORE_INFO, data.buttons.size());
dpapad943ef182018-01-20 02:58:20147 data.buttons.emplace_back(GetStringUTF16(IDS_LEARN_MORE));
Evan Stadecf27dae12017-11-07 23:57:51148
Regan Hsu62dc18f2019-10-03 20:30:44149 if (now < eol_date) {
150 // Notifies user that updates will stop occurring at a month and year.
151 notification = ash::CreateSystemNotification(
152 message_center::NOTIFICATION_TYPE_SIMPLE, kEolNotificationId,
153 l10n_util::GetStringFUTF16(IDS_PENDING_EOL_NOTIFICATION_TITLE,
154 FormatMonthAndYearWithOffset(eol_date)),
155 l10n_util::GetStringFUTF16(IDS_PENDING_EOL_NOTIFICATION_MESSAGE,
156 ui::GetChromeOSDeviceName()),
157 base::string16() /* display_source */, GURL(kEolNotificationId),
158 message_center::NotifierId(
159 message_center::NotifierType::SYSTEM_COMPONENT, kEolNotificationId),
160 data,
161 base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
162 weak_ptr_factory_.GetWeakPtr()),
163 vector_icons::kBusinessIcon,
164 message_center::SystemNotificationWarningLevel::NORMAL);
165 } else {
166 DCHECK_EQ(BUTTON_DISMISS, data.buttons.size());
167 data.buttons.emplace_back(GetStringUTF16(IDS_EOL_DISMISS_BUTTON));
Regan Hsu8de051a2019-09-03 17:17:16168
Regan Hsu62dc18f2019-10-03 20:30:44169 // Notifies user that updates will no longer occur after this final update.
170 notification = ash::CreateSystemNotification(
171 message_center::NOTIFICATION_TYPE_SIMPLE, kEolNotificationId,
172 GetStringUTF16(IDS_EOL_NOTIFICATION_TITLE),
173 l10n_util::GetStringFUTF16(IDS_EOL_NOTIFICATION_EOL,
174 ui::GetChromeOSDeviceName()),
175 base::string16() /* display_source */, GURL(kEolNotificationId),
176 message_center::NotifierId(
177 message_center::NotifierType::SYSTEM_COMPONENT, kEolNotificationId),
178 data,
179 base::MakeRefCounted<message_center::ThunkNotificationDelegate>(
180 weak_ptr_factory_.GetWeakPtr()),
181 kNotificationEndOfSupportIcon,
182 message_center::SystemNotificationWarningLevel::NORMAL);
183 }
Evan Stadecc63b182017-09-26 16:05:12184
Evan Stadecc63b182017-09-26 16:05:12185 NotificationDisplayServiceFactory::GetForProfile(profile_)->Display(
Lei Zhang03c6b782019-03-21 05:22:24186 NotificationHandler::Type::TRANSIENT, *notification,
187 /*metadata=*/nullptr);
xiaoyinhf39e3dd2016-06-18 04:50:23188}
189
Regan Hsu62dc18f2019-10-03 20:30:44190void EolNotification::Close(bool by_user) {
191 // Only the final Eol notification has an explicit dismiss button, and
192 // is only dismissible by that button. The first and second warning
193 // buttons do not have an explicit dismiss button.
194 if (!by_user || !dismiss_pref_ ||
195 dismiss_pref_ == prefs::kEolNotificationDismissed) {
196 return;
197 }
198
199 profile_->GetPrefs()->SetBoolean(*dismiss_pref_, true);
200}
201
202void EolNotification::Click(const base::Optional<int>& button_index,
203 const base::Optional<base::string16>& reply) {
204 if (!button_index)
205 return;
206
207 switch (*button_index) {
208 case BUTTON_MORE_INFO: {
209 // show eol link
210 NavigateParams params(profile_, GURL(chrome::kEolNotificationURL),
211 ui::PAGE_TRANSITION_LINK);
212 params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
213 params.window_action = NavigateParams::SHOW_WINDOW;
214 Navigate(&params);
215 break;
216 }
217 case BUTTON_DISMISS:
218 CHECK(dismiss_pref_);
219 // set dismiss pref.
220 profile_->GetPrefs()->SetBoolean(*dismiss_pref_, true);
221 break;
222 }
223
224 if (dismiss_pref_ && (*dismiss_pref_ != prefs::kEolNotificationDismissed))
225 profile_->GetPrefs()->SetBoolean(*dismiss_pref_, true);
226
227 NotificationDisplayServiceFactory::GetForProfile(profile_)->Close(
228 NotificationHandler::Type::TRANSIENT, kEolNotificationId);
229}
230
xiaoyinhf39e3dd2016-06-18 04:50:23231} // namespace chromeos