[Merge to M65]cros: Add device end of life message in md about page.

Chrome OS devices receive new feature Stable updates every six weeks,
these updates are for at least five years from the time devices are launched,
after 5 years the new feature updates are no longer guaranteed and devices will
eventually stop receiving updates. At this point, the device will be considered
End of Life.

The about page retrieves the end of life status(supported or end of life) from
update engine and show a message, an end of life icon, as well as an link for
more information: https://www.google.com/chromebook/older/
when status == end of life.

TBR=stevenjb@chromium.org,michaelpg@chromium.org
(cherry picked from commit 32816fa77bef96fdbb306a65352a1f3c1bc91a74)

Bug: 809258
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I18568894a88cb8e1aabdf22613f748eb8bfbf8be
Reviewed-on: https://chromium-review.googlesource.com/936448
Reviewed-by: Michael Giuffrida <michaelpg@chromium.org>
Reviewed-by: Steven Bennetts <stevenjb@chromium.org>
Commit-Queue: Xiaoyin Hu <xiaoyinh@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#539661}
Reviewed-on: https://chromium-review.googlesource.com/941793
Reviewed-by: Xiaoyin Hu <xiaoyinh@chromium.org>
Cr-Commit-Position: refs/branch-heads/3325@{#627}
Cr-Branched-From: bc084a8b5afa3744a74927344e304c02ae54189f-refs/heads/master@{#530369}
diff --git a/chrome/browser/chromeos/eol_notification.cc b/chrome/browser/chromeos/eol_notification.cc
index b7a8206..6686a35 100644
--- a/chrome/browser/chromeos/eol_notification.cc
+++ b/chrome/browser/chromeos/eol_notification.cc
@@ -105,8 +105,8 @@
       DBusThreadManager::Get()->GetUpdateEngineClient();
 
   // Request the Eol Status.
-  update_engine_client->GetEolStatus(
-      base::Bind(&EolNotification::OnEolStatus, weak_factory_.GetWeakPtr()));
+  update_engine_client->GetEolStatus(base::BindOnce(
+      &EolNotification::OnEolStatus, weak_factory_.GetWeakPtr()));
 }
 
 void EolNotification::OnEolStatus(update_engine::EndOfLifeStatus status) {
diff --git a/chrome/browser/resources/settings/about_page/about_page.html b/chrome/browser/resources/settings/about_page/about_page.html
index 42a110c6..4f2f097 100644
--- a/chrome/browser/resources/settings/about_page/about_page.html
+++ b/chrome/browser/resources/settings/about_page/about_page.html
@@ -96,12 +96,22 @@
             </div>
             <div class="settings-box two-line">
               <!-- TODO(dpapad): Investigate why vulcanize does not handle well
-                a new line after "getIconSrc_(", causes incorrect src URL -->
+                a new line after "getThrobberSrcIfUpdating_(", causes incorrect
+                src URL -->
+              <!-- Set the icon from the iconset (when it's obsolete/EOL and
+                when update is done) or set the src (when it's updating). -->
               <iron-icon
-                  hidden="[[!showUpdateStatus_]]"
-                  icon$="[[getIcon_(
+                  hidden="[[!shouldShowIcons_(showUpdateStatus_)]]"
+<if expr="not chromeos">
+                  icon$="[[getUpdateStatusIcon_(
                       obsoleteSystemInfo_, currentUpdateStatusEvent_)]]"
-                  src="[[getIconSrc_(obsoleteSystemInfo_, currentUpdateStatusEvent_)]]">
+                  src="[[getThrobberSrcIfUpdating_(obsoleteSystemInfo_, currentUpdateStatusEvent_)]]">
+</if>
+<if expr="chromeos">
+                  icon$="[[getUpdateStatusIcon_(
+                      hasEndOfLife_, currentUpdateStatusEvent_)]]"
+                  src="[[getThrobberSrcIfUpdating_(hasEndOfLife_, currentUpdateStatusEvent_)]]">
+</if>
               </iron-icon>
               <div class="start padded">
                 <div id="updateStatusMessage" hidden="[[!showUpdateStatus_]]">
@@ -121,6 +131,7 @@
                     $i18n{learnMore}
                   </a>
                 </div>
+<if expr="not chromeos">
                 <span id="deprecationWarning"
                     hidden="[[!obsoleteSystemInfo_.obsolete]]">
                   $i18n{aboutObsoleteSystem}
@@ -128,6 +139,15 @@
                     $i18n{learnMore}
                   </a>
                 </span>
+</if>
+<if expr="chromeos">
+               <div id="endOfLifeMessageContainer" hidden="[[!hasEndOfLife_]]">
+                 $i18n{endOfLifeMessage}
+                 <a href="$i18n{endOfLifeLearnMoreURL}" target="_blank">
+                   $i18n{learnMore}
+                 </a>
+               </div>
+</if>
                 <div class="secondary">$i18n{aboutBrowserVersion}</div>
               </div>
               <div class="separator" hidden="[[!showButtonContainer_]]"></div>
diff --git a/chrome/browser/resources/settings/about_page/about_page.js b/chrome/browser/resources/settings/about_page/about_page.js
index beecac1..d2dbea9b 100644
--- a/chrome/browser/resources/settings/about_page/about_page.js
+++ b/chrome/browser/resources/settings/about_page/about_page.js
@@ -34,6 +34,9 @@
 
     /** @private {?RegulatoryInfo} */
     regulatoryInfo_: Object,
+
+    /** @private */
+    hasEndOfLife_: Boolean,
     // </if>
 
     // <if expr="_google_chrome and is_macosx">
@@ -41,6 +44,7 @@
     promoteUpdaterStatus_: Object,
     // </if>
 
+    // <if expr="not chromeos">
     /** @private {!{obsolete: boolean, endOfLine: boolean}} */
     obsoleteSystemInfo_: {
       type: Object,
@@ -51,6 +55,7 @@
         };
       },
     },
+    // </if>
 
     /** @private */
     showUpdateStatus_: Boolean,
@@ -73,7 +78,7 @@
     showCheckUpdates_: {
       type: Boolean,
       computed: 'computeShowCheckUpdates_(' +
-          'currentUpdateStatusEvent_, hasCheckedForUpdates_)',
+          'currentUpdateStatusEvent_, hasCheckedForUpdates_, hasEndOfLife_)',
     },
 
     /** @private {!Map<string, string>} */
@@ -117,7 +122,7 @@
 
     // <if expr="chromeos">
     'updateShowUpdateStatus_(' +
-        'obsoleteSystemInfo_, currentUpdateStatusEvent_,' +
+        'hasEndOfLife_, currentUpdateStatusEvent_,' +
         'hasCheckedForUpdates_)',
     'updateShowRelaunch_(currentUpdateStatusEvent_, targetChannel_,' +
         'currentChannel_)',
@@ -155,6 +160,10 @@
     this.aboutBrowserProxy_.getRegulatoryInfo().then(info => {
       this.regulatoryInfo_ = info;
     });
+
+    this.aboutBrowserProxy_.getHasEndOfLife().then(result => {
+      this.hasEndOfLife_ = result;
+    });
     // </if>
     // <if expr="not chromeos">
     this.startListening_();
@@ -250,10 +259,22 @@
       this.showUpdateStatus_ = false;
       return;
     }
+
+    // Do not show "updated" status if the device is end of life.
+    if (this.hasEndOfLife_) {
+      this.showUpdateStatus_ = false;
+      return;
+    }
+    // </if>
+
+    // <if expr="not chromeos">
+    if (this.obsoleteSystemInfo_.endOfLine) {
+      this.showUpdateStatus_ = false;
+      return;
+    }
     // </if>
     this.showUpdateStatus_ =
-        this.currentUpdateStatusEvent_.status != UpdateStatus.DISABLED &&
-        !this.obsoleteSystemInfo_.endOfLine;
+        this.currentUpdateStatusEvent_.status != UpdateStatus.DISABLED;
   },
 
   /**
@@ -353,11 +374,21 @@
    * @return {?string}
    * @private
    */
-  getIcon_: function() {
+  getUpdateStatusIcon_: function() {
+    // <if expr="chromeos">
+    // If Chrome OS has reached end of life, display a special icon and
+    // ignore UpdateStatus.
+    if (this.hasEndOfLife_) {
+      return 'settings:end-of-life';
+    }
+    // </if>
+
+    // <if expr="not chromeos">
     // If this platform has reached the end of the line, display an error icon
     // and ignore UpdateStatus.
     if (this.obsoleteSystemInfo_.endOfLine)
       return 'settings:error';
+    // </if>
 
     switch (this.currentUpdateStatusEvent_.status) {
       case UpdateStatus.DISABLED_BY_ADMIN:
@@ -376,9 +407,17 @@
    * @return {?string}
    * @private
    */
-  getIconSrc_: function() {
+  getThrobberSrcIfUpdating_: function() {
+    // <if expr="chromeos">
+    if (this.hasEndOfLife_) {
+      return null;
+    }
+    // </if>
+
+    // <if expr="not chromeos">
     if (this.obsoleteSystemInfo_.endOfLine)
       return null;
+    // </if>
 
     switch (this.currentUpdateStatusEvent_.status) {
       case UpdateStatus.CHECKING:
@@ -440,6 +479,11 @@
    * @private
    */
   computeShowCheckUpdates_: function() {
+    // Disable update button if the device is end of life.
+    if (this.hasEndOfLife_) {
+      return false;
+    }
+
     // Enable the update button if we are in a stale 'updated' status or
     // update has failed. Disable it otherwise.
     const staleUpdatedStatus =
@@ -501,4 +545,20 @@
     this.aboutBrowserProxy_.openFeedbackDialog();
   },
   // </if>
+
+  /**
+   * @return {boolean}
+   * @private
+   */
+  shouldShowIcons_: function() {
+    // <if expr="chromeos">
+    if (this.hasEndOfLife_)
+      return true;
+    // </if>
+    // <if expr="not chromeos">
+    if (this.obsoleteSystemInfo_.endOfLine)
+      return true;
+    // </if>
+    return this.showUpdateStatus_;
+  },
 });
diff --git a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
index 78a6565..c074d7e 100644
--- a/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
+++ b/chrome/browser/resources/settings/about_page/about_page_browser_proxy.js
@@ -198,6 +198,13 @@
     getRegulatoryInfo() {}
 
     /**
+     * Checks if the device has reached end-of-life status and will no longer
+     * receive updates.
+     * @return {!Promise<boolean>}
+     */
+    getHasEndOfLife() {}
+
+    /**
      * Request TPM firmware update status from the browser. It results in one or
      * more 'tpm-firmware-update-status-changed' WebUI events.
      */
@@ -279,6 +286,11 @@
     }
 
     /** @override */
+    getHasEndOfLife() {
+      return cr.sendWithPromise('getHasEndOfLife');
+    }
+
+    /** @override */
     refreshTPMFirmwareUpdateStatus() {
       chrome.send('refreshTPMFirmwareUpdateStatus');
     }
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html
index 5c035d8..c15dd7b 100644
--- a/chrome/browser/resources/settings/icons.html
+++ b/chrome/browser/resources/settings/icons.html
@@ -126,6 +126,7 @@
       <g id="volume-up"><path d="M3 9v6h4l5 5V4L7 9H3zm13.5 3c0-1.77-1.02-3.29-2.5-4.03v8.05c1.48-.73 2.5-2.25 2.5-4.02zM14 3.23v2.06c2.89.86 5 3.54 5 6.71s-2.11 5.85-5 6.71v2.06c4.01-.91 7-4.49 7-8.77s-2.99-7.86-7-8.77z"></path></g>
 <if expr="chromeos">
       <g id="warning"><path d="M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z"></path></g>
+      <g id="end-of-life"><path d="M4.574 2.916H4.55l.01.01.014-.01zM2.5 4.968v.023a.18.18 0 0 1 .01-.013l-.01-.01zm14.585 10.49v-.024l-.01.013.01.01zm.223 1.817l-.933-.95-4.106-4.11L8.026 7.99 3.675 3.635l-.942-.941-.712-.713L1 3.002l1.733 1.733A9.056 9.056 0 0 0 1.05 9.98c0 1.95.628 3.748 1.683 5.22.574.8 1.274 1.501 2.074 2.075a8.918 8.918 0 0 0 5.218 1.684 8.918 8.918 0 0 0 5.218-1.684L16.991 19l1.02-1.021-.703-.704zM15.243 2.684A8.922 8.922 0 0 0 10.025 1a8.922 8.922 0 0 0-5.218 1.684c-.005.003 4.135 4.16 4.135 4.16l1.083-1.814L15.042 11h-1.846l4.11 4.214a8.939 8.939 0 0 0 .011-10.456 9.021 9.021 0 0 0-2.074-2.074zM12 15H7.012v-3.989L4.5 11l2.227-1.876L12 14.6v.4z" fill="#DB4437" fill-rule="evenodd"></path></g>
 </if>
 <if expr="not chromeos">
       <g id="web"><path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z"></path></g>
diff --git a/chrome/browser/ui/webui/help/version_updater.h b/chrome/browser/ui/webui/help/version_updater.h
index bbf32c3..2bfc0d5 100644
--- a/chrome/browser/ui/webui/help/version_updater.h
+++ b/chrome/browser/ui/webui/help/version_updater.h
@@ -48,7 +48,7 @@
   // types.
 #if defined(OS_CHROMEOS)
   typedef base::Callback<void(const std::string&)> ChannelCallback;
-  typedef base::Callback<void(update_engine::EndOfLifeStatus status)>
+  typedef base::OnceCallback<void(update_engine::EndOfLifeStatus status)>
       EolStatusCallback;
 #endif
 
@@ -91,7 +91,7 @@
                           bool is_powerwash_allowed) = 0;
   virtual void GetChannel(bool get_current_channel,
                           const ChannelCallback& callback) = 0;
-  virtual void GetEolStatus(const EolStatusCallback& callback) = 0;
+  virtual void GetEolStatus(EolStatusCallback callback) = 0;
 
   // Sets a one time permission on a certain update in Update Engine.
   // - update_version: the Chrome OS version we want to update to.
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.cc b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
index 6f1d85c..9a61c4f 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.cc
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.cc
@@ -243,19 +243,20 @@
   cb.Run(current_channel);
 }
 
-void VersionUpdaterCros::GetEolStatus(const EolStatusCallback& cb) {
+void VersionUpdaterCros::GetEolStatus(EolStatusCallback cb) {
   UpdateEngineClient* update_engine_client =
       DBusThreadManager::Get()->GetUpdateEngineClient();
 
   // Request the Eol Status. Bind to a weak_ptr bound method rather than passing
   // |cb| directly so that |cb| does not outlive |this|.
-  update_engine_client->GetEolStatus(base::Bind(
-      &VersionUpdaterCros::OnGetEolStatus, weak_ptr_factory_.GetWeakPtr(), cb));
+  update_engine_client->GetEolStatus(
+      base::BindOnce(&VersionUpdaterCros::OnGetEolStatus,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(cb)));
 }
 
-void VersionUpdaterCros::OnGetEolStatus(const EolStatusCallback& cb,
+void VersionUpdaterCros::OnGetEolStatus(EolStatusCallback cb,
                                         update_engine::EndOfLifeStatus status) {
-  cb.Run(status);
+  std::move(cb).Run(status);
 }
 
 VersionUpdaterCros::VersionUpdaterCros(content::WebContents* web_contents)
diff --git a/chrome/browser/ui/webui/help/version_updater_chromeos.h b/chrome/browser/ui/webui/help/version_updater_chromeos.h
index 7defbfa5..ec6ea7d 100644
--- a/chrome/browser/ui/webui/help/version_updater_chromeos.h
+++ b/chrome/browser/ui/webui/help/version_updater_chromeos.h
@@ -33,7 +33,7 @@
   // Gets the last update status, without triggering a new check or download.
   void GetUpdateStatus(const StatusCallback& callback);
 
-  void GetEolStatus(const EolStatusCallback& callback) override;
+  void GetEolStatus(EolStatusCallback callback) override;
 
  protected:
   friend class VersionUpdater;
@@ -58,7 +58,7 @@
                     const std::string& current_channel);
 
   // Callback from UpdateEngineClient::GetEolStatus().
-  void OnGetEolStatus(const EolStatusCallback& cb,
+  void OnGetEolStatus(EolStatusCallback cb,
                       update_engine::EndOfLifeStatus status);
 
   // BrowserContext in which the class was instantiated.
diff --git a/chrome/browser/ui/webui/settings/about_handler.cc b/chrome/browser/ui/webui/settings/about_handler.cc
index 1920f85c..11da497 100644
--- a/chrome/browser/ui/webui/settings/about_handler.cc
+++ b/chrome/browser/ui/webui/settings/about_handler.cc
@@ -335,6 +335,10 @@
   html_source->AddString("aboutUserAgent", GetUserAgent());
   html_source->AddString("aboutJsEngineVersion", V8_VERSION_STRING);
   html_source->AddString("aboutBlinkVersion", content::GetWebKitVersion());
+  html_source->AddString("endOfLifeMessage",
+                         l10n_util::GetStringUTF16(IDS_EOL_NOTIFICATION_EOL));
+  html_source->AddString("endOfLifeLearnMoreURL",
+                         base::ASCIIToUTF16(chrome::kEolNotificationURL));
 #endif
 
   return new AboutHandler();
@@ -378,6 +382,10 @@
       "refreshTPMFirmwareUpdateStatus",
       base::Bind(&AboutHandler::HandleRefreshTPMFirmwareUpdateStatus,
                  base::Unretained(this)));
+  web_ui()->RegisterMessageCallback(
+      "getHasEndOfLife",
+      base::BindRepeating(&AboutHandler::HandleGetHasEndOfLife,
+                          base::Unretained(this)));
 #endif
 #if defined(OS_MACOSX)
   web_ui()->RegisterMessageCallback(
@@ -602,6 +610,25 @@
   event->SetBoolean("updateAvailable", update_available);
   FireWebUIListener("tpm-firmware-update-status-changed", *event);
 }
+
+void AboutHandler::HandleGetHasEndOfLife(const base::ListValue* args) {
+  CHECK_EQ(1U, args->GetSize());
+  std::string callback_id;
+  CHECK(args->GetString(0, &callback_id));
+  version_updater_->GetEolStatus(
+      base::BindOnce(&AboutHandler::OnGetEndOfLifeStatus,
+                     weak_factory_.GetWeakPtr(), callback_id));
+}
+
+void AboutHandler::OnGetEndOfLifeStatus(std::string callback_id,
+                                        update_engine::EndOfLifeStatus status) {
+  // Check for EndOfLifeStatus::kEol only because
+  // EndOfLifeStatus::kSecurityOnly state is no longer supported.
+  ResolveJavascriptCallback(
+      base::Value(callback_id),
+      base::Value(status == update_engine::EndOfLifeStatus::kEol));
+}
+
 #endif  // defined(OS_CHROMEOS)
 
 void AboutHandler::RequestUpdate() {
diff --git a/chrome/browser/ui/webui/settings/about_handler.h b/chrome/browser/ui/webui/settings/about_handler.h
index 3127e97..0e47e25 100644
--- a/chrome/browser/ui/webui/settings/about_handler.h
+++ b/chrome/browser/ui/webui/settings/about_handler.h
@@ -147,6 +147,16 @@
   void OnRegulatoryLabelTextRead(std::string callback_id,
                                  const base::FilePath& label_dir_path,
                                  const std::string& text);
+
+  // Retrieves device end of life status.
+  // Will asynchronously resolve the provided callback with a boolean
+  // indicating whether the device has reached end-of-life status (will no
+  // longer receive updates).
+  void HandleGetHasEndOfLife(const base::ListValue* args);
+
+  // Callbacks for version_updater_->GetEolStatus calls.
+  void OnGetEndOfLifeStatus(std::string callback_id,
+                            update_engine::EndOfLifeStatus status);
 #endif
 
   // Specialized instance of the VersionUpdater used to update the browser.
diff --git a/chrome/test/data/webui/settings/about_page_tests.js b/chrome/test/data/webui/settings/about_page_tests.js
index ede8dbc6..d90ca932 100644
--- a/chrome/test/data/webui/settings/about_page_tests.js
+++ b/chrome/test/data/webui/settings/about_page_tests.js
@@ -15,11 +15,8 @@
 
       if (cr.isChromeOS) {
         methodNames.push(
-          'getChannelInfo',
-          'getVersionInfo',
-          'getRegulatoryInfo',
-          'refreshTPMFirmwareUpdateStatus',
-          'setChannel');
+            'getChannelInfo', 'getVersionInfo', 'getRegulatoryInfo',
+            'getHasEndOfLife', 'refreshTPMFirmwareUpdateStatus', 'setChannel');
       }
 
       if (cr.isMac)
@@ -52,6 +49,9 @@
         this.tpmFirmwareUpdateStatus_ = {
           updateAvailable: false,
         };
+
+        /** @private {boolean} */
+        this.hasEndOfLife_ = false;
       }
     }
 
@@ -129,6 +129,12 @@
       this.regulatoryInfo_ = regulatoryInfo;
     };
 
+    /** @param {boolean} hasEndOfLife */
+    TestAboutPageBrowserProxy.prototype.setHasEndOfLife = function(
+        hasEndOfLife) {
+      this.hasEndOfLife_ = hasEndOfLife;
+    };
+
     /** @override */
     TestAboutPageBrowserProxy.prototype.getChannelInfo = function() {
       this.methodCalled('getChannelInfo');
@@ -148,6 +154,12 @@
     };
 
     /** @override */
+    TestAboutPageBrowserProxy.prototype.getHasEndOfLife = function() {
+      this.methodCalled('getHasEndOfLife');
+      return Promise.resolve(this.hasEndOfLife_);
+    };
+
+    /** @override */
     TestAboutPageBrowserProxy.prototype.setChannel = function(
         channel, isPowerwashAllowed) {
       this.methodCalled('setChannel', [channel, isPowerwashAllowed]);
@@ -302,85 +314,83 @@
         assertTrue(!!page.$$('#updateStatusMessage a[hidden]'));
       });
 
-      /**
-       * Test that when the current platform has been marked as deprecated (but
-       * not end of the line) a deprecation warning message is displayed,
-       * without interfering with the update status message and icon.
-       */
-      test('ObsoleteSystem', function() {
-        loadTimeData.overrideValues({
-          aboutObsoleteNowOrSoon: true,
-          aboutObsoleteEndOfTheLine: false,
+      if (!cr.isChromeOS) {
+        /**
+         * Test that when the current platform has been marked as deprecated
+         * (but not end of the line) a deprecation warning message is displayed,
+         * without interfering with the update status message and icon.
+         */
+        test('ObsoleteSystem', function() {
+          loadTimeData.overrideValues({
+            aboutObsoleteNowOrSoon: true,
+            aboutObsoleteEndOfTheLine: false,
+          });
+
+          return initNewPage().then(function() {
+            const icon = page.$$('iron-icon');
+            assertTrue(!!icon);
+            assertTrue(!!page.$.updateStatusMessage);
+            assertTrue(!!page.$.deprecationWarning);
+            assertFalse(page.$.deprecationWarning.hidden);
+
+            fireStatusChanged(UpdateStatus.CHECKING);
+            assertEquals(SPINNER_ICON, icon.src);
+            assertEquals(null, icon.getAttribute('icon'));
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertFalse(page.$.updateStatusMessage.hidden);
+
+            fireStatusChanged(UpdateStatus.UPDATING);
+            assertEquals(SPINNER_ICON, icon.src);
+            assertEquals(null, icon.getAttribute('icon'));
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertFalse(page.$.updateStatusMessage.hidden);
+
+            fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
+            assertEquals(null, icon.src);
+            assertEquals('settings:check-circle', icon.icon);
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertFalse(page.$.updateStatusMessage.hidden);
+          });
         });
 
-        return initNewPage().then(function() {
-          const icon = page.$$('iron-icon');
-          assertTrue(!!icon);
-          assertTrue(!!page.$.updateStatusMessage);
-          assertTrue(!!page.$.deprecationWarning);
+        /**
+         * Test that when the current platform has reached the end of the line,
+         * a deprecation warning message and an error icon is displayed.
+         */
+        test('ObsoleteSystemEndOfLine', function() {
+          loadTimeData.overrideValues({
+            aboutObsoleteNowOrSoon: true,
+            aboutObsoleteEndOfTheLine: true,
+          });
+          return initNewPage().then(function() {
+            const icon = page.$$('iron-icon');
+            assertTrue(!!icon);
+            assertTrue(!!page.$.deprecationWarning);
+            assertTrue(!!page.$.updateStatusMessage);
 
-          assertFalse(page.$.deprecationWarning.hidden);
-          // Update status message should be hidden before user has checked for
-          // updates, on ChromeOS.
-          assertEquals(cr.isChromeOS, page.$.updateStatusMessage.hidden);
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertFalse(page.$.deprecationWarning.hidden);
 
-          fireStatusChanged(UpdateStatus.CHECKING);
-          assertEquals(SPINNER_ICON, icon.src);
-          assertEquals(null, icon.getAttribute('icon'));
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertFalse(page.$.updateStatusMessage.hidden);
+            fireStatusChanged(UpdateStatus.CHECKING);
+            assertEquals(null, icon.src);
+            assertEquals('settings:error', icon.icon);
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertTrue(page.$.updateStatusMessage.hidden);
 
-          fireStatusChanged(UpdateStatus.UPDATING);
-          assertEquals(SPINNER_ICON, icon.src);
-          assertEquals(null, icon.getAttribute('icon'));
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertFalse(page.$.updateStatusMessage.hidden);
+            fireStatusChanged(UpdateStatus.FAILED);
+            assertEquals(null, icon.src);
+            assertEquals('settings:error', icon.icon);
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertTrue(page.$.updateStatusMessage.hidden);
 
-          fireStatusChanged(UpdateStatus.NEARLY_UPDATED);
-          assertEquals(null, icon.src);
-          assertEquals('settings:check-circle', icon.icon);
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertFalse(page.$.updateStatusMessage.hidden);
+            fireStatusChanged(UpdateStatus.UPDATED);
+            assertEquals(null, icon.src);
+            assertEquals('settings:error', icon.icon);
+            assertFalse(page.$.deprecationWarning.hidden);
+            assertTrue(page.$.updateStatusMessage.hidden);
+          });
         });
-      });
-
-      /**
-       * Test that when the current platform has reached the end of the line, a
-       * deprecation warning message and an error icon is displayed.
-       */
-      test('ObsoleteSystemEndOfLine', function() {
-        loadTimeData.overrideValues({
-          aboutObsoleteNowOrSoon: true,
-          aboutObsoleteEndOfTheLine: true,
-        });
-        return initNewPage().then(function() {
-          const icon = page.$$('iron-icon');
-          assertTrue(!!icon);
-          assertTrue(!!page.$.deprecationWarning);
-          assertTrue(!!page.$.updateStatusMessage);
-
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertFalse(page.$.deprecationWarning.hidden);
-
-          fireStatusChanged(UpdateStatus.CHECKING);
-          assertEquals(null, icon.src);
-          assertEquals('settings:error', icon.icon);
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertTrue(page.$.updateStatusMessage.hidden);
-
-          fireStatusChanged(UpdateStatus.FAILED);
-          assertEquals(null, icon.src);
-          assertEquals('settings:error', icon.icon);
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertTrue(page.$.updateStatusMessage.hidden);
-
-          fireStatusChanged(UpdateStatus.UPDATED);
-          assertEquals(null, icon.src);
-          assertEquals('settings:error', icon.icon);
-          assertFalse(page.$.deprecationWarning.hidden);
-          assertTrue(page.$.updateStatusMessage.hidden);
-        });
-      });
+      }
 
       test('Relaunch', function() {
         let relaunch = page.$.relaunch;
@@ -598,6 +608,51 @@
                 });
           });
         });
+
+        test('DeviceEndOfLife', function() {
+          /**
+           * Checks the visibility of the end of life message and icon.
+           * @param {boolean} isShowing Whether the end of life UI is expected
+           *     to be visible.
+           * @return {!Promise}
+           */
+          function checkHasEndOfLife(isShowing) {
+            return aboutBrowserProxy.whenCalled('getHasEndOfLife')
+                .then(function() {
+                  const endOfLifeMessageContainer =
+                      page.$.endOfLifeMessageContainer;
+                  assertTrue(!!endOfLifeMessageContainer);
+                  assertEquals(isShowing, !endOfLifeMessageContainer.hidden);
+
+                  // Update status message should be hidden before user has
+                  // checked for updates.
+                  assertTrue(page.$.updateStatusMessage.hidden);
+
+                  fireStatusChanged(UpdateStatus.CHECKING);
+                  assertEquals(isShowing, page.$.updateStatusMessage.hidden);
+
+                  if (isShowing) {
+                    const icon = page.$$('iron-icon');
+                    assertTrue(!!icon);
+                    assertEquals(null, icon.src);
+                    assertEquals('settings:end-of-life', icon.icon);
+
+                    const checkForUpdates = page.$.checkForUpdates;
+                    assertTrue(!!checkForUpdates);
+                    assertTrue(checkForUpdates.hidden);
+                  }
+                });
+          }
+
+          return checkHasEndOfLife(false)
+              .then(function() {
+                aboutBrowserProxy.setHasEndOfLife(true);
+                return initNewPage();
+              })
+              .then(function() {
+                return checkHasEndOfLife(true);
+              });
+        });
       }
 
       if (!cr.isChromeOS) {
diff --git a/chromeos/dbus/fake_update_engine_client.cc b/chromeos/dbus/fake_update_engine_client.cc
index 859cfd9..ccd22b5b 100644
--- a/chromeos/dbus/fake_update_engine_client.cc
+++ b/chromeos/dbus/fake_update_engine_client.cc
@@ -85,11 +85,10 @@
       FROM_HERE, base::Bind(callback, std::string()));
 }
 
-void FakeUpdateEngineClient::GetEolStatus(
-    const GetEolStatusCallback& callback) {
+void FakeUpdateEngineClient::GetEolStatus(GetEolStatusCallback callback) {
   base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE,
-      base::Bind(callback, update_engine::EndOfLifeStatus::kSupported));
+      FROM_HERE, base::BindOnce(std::move(callback),
+                                update_engine::EndOfLifeStatus::kSupported));
 }
 
 void FakeUpdateEngineClient::SetUpdateOverCellularPermission(
diff --git a/chromeos/dbus/fake_update_engine_client.h b/chromeos/dbus/fake_update_engine_client.h
index 2b49014..eaf1460 100644
--- a/chromeos/dbus/fake_update_engine_client.h
+++ b/chromeos/dbus/fake_update_engine_client.h
@@ -35,7 +35,7 @@
                   bool is_powerwash_allowed) override;
   void GetChannel(bool get_current_channel,
                   const GetChannelCallback& callback) override;
-  void GetEolStatus(const GetEolStatusCallback& callback) override;
+  void GetEolStatus(GetEolStatusCallback callback) override;
   void SetUpdateOverCellularPermission(bool allowed,
                                        const base::Closure& callback) override;
   void SetUpdateOverCellularOneTimePermission(
diff --git a/chromeos/dbus/update_engine_client.cc b/chromeos/dbus/update_engine_client.cc
index 747dc8d..e00b7f9 100644
--- a/chromeos/dbus/update_engine_client.cc
+++ b/chromeos/dbus/update_engine_client.cc
@@ -211,7 +211,7 @@
                        weak_ptr_factory_.GetWeakPtr(), callback));
   }
 
-  void GetEolStatus(const GetEolStatusCallback& callback) override {
+  void GetEolStatus(GetEolStatusCallback callback) override {
     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
                                  update_engine::kGetEolStatus);
 
@@ -219,7 +219,7 @@
     update_engine_proxy_->CallMethod(
         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
         base::BindOnce(&UpdateEngineClientImpl::OnGetEolStatus,
-                       weak_ptr_factory_.GetWeakPtr(), callback));
+                       weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
   }
 
   void SetUpdateOverCellularPermission(bool allowed,
@@ -419,18 +419,17 @@
   }
 
   // Called when a response for GetEolStatus() is received.
-  void OnGetEolStatus(const GetEolStatusCallback& callback,
-                      dbus::Response* response) {
+  void OnGetEolStatus(GetEolStatusCallback callback, dbus::Response* response) {
     if (!response) {
       LOG(ERROR) << "Failed to request getting eol status";
-      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      std::move(callback).Run(update_engine::EndOfLifeStatus::kSupported);
       return;
     }
     dbus::MessageReader reader(response);
     int status;
     if (!reader.PopInt32(&status)) {
       LOG(ERROR) << "Incorrect response: " << response->ToString();
-      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      std::move(callback).Run(update_engine::EndOfLifeStatus::kSupported);
       return;
     }
 
@@ -438,12 +437,13 @@
     if (status > update_engine::EndOfLifeStatus::kEol ||
         status < update_engine::EndOfLifeStatus::kSupported) {
       LOG(ERROR) << "Incorrect status value: " << status;
-      callback.Run(update_engine::EndOfLifeStatus::kSupported);
+      std::move(callback).Run(update_engine::EndOfLifeStatus::kSupported);
       return;
     }
 
     VLOG(1) << "Eol status received: " << status;
-    callback.Run(static_cast<update_engine::EndOfLifeStatus>(status));
+    std::move(callback).Run(
+        static_cast<update_engine::EndOfLifeStatus>(status));
   }
 
   // Called when a response for SetUpdateOverCellularPermission() is received.
@@ -576,8 +576,8 @@
       callback.Run(target_channel_);
   }
 
-  void GetEolStatus(const GetEolStatusCallback& callback) override {
-    callback.Run(update_engine::EndOfLifeStatus::kSupported);
+  void GetEolStatus(GetEolStatusCallback callback) override {
+    std::move(callback).Run(update_engine::EndOfLifeStatus::kSupported);
   }
 
   void SetUpdateOverCellularPermission(bool allowed,
diff --git a/chromeos/dbus/update_engine_client.h b/chromeos/dbus/update_engine_client.h
index 6345629..059a5a1 100644
--- a/chromeos/dbus/update_engine_client.h
+++ b/chromeos/dbus/update_engine_client.h
@@ -145,10 +145,10 @@
   // Called once GetEolStatus() is complete. Takes one parameter;
   // - EndOfLife Status: the end of life status of the device.
   using GetEolStatusCallback =
-      base::Callback<void(update_engine::EndOfLifeStatus status)>;
+      base::OnceCallback<void(update_engine::EndOfLifeStatus status)>;
 
   // Get EndOfLife status of the device and calls |callback| when completed.
-  virtual void GetEolStatus(const GetEolStatusCallback& callback) = 0;
+  virtual void GetEolStatus(GetEolStatusCallback callback) = 0;
 
   // Either allow or disallow receiving updates over cellular connections.
   // Synchronous (blocking) method.