update_notification_controller.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. // Copyright 2018 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. #include "ash/system/update/update_notification_controller.h"
  5. #include "ash/constants/notifier_catalogs.h"
  6. #include "ash/public/cpp/notification_utils.h"
  7. #include "ash/public/cpp/system_tray_client.h"
  8. #include "ash/public/cpp/update_types.h"
  9. #include "ash/resources/vector_icons/vector_icons.h"
  10. #include "ash/session/session_controller_impl.h"
  11. #include "ash/shell.h"
  12. #include "ash/strings/grit/ash_strings.h"
  13. #include "ash/system/model/enterprise_domain_model.h"
  14. #include "ash/system/model/system_tray_model.h"
  15. #include "ash/system/session/shutdown_confirmation_dialog.h"
  16. #include "base/bind.h"
  17. #include "base/files/file_path.h"
  18. #include "base/files/file_util.h"
  19. #include "base/metrics/user_metrics.h"
  20. #include "base/strings/utf_string_conversions.h"
  21. #include "base/task/thread_pool.h"
  22. #include "chromeos/ash/components/dbus/update_engine/update_engine_client.h"
  23. #include "components/vector_icons/vector_icons.h"
  24. #include "third_party/abseil-cpp/absl/types/optional.h"
  25. #include "ui/base/l10n/l10n_util.h"
  26. #include "ui/chromeos/devicetype_utils.h"
  27. #include "ui/gfx/vector_icon_types.h"
  28. #include "ui/message_center/message_center.h"
  29. #include "ui/message_center/public/cpp/notification.h"
  30. using message_center::MessageCenter;
  31. using message_center::Notification;
  32. namespace ash {
  33. namespace {
  34. const char kNotifierId[] = "ash.update";
  35. const char kNotificationId[] = "chrome://update";
  36. bool CheckForSlowBoot(const base::FilePath& slow_boot_file_path) {
  37. if (base::PathExists(slow_boot_file_path)) {
  38. return true;
  39. }
  40. return false;
  41. }
  42. std::u16string GetDomainManager(
  43. RelaunchNotificationState::PolicySource policy_source) {
  44. EnterpriseDomainModel* enterprise_domain =
  45. Shell::Get()->system_tray_model()->enterprise_domain();
  46. std::string domain_manager;
  47. switch (policy_source) {
  48. case RelaunchNotificationState::kDevice:
  49. domain_manager = enterprise_domain->enterprise_domain_manager();
  50. break;
  51. case RelaunchNotificationState::kUser:
  52. domain_manager = enterprise_domain->account_domain_manager();
  53. break;
  54. }
  55. return base::UTF8ToUTF16(domain_manager);
  56. }
  57. } // namespace
  58. UpdateNotificationController::UpdateNotificationController()
  59. : model_(Shell::Get()->system_tray_model()->update_model()),
  60. slow_boot_file_path_("/mnt/stateful_partition/etc/slow_boot_required") {
  61. model_->AddObserver(this);
  62. OnUpdateAvailable();
  63. }
  64. UpdateNotificationController::~UpdateNotificationController() {
  65. model_->RemoveObserver(this);
  66. }
  67. void UpdateNotificationController::GenerateUpdateNotification(
  68. absl::optional<bool> slow_boot_file_path_exists) {
  69. if (!ShouldShowUpdate()) {
  70. message_center::MessageCenter::Get()->RemoveNotification(
  71. kNotificationId, false /* by_user */);
  72. return;
  73. }
  74. if (slow_boot_file_path_exists != absl::nullopt) {
  75. slow_boot_file_path_exists_ = slow_boot_file_path_exists.value();
  76. }
  77. std::unique_ptr<Notification> notification = CreateSystemNotification(
  78. message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, GetTitle(),
  79. GetMessage(), std::u16string() /* display_source */, GURL(),
  80. message_center::NotifierId(message_center::NotifierType::SYSTEM_COMPONENT,
  81. kNotifierId, NotificationCatalogName::kUpdate),
  82. message_center::RichNotificationData(),
  83. base::MakeRefCounted<message_center::HandleNotificationClickDelegate>(
  84. base::BindRepeating(
  85. &UpdateNotificationController::HandleNotificationClick,
  86. weak_ptr_factory_.GetWeakPtr())),
  87. GetIcon(), GetWarningLevel());
  88. notification->set_pinned(true);
  89. if (model_->relaunch_notification_state().requirement_type ==
  90. RelaunchNotificationState::kRequired)
  91. notification->SetSystemPriority();
  92. if (ShouldShowDeferredUpdate()) {
  93. notification->set_buttons({
  94. message_center::ButtonInfo(l10n_util::GetStringUTF16(
  95. IDS_UPDATE_NOTIFICATION_APPLY_UPDATE_BUTTON)),
  96. message_center::ButtonInfo(l10n_util::GetStringUTF16(
  97. IDS_UPDATE_NOTIFICATION_AUTOMATIC_UPDATE_BUTTON)),
  98. });
  99. } else if (model_->update_required()) {
  100. std::vector<message_center::ButtonInfo> notification_actions;
  101. if (model_->rollback()) {
  102. notification_actions.push_back(message_center::ButtonInfo(
  103. l10n_util::GetStringUTF16(IDS_ROLLBACK_NOTIFICATION_RESTART_BUTTON)));
  104. } else if (model_->factory_reset_required()) {
  105. notification_actions.push_back(message_center::ButtonInfo(
  106. l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_RESET_TO_UPDATE)));
  107. } else {
  108. notification_actions.push_back(message_center::ButtonInfo(
  109. l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_RESTART_BUTTON)));
  110. }
  111. notification->set_buttons(notification_actions);
  112. }
  113. MessageCenter::Get()->AddNotification(std::move(notification));
  114. }
  115. void UpdateNotificationController::OnUpdateAvailable() {
  116. base::ThreadPool::PostTaskAndReplyWithResult(
  117. FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING},
  118. base::BindOnce(&CheckForSlowBoot, slow_boot_file_path_),
  119. base::BindOnce(&UpdateNotificationController::GenerateUpdateNotification,
  120. weak_ptr_factory_.GetWeakPtr()));
  121. }
  122. bool UpdateNotificationController::ShouldShowUpdate() const {
  123. return model_->update_required() ||
  124. model_->update_over_cellular_available() || ShouldShowDeferredUpdate();
  125. }
  126. bool UpdateNotificationController::ShouldShowDeferredUpdate() const {
  127. return model_->update_deferred() == DeferredUpdateState::kShowNotification;
  128. }
  129. std::u16string UpdateNotificationController::GetTitle() const {
  130. if (model_->update_type() == UpdateType::kLacros)
  131. return l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_TITLE_LACROS);
  132. switch (model_->relaunch_notification_state().requirement_type) {
  133. case RelaunchNotificationState::kRecommendedAndOverdue:
  134. return model_->rollback() ? l10n_util::GetStringUTF16(
  135. IDS_ROLLBACK_OVERDUE_NOTIFICATION_TITLE)
  136. : l10n_util::GetStringUTF16(
  137. IDS_RELAUNCH_RECOMMENDED_OVERDUE_TITLE);
  138. case RelaunchNotificationState::kRecommendedNotOverdue:
  139. return model_->rollback()
  140. ? l10n_util::GetStringUTF16(IDS_ROLLBACK_NOTIFICATION_TITLE)
  141. : l10n_util::GetStringUTF16(IDS_RELAUNCH_RECOMMENDED_TITLE);
  142. case RelaunchNotificationState::kRequired: {
  143. const base::TimeDelta& rounded_offset =
  144. model_->relaunch_notification_state()
  145. .rounded_time_until_reboot_required;
  146. if (rounded_offset.InDays() >= 2) {
  147. return l10n_util::GetPluralStringFUTF16(
  148. model_->rollback() ? IDS_ROLLBACK_REQUIRED_TITLE_DAYS
  149. : IDS_RELAUNCH_REQUIRED_TITLE_DAYS,
  150. rounded_offset.InDays());
  151. }
  152. if (rounded_offset.InHours() >= 1) {
  153. return l10n_util::GetPluralStringFUTF16(
  154. model_->rollback() ? IDS_ROLLBACK_REQUIRED_TITLE_HOURS
  155. : IDS_RELAUNCH_REQUIRED_TITLE_HOURS,
  156. rounded_offset.InHours());
  157. }
  158. if (rounded_offset.InMinutes() >= 1) {
  159. return l10n_util::GetPluralStringFUTF16(
  160. model_->rollback() ? IDS_ROLLBACK_REQUIRED_TITLE_MINUTES
  161. : IDS_RELAUNCH_REQUIRED_TITLE_MINUTES,
  162. rounded_offset.InMinutes());
  163. }
  164. return l10n_util::GetPluralStringFUTF16(
  165. model_->rollback() ? IDS_ROLLBACK_REQUIRED_TITLE_SECONDS
  166. : IDS_RELAUNCH_REQUIRED_TITLE_SECONDS,
  167. rounded_offset.InSeconds());
  168. }
  169. case RelaunchNotificationState::kNone:
  170. return model_->rollback()
  171. ? l10n_util::GetStringUTF16(IDS_ROLLBACK_NOTIFICATION_TITLE)
  172. : l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_TITLE);
  173. }
  174. }
  175. std::u16string UpdateNotificationController::GetMessage() const {
  176. if (model_->update_type() == UpdateType::kLacros)
  177. return l10n_util::GetStringUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_LACROS);
  178. if (ShouldShowDeferredUpdate()) {
  179. return l10n_util::GetStringUTF16(
  180. IDS_UPDATE_NOTIFICATION_MESSAGE_DEFERRED_UPDATE);
  181. }
  182. const std::u16string system_app_name =
  183. l10n_util::GetStringUTF16(IDS_ASH_MESSAGE_CENTER_SYSTEM_APP_NAME);
  184. if (model_->factory_reset_required() && !model_->rollback()) {
  185. return l10n_util::GetStringFUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_POWERWASH,
  186. ui::GetChromeOSDeviceName(),
  187. system_app_name);
  188. }
  189. absl::optional<int> body_message_id = absl::nullopt;
  190. switch (model_->relaunch_notification_state().requirement_type) {
  191. case RelaunchNotificationState::kRecommendedNotOverdue:
  192. body_message_id = model_->rollback()
  193. ? IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK
  194. : IDS_RELAUNCH_RECOMMENDED_BODY;
  195. break;
  196. case RelaunchNotificationState::kRecommendedAndOverdue:
  197. body_message_id = model_->rollback()
  198. ? IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK_OVERDUE
  199. : IDS_RELAUNCH_RECOMMENDED_OVERDUE_BODY;
  200. break;
  201. case RelaunchNotificationState::kRequired:
  202. body_message_id = model_->rollback() ? IDS_ROLLBACK_REQUIRED_BODY
  203. : IDS_RELAUNCH_REQUIRED_BODY;
  204. break;
  205. case RelaunchNotificationState::kNone:
  206. if (model_->rollback()) {
  207. body_message_id = IDS_UPDATE_NOTIFICATION_MESSAGE_ROLLBACK;
  208. }
  209. break;
  210. }
  211. std::u16string update_text;
  212. std::u16string domain_manager =
  213. GetDomainManager(model_->relaunch_notification_state().policy_source);
  214. if (body_message_id.has_value() && !domain_manager.empty() &&
  215. model_->update_type() == UpdateType::kSystem) {
  216. update_text = l10n_util::GetStringFUTF16(*body_message_id, domain_manager,
  217. ui::GetChromeOSDeviceName());
  218. } else {
  219. update_text = l10n_util::GetStringFUTF16(
  220. IDS_UPDATE_NOTIFICATION_MESSAGE_LEARN_MORE, system_app_name);
  221. }
  222. if (slow_boot_file_path_exists_) {
  223. return l10n_util::GetStringFUTF16(IDS_UPDATE_NOTIFICATION_MESSAGE_SLOW_BOOT,
  224. update_text);
  225. }
  226. return update_text;
  227. }
  228. const gfx::VectorIcon& UpdateNotificationController::GetIcon() const {
  229. if (model_->rollback())
  230. return kSystemMenuRollbackIcon;
  231. if (model_->relaunch_notification_state().requirement_type ==
  232. RelaunchNotificationState::kNone)
  233. return kSystemMenuUpdateIcon;
  234. return vector_icons::kBusinessIcon;
  235. }
  236. message_center::SystemNotificationWarningLevel
  237. UpdateNotificationController::GetWarningLevel() const {
  238. if (model_->rollback() &&
  239. (model_->relaunch_notification_state().requirement_type ==
  240. RelaunchNotificationState::kRequired ||
  241. model_->relaunch_notification_state().requirement_type ==
  242. RelaunchNotificationState::kRecommendedAndOverdue)) {
  243. return message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
  244. }
  245. if (model_->rollback() ||
  246. model_->relaunch_notification_state().requirement_type ==
  247. RelaunchNotificationState::kRequired) {
  248. return message_center::SystemNotificationWarningLevel::WARNING;
  249. }
  250. return message_center::SystemNotificationWarningLevel::NORMAL;
  251. }
  252. void UpdateNotificationController::RestartForUpdate() {
  253. confirmation_dialog_ = nullptr;
  254. if (model_->update_type() == UpdateType::kLacros) {
  255. // Lacros only needs to restart the browser to cause the component updater
  256. // to use the new lacros component.
  257. Shell::Get()->session_controller()->AttemptRestartChrome();
  258. return;
  259. }
  260. // System updates require restarting the device.
  261. Shell::Get()->system_tray_model()->client()->RequestRestartForUpdate();
  262. base::RecordAction(
  263. base::UserMetricsAction("StatusArea_OS_Update_Default_Selected"));
  264. }
  265. void UpdateNotificationController::RestartCancelled() {
  266. confirmation_dialog_ = nullptr;
  267. // Put the notification back.
  268. GenerateUpdateNotification(absl::nullopt);
  269. }
  270. void UpdateNotificationController::HandleNotificationClick(
  271. absl::optional<int> button_index) {
  272. DCHECK(ShouldShowUpdate());
  273. if (!button_index) {
  274. // Notification message body clicked, which says "learn more".
  275. Shell::Get()->system_tray_model()->client()->ShowAboutChromeOS();
  276. return;
  277. }
  278. if (button_index.value() == 0) {
  279. message_center::MessageCenter::Get()->RemoveNotification(
  280. kNotificationId, false /* by_user */);
  281. if (ShouldShowDeferredUpdate()) {
  282. // When the "update" button is clicked, apply the deferred update.
  283. ash::UpdateEngineClient::Get()->ApplyDeferredUpdate(base::DoNothing());
  284. } else if (model_->update_required()) {
  285. // Restart
  286. if (slow_boot_file_path_exists_) {
  287. // An active dialog exists already.
  288. if (confirmation_dialog_)
  289. return;
  290. confirmation_dialog_ = new ShutdownConfirmationDialog(
  291. IDS_DIALOG_TITLE_SLOW_BOOT, IDS_DIALOG_MESSAGE_SLOW_BOOT,
  292. base::BindOnce(&UpdateNotificationController::RestartForUpdate,
  293. weak_ptr_factory_.GetWeakPtr()),
  294. base::BindOnce(&UpdateNotificationController::RestartCancelled,
  295. weak_ptr_factory_.GetWeakPtr()));
  296. } else {
  297. RestartForUpdate();
  298. }
  299. } else {
  300. // Shows the about chrome OS page and checks for update after the page is
  301. // loaded.
  302. Shell::Get()->system_tray_model()->client()->ShowAboutChromeOS();
  303. }
  304. } else {
  305. // When the "automatic update" button is clicked, take user to the ChromeOS
  306. // additional details page that has the automatic update toggle.
  307. Shell::Get()->system_tray_model()->client()->ShowAboutChromeOSDetails();
  308. }
  309. }
  310. } // namespace ash