cros: Remove supervised user methods from ash::SystemTrayDelegate

For mustash we need to replace SystemTrayDelegate. Use SessionController
for supervised user data instead.

Send a UserSession update when the Profile becomes available, because
supervised user data is kept in a profile keyed service.

Don't try to update the system tray item text while the menu is open.
The supervised user custodian almost never changes, so we'll see the new
value when the menu is opened again.

For testing, allow FakeChromeUserManager to skip the fake-addition of a
profile to a user on login. It shouldn't be doing this -- in production
the profile is loaded later.

BUG=712799
TEST=added to ash_unittests and unit_tests

Review-Url: https://codereview.chromium.org/2832903002
Cr-Commit-Position: refs/heads/master@{#466752}
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index bc78d24..0e15da5 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -475,7 +475,6 @@
     "system/status_area_widget.h",
     "system/status_area_widget_delegate.cc",
     "system/status_area_widget_delegate.h",
-    "system/supervised/custodian_info_tray_observer.h",
     "system/supervised/tray_supervised_user.cc",
     "system/supervised/tray_supervised_user.h",
     "system/system_clock_observer.cc",
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index c03d30b..f4d3ec44 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -368,9 +368,19 @@
         Shut down
       </message>
 
+      <!-- Status tray supervised users. -->
       <message name="IDS_ASH_STATUS_TRAY_SUPERVISED_LABEL" desc="Label shown instead of email for supervised users">
          Supervised user
       </message>
+      <message name="IDS_ASH_USER_IS_SUPERVISED_BY_NOTICE" desc="Text for notifications showing that this user is supervised">
+        Usage and history of this user can be reviewed by the manager (<ph name="MANAGER_EMAIL">$1<ex>user@example.com</ex></ph>) on chrome.com.
+      </message>
+      <message name="IDS_ASH_CHILD_USER_IS_MANAGED_BY_ONE_PARENT_NOTICE" desc="Text for notifications showing that this child user is managed by parent's account.">
+        This is an account for kids managed by <ph name="MANAGER_EMAIL">$1<ex>user@example.com</ex></ph>
+      </message>
+      <message name="IDS_ASH_CHILD_USER_IS_MANAGED_BY_TWO_PARENTS_NOTICE" desc="Text for notifications showing that this child user is managed by two parents' accounts.">
+        This is an account for kids managed by <ph name="FIRST_PARENT_EMAIL">$1<ex>first@example.com</ex></ph> and <ph name="SECOND_PARENT_EMAIL">$2<ex>second@example.com</ex></ph>
+      </message>
 
       <message name="IDS_ASH_STATUS_TRAY_PREVIOUS_MENU" desc="The accessible text for header entries for detailed versions of status tray items.">
         Previous menu
diff --git a/ash/public/interfaces/session_controller.mojom b/ash/public/interfaces/session_controller.mojom
index 0dd24b16..fbebb372 100644
--- a/ash/public/interfaces/session_controller.mojom
+++ b/ash/public/interfaces/session_controller.mojom
@@ -71,7 +71,8 @@
   PREVIOUS,  // Cycle to the previous user.
 };
 
-// Info about a user session in ash.
+// Info about a user session in ash. May be sent repeatedly for a single user
+// because individual fields may change (e.g. the avatar image or custodians).
 struct UserSession {
   // A user session id for the user session. It is generated by session manager
   // (chrome) when a user session starts and never changes during the lifetime
@@ -85,6 +86,14 @@
   string display_email;
   gfx.mojom.ImageSkia avatar;
 
+  // For supervised users only, the email address of the custodian account.
+  // Empty for non-supervised users. Available after profile is loaded.
+  string custodian_email;
+
+  // For supervised users only, the email address of the second custodian
+  // account, if any. Available after profile is loaded.
+  string second_custodian_email;
+
   // Whether the settings icon should be enabled in the system tray menu.
   // Usually true after login, but can be false for specialized user sessions
   // (e.g. adding supervised users).
diff --git a/ash/system/supervised/custodian_info_tray_observer.h b/ash/system/supervised/custodian_info_tray_observer.h
deleted file mode 100644
index fa27f15..0000000
--- a/ash/system/supervised/custodian_info_tray_observer.h
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright 2014 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 ASH_SYSTEM_SUPERVISED_CUSTODIAN_INFO_TRAY_OBSERVER_H_
-#define ASH_SYSTEM_SUPERVISED_CUSTODIAN_INFO_TRAY_OBSERVER_H_
-
-namespace ash {
-
-// Used to observe SystemTrayDelegate.
-class CustodianInfoTrayObserver {
- public:
-  // Called when information about the supervised user's custodian is changed,
-  // e.g. the display name.
-  virtual void OnCustodianInfoChanged() = 0;
-
- protected:
-  virtual ~CustodianInfoTrayObserver() {}
-};
-
-}  // namespace ash
-
-#endif  // ASH_SYSTEM_SUPERVISED_CUSTODIAN_INFO_TRAY_OBSERVER_H_
diff --git a/ash/system/supervised/tray_supervised_user.cc b/ash/system/supervised/tray_supervised_user.cc
index fe336b82..d725ec8 100644
--- a/ash/system/supervised/tray_supervised_user.cc
+++ b/ash/system/supervised/tray_supervised_user.cc
@@ -6,128 +6,122 @@
 
 #include <utility>
 
-#include "ash/login_status.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/strings/grit/ash_strings.h"
 #include "ash/system/system_notifier.h"
 #include "ash/system/tray/label_tray_view.h"
-#include "ash/system/tray/system_tray_delegate.h"
 #include "ash/system/tray/tray_constants.h"
 #include "base/callback.h"
 #include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "ui/base/l10n/l10n_util.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_delegate.h"
 
+using base::UTF8ToUTF16;
+using message_center::MessageCenter;
 using message_center::Notification;
 
 namespace ash {
+namespace {
+
+const gfx::VectorIcon& GetSupervisedUserIcon() {
+  SessionController* session_controller = Shell::Get()->session_controller();
+  DCHECK(session_controller->IsUserSupervised());
+
+  if (session_controller->IsUserChild())
+    return kSystemMenuChildUserIcon;
+
+  return kSystemMenuSupervisedUserIcon;
+}
+
+}  // namespace
 
 const char TraySupervisedUser::kNotificationId[] =
     "chrome://user/locally-managed";
 
 TraySupervisedUser::TraySupervisedUser(SystemTray* system_tray)
     : SystemTrayItem(system_tray, UMA_SUPERVISED_USER),
-      tray_view_(nullptr),
-      status_(LoginStatus::NOT_LOGGED_IN),
-      is_user_supervised_(false) {
-  Shell::Get()->system_tray_delegate()->AddCustodianInfoTrayObserver(this);
-}
+      scoped_session_observer_(this) {}
 
-TraySupervisedUser::~TraySupervisedUser() {
-  // We need the check as on shell destruction delegate is destroyed first.
-  SystemTrayDelegate* system_tray_delegate =
-      Shell::Get()->system_tray_delegate();
-  if (system_tray_delegate)
-    system_tray_delegate->RemoveCustodianInfoTrayObserver(this);
-}
-
-void TraySupervisedUser::UpdateMessage() {
-  base::string16 message =
-      Shell::Get()->system_tray_delegate()->GetSupervisedUserMessage();
-  if (tray_view_)
-    tray_view_->SetMessage(message);
-  if (message_center::MessageCenter::Get()->FindVisibleNotificationById(
-          kNotificationId))
-    CreateOrUpdateNotification(message);
-}
+TraySupervisedUser::~TraySupervisedUser() = default;
 
 views::View* TraySupervisedUser::CreateDefaultView(LoginStatus status) {
-  DCHECK(!tray_view_);
   if (!Shell::Get()->session_controller()->IsUserSupervised())
     return nullptr;
 
-  tray_view_ = new LabelTrayView(this, GetSupervisedUserIcon());
-  UpdateMessage();
-  return tray_view_;
+  LabelTrayView* tray_view =
+      new LabelTrayView(nullptr, GetSupervisedUserIcon());
+  // The message almost never changes during a session, so we compute it when
+  // the menu is shown. We don't update it while the menu is open.
+  tray_view->SetMessage(GetSupervisedUserMessage());
+  return tray_view;
 }
 
-void TraySupervisedUser::DestroyDefaultView() {
-  tray_view_ = nullptr;
-}
-
-void TraySupervisedUser::OnViewClicked(views::View* sender) {
-  // TODO(antrim): Find out what should we show in this case.
-}
-
-void TraySupervisedUser::UpdateAfterLoginStatusChange(LoginStatus status) {
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  SessionController* session = Shell::Get()->session_controller();
-
-  const bool is_user_supervised = session->IsUserSupervised();
-  if (status == status_ && is_user_supervised == is_user_supervised_)
+void TraySupervisedUser::OnUserSessionUpdated(const AccountId& account_id) {
+  // NOTE: This doesn't use OnUserSessionAdded() because the custodian info
+  // isn't available until after the session starts.
+  SessionController* session_controller = Shell::Get()->session_controller();
+  if (!session_controller->IsUserSupervised())
     return;
 
-  if (is_user_supervised && !session->IsUserChild() &&
-      status_ != LoginStatus::LOCKED &&
-      !delegate->GetSupervisedUserManager().empty()) {
-    CreateOrUpdateSupervisedWarningNotification();
-  }
+  // Get the active user session.
+  DCHECK(session_controller->IsActiveUserSessionStarted());
+  const mojom::UserSession* const user_session =
+      session_controller->GetUserSession(0);
+  DCHECK(user_session);
 
-  status_ = status;
-  is_user_supervised_ = is_user_supervised;
+  // Only respond to updates for the active user.
+  if (user_session->account_id != account_id)
+    return;
+
+  // Show notifications when custodian data first becomes available on login
+  // and if the custodian data changes.
+  if (custodian_email_ == user_session->custodian_email &&
+      second_custodian_email_ == user_session->second_custodian_email) {
+    return;
+  }
+  custodian_email_ = user_session->custodian_email;
+  second_custodian_email_ = user_session->second_custodian_email;
+
+  CreateOrUpdateNotification();
 }
 
-void TraySupervisedUser::CreateOrUpdateNotification(
-    const base::string16& new_message) {
+void TraySupervisedUser::CreateOrUpdateNotification() {
   std::unique_ptr<Notification> notification(
       message_center::Notification::CreateSystemNotification(
-          kNotificationId, base::string16() /* no title */, new_message,
+          kNotificationId, base::string16() /* no title */,
+          GetSupervisedUserMessage(),
           gfx::Image(
               gfx::CreateVectorIcon(GetSupervisedUserIcon(), kMenuIconColor)),
           system_notifier::kNotifierSupervisedUser,
           base::Closure() /* null callback */));
-  message_center::MessageCenter::Get()->AddNotification(
-      std::move(notification));
+  // AddNotification does an update if the notification already exists.
+  MessageCenter::Get()->AddNotification(std::move(notification));
 }
 
-void TraySupervisedUser::CreateOrUpdateSupervisedWarningNotification() {
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  CreateOrUpdateNotification(delegate->GetSupervisedUserMessage());
-}
+base::string16 TraySupervisedUser::GetSupervisedUserMessage() const {
+  base::string16 first_custodian = UTF8ToUTF16(custodian_email_);
+  base::string16 second_custodian = UTF8ToUTF16(second_custodian_email_);
 
-void TraySupervisedUser::OnCustodianInfoChanged() {
-  SystemTrayDelegate* delegate = Shell::Get()->system_tray_delegate();
-  std::string manager_name = delegate->GetSupervisedUserManager();
-  if (!manager_name.empty()) {
-    if (!Shell::Get()->session_controller()->IsUserChild() &&
-        !message_center::MessageCenter::Get()->FindVisibleNotificationById(
-            kNotificationId)) {
-      CreateOrUpdateSupervisedWarningNotification();
-    }
-    UpdateMessage();
+  // Regular supervised user. The "manager" is the first custodian.
+  if (!Shell::Get()->session_controller()->IsUserChild()) {
+    return l10n_util::GetStringFUTF16(IDS_ASH_USER_IS_SUPERVISED_BY_NOTICE,
+                                      first_custodian);
   }
-}
 
-const gfx::VectorIcon& TraySupervisedUser::GetSupervisedUserIcon() const {
-  // Not intended to be used for non-supervised users.
-  DCHECK(Shell::Get()->session_controller()->IsUserSupervised());
-
-  if (Shell::Get()->session_controller()->IsUserChild())
-    return kSystemMenuChildUserIcon;
-  return kSystemMenuSupervisedUserIcon;
+  // Child supervised user.
+  if (second_custodian.empty()) {
+    return l10n_util::GetStringFUTF16(
+        IDS_ASH_CHILD_USER_IS_MANAGED_BY_ONE_PARENT_NOTICE, first_custodian);
+  }
+  return l10n_util::GetStringFUTF16(
+      IDS_ASH_CHILD_USER_IS_MANAGED_BY_TWO_PARENTS_NOTICE, first_custodian,
+      second_custodian);
 }
 
 }  // namespace ash
diff --git a/ash/system/supervised/tray_supervised_user.h b/ash/system/supervised/tray_supervised_user.h
index 7b3a7c5..dd04292 100644
--- a/ash/system/supervised/tray_supervised_user.h
+++ b/ash/system/supervised/tray_supervised_user.h
@@ -6,60 +6,43 @@
 #define ASH_SYSTEM_SUPERVISED_TRAY_SUPERVISED_USER_H_
 
 #include "ash/ash_export.h"
-#include "ash/system/supervised/custodian_info_tray_observer.h"
+#include "ash/session/session_observer.h"
 #include "ash/system/tray/system_tray_item.h"
-#include "ash/system/tray/view_click_listener.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
 
-namespace gfx {
-struct VectorIcon;
-}  // namespace gfx
-
 namespace ash {
-class LabelTrayView;
+
 class SystemTray;
 
+// System tray item that shows a message if the user is supervised or a child.
+// Also shows a notification on login if the user is supervised. Shows a new
+// notification if the user manager/custodian changes.
 class ASH_EXPORT TraySupervisedUser : public SystemTrayItem,
-                                      public ViewClickListener,
-                                      public CustodianInfoTrayObserver {
+                                      public SessionObserver {
  public:
   explicit TraySupervisedUser(SystemTray* system_tray);
   ~TraySupervisedUser() override;
 
-  // If message is not empty updates content of default view, otherwise hides
-  // tray items.
-  void UpdateMessage();
-
-  // Overridden from SystemTrayItem.
+  // SystemTrayItem:
   views::View* CreateDefaultView(LoginStatus status) override;
-  void DestroyDefaultView() override;
-  void UpdateAfterLoginStatusChange(LoginStatus status) override;
 
-  // Overridden from ViewClickListener.
-  void OnViewClicked(views::View* sender) override;
-
-  // Overridden from CustodianInfoTrayObserver:
-  void OnCustodianInfoChanged() override;
+  // SessionObserver:
+  void OnUserSessionUpdated(const AccountId& account_id) override;
 
  private:
   friend class TraySupervisedUserTest;
 
   static const char kNotificationId[];
 
-  void CreateOrUpdateNotification(const base::string16& new_message);
+  void CreateOrUpdateNotification();
 
-  void CreateOrUpdateSupervisedWarningNotification();
+  base::string16 GetSupervisedUserMessage() const;
 
-  const gfx::VectorIcon& GetSupervisedUserIcon() const;
+  std::string custodian_email_;
+  std::string second_custodian_email_;
 
-  LabelTrayView* tray_view_;
-
-  // Previous login status to avoid showing notification upon unlock.
-  LoginStatus status_;
-
-  // Previous user supervised state to avoid showing notification upon unlock.
-  bool is_user_supervised_;
+  ScopedSessionObserver scoped_session_observer_;
 
   DISALLOW_COPY_AND_ASSIGN(TraySupervisedUser);
 };
diff --git a/ash/system/supervised/tray_supervised_user_unittest.cc b/ash/system/supervised/tray_supervised_user_unittest.cc
index 37c96c9..6907a05 100644
--- a/ash/system/supervised/tray_supervised_user_unittest.cc
+++ b/ash/system/supervised/tray_supervised_user_unittest.cc
@@ -7,13 +7,18 @@
 #include "ash/login_status.h"
 #include "ash/session/session_controller.h"
 #include "ash/shell.h"
+#include "ash/system/tray/label_tray_view.h"
+#include "ash/system/tray/system_tray.h"
 #include "ash/test/ash_test_base.h"
 #include "ash/test/test_session_controller_client.h"
 #include "ash/test/test_system_tray_delegate.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/utf_string_conversions.h"
 #include "ui/message_center/message_center.h"
 #include "ui/message_center/notification.h"
 #include "ui/message_center/notification_list.h"
 #include "ui/message_center/notification_types.h"
+#include "ui/views/view.h"
 
 using message_center::NotificationList;
 
@@ -54,13 +59,71 @@
   // Simulate a supervised user logging in.
   test::TestSessionControllerClient* client = GetSessionControllerClient();
   client->Reset();
-  client->AddUserSession("user1@test.com", user_manager::USER_TYPE_SUPERVISED);
+  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
   client->SetSessionState(session_manager::SessionState::ACTIVE);
 
+  // No notification because custodian email not available yet.
   message_center::Notification* notification = GetPopup();
-  ASSERT_NE(static_cast<message_center::Notification*>(NULL), notification);
+  EXPECT_FALSE(notification);
+
+  // Update the user session with the custodian data (which happens after the
+  // profile loads).
+  mojom::UserSessionPtr user_session = session->GetUserSession(0)->Clone();
+  user_session->custodian_email = "parent1@test.com";
+  session->UpdateUserSession(std::move(user_session));
+
+  // Notification is shown.
+  notification = GetPopup();
+  ASSERT_TRUE(notification);
   EXPECT_EQ(static_cast<int>(message_center::SYSTEM_PRIORITY),
             notification->rich_notification_data().priority);
+  EXPECT_EQ(
+      "Usage and history of this user can be reviewed by the manager "
+      "(parent1@test.com) on chrome.com.",
+      UTF16ToUTF8(notification->message()));
+
+  // Update the user session with new custodian data.
+  user_session = session->GetUserSession(0)->Clone();
+  user_session->custodian_email = "parent2@test.com";
+  session->UpdateUserSession(std::move(user_session));
+
+  // Notification is shown with updated message.
+  notification = GetPopup();
+  ASSERT_TRUE(notification);
+  EXPECT_EQ(
+      "Usage and history of this user can be reviewed by the manager "
+      "(parent2@test.com) on chrome.com.",
+      UTF16ToUTF8(notification->message()));
+}
+
+// Verifies an item is created for a supervised user.
+TEST_F(TraySupervisedUserTest, CreateDefaultView) {
+  TraySupervisedUser* tray =
+      GetPrimarySystemTray()->GetTraySupervisedUserForTesting();
+  SessionController* session = Shell::Get()->session_controller();
+  ASSERT_FALSE(session->IsActiveUserSessionStarted());
+
+  // Before login there is no supervised user item.
+  const LoginStatus unused = LoginStatus::NOT_LOGGED_IN;
+  EXPECT_FALSE(tray->CreateDefaultView(unused));
+
+  // Simulate a supervised user logging in.
+  test::TestSessionControllerClient* client = GetSessionControllerClient();
+  client->Reset();
+  client->AddUserSession("child@test.com", user_manager::USER_TYPE_SUPERVISED);
+  client->SetSessionState(session_manager::SessionState::ACTIVE);
+  mojom::UserSessionPtr user_session = session->GetUserSession(0)->Clone();
+  user_session->custodian_email = "parent@test.com";
+  session->UpdateUserSession(std::move(user_session));
+
+  // Now there is a supervised user item.
+  std::unique_ptr<views::View> view =
+      base::WrapUnique(tray->CreateDefaultView(unused));
+  ASSERT_TRUE(view);
+  EXPECT_EQ(
+      "Usage and history of this user can be reviewed by the manager "
+      "(parent@test.com) on chrome.com.",
+      UTF16ToUTF8(static_cast<LabelTrayView*>(view.get())->message()));
 }
 
 }  // namespace ash
diff --git a/ash/system/tray/hover_highlight_view.h b/ash/system/tray/hover_highlight_view.h
index 300a6e3..bdaf647 100644
--- a/ash/system/tray/hover_highlight_view.h
+++ b/ash/system/tray/hover_highlight_view.h
@@ -34,6 +34,7 @@
     UNCHECKED_CHECKBOX
   };
 
+  // If |listener| is null then no action is taken on click.
   explicit HoverHighlightView(ViewClickListener* listener);
   ~HoverHighlightView() override;
 
diff --git a/ash/system/tray/label_tray_view.h b/ash/system/tray/label_tray_view.h
index 3955733..cb8346e92 100644
--- a/ash/system/tray/label_tray_view.h
+++ b/ash/system/tray/label_tray_view.h
@@ -21,8 +21,12 @@
 // is supported.
 class LabelTrayView : public views::View {
  public:
+  // If |click_listener| is null then no action is taken on click.
   LabelTrayView(ViewClickListener* click_listener, const gfx::VectorIcon& icon);
   ~LabelTrayView() override;
+
+  const base::string16& message() { return message_; }
+
   void SetMessage(const base::string16& message);
 
  private:
diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc
index f1909a0..b46a2da7 100644
--- a/ash/system/tray/system_tray.cc
+++ b/ash/system/tray/system_tray.cc
@@ -256,7 +256,8 @@
 
   AddTrayItem(base::MakeUnique<TraySessionLengthLimit>(this));
   AddTrayItem(base::MakeUnique<TrayEnterprise>(this));
-  AddTrayItem(base::MakeUnique<TraySupervisedUser>(this));
+  tray_supervised_user_ = new TraySupervisedUser(this);
+  AddTrayItem(base::WrapUnique(tray_supervised_user_));
   AddTrayItem(base::MakeUnique<TrayIME>(this));
   AddTrayItem(base::WrapUnique(tray_accessibility_));
   AddTrayItem(base::MakeUnique<TrayTracing>(this));
@@ -304,7 +305,6 @@
   if (tray_item) {
     tray_container()->AddChildViewAt(tray_item, 0);
     PreferredSizeChanged();
-    tray_item_map_[item_ptr] = tray_item;
   }
 }
 
@@ -612,12 +612,6 @@
   HideBubbleWithView(bubble_view);
 }
 
-views::View* SystemTray::GetTrayItemViewForTest(SystemTrayItem* item) {
-  std::map<SystemTrayItem*, views::View*>::iterator it =
-      tray_item_map_.find(item);
-  return it == tray_item_map_.end() ? NULL : it->second;
-}
-
 TrayCast* SystemTray::GetTrayCastForTesting() const {
   return tray_cast_;
 }
@@ -626,6 +620,10 @@
   return tray_network_;
 }
 
+TraySupervisedUser* SystemTray::GetTraySupervisedUserForTesting() const {
+  return tray_supervised_user_;
+}
+
 TraySystemInfo* SystemTray::GetTraySystemInfoForTesting() const {
   return tray_system_info_;
 }
diff --git a/ash/system/tray/system_tray.h b/ash/system/tray/system_tray.h
index 603997a..0ea8505 100644
--- a/ash/system/tray/system_tray.h
+++ b/ash/system/tray/system_tray.h
@@ -28,6 +28,7 @@
 class TrayAudio;
 class TrayCast;
 class TrayNetwork;
+class TraySupervisedUser;
 class TraySystemInfo;
 class TrayTiles;
 class TrayUpdate;
@@ -140,11 +141,9 @@
     return tray_accessibility_;
   }
 
-  // Get the tray item view (or NULL) for a given |tray_item| in a unit test.
-  views::View* GetTrayItemViewForTest(SystemTrayItem* tray_item);
-
   TrayCast* GetTrayCastForTesting() const;
   TrayNetwork* GetTrayNetworkForTesting() const;
+  TraySupervisedUser* GetTraySupervisedUserForTesting() const;
   TraySystemInfo* GetTraySystemInfoForTesting() const;
   TrayTiles* GetTrayTilesForTesting() const;
 
@@ -210,9 +209,6 @@
   // Pointers to members of |items_|.
   SystemTrayItem* detailed_item_ = nullptr;
 
-  // Mappings of system tray item and it's view in the tray.
-  std::map<SystemTrayItem*, views::View*> tray_item_map_;
-
   // Bubble for default and detailed views.
   std::unique_ptr<SystemBubbleWrapper> system_bubble_;
 
@@ -231,6 +227,7 @@
   TrayCast* tray_cast_ = nullptr;
   TrayNetwork* tray_network_ = nullptr;
   TrayTiles* tray_tiles_ = nullptr;
+  TraySupervisedUser* tray_supervised_user_ = nullptr;
   TraySystemInfo* tray_system_info_ = nullptr;
   TrayUpdate* tray_update_ = nullptr;
 
diff --git a/ash/system/tray/system_tray_delegate.cc b/ash/system/tray/system_tray_delegate.cc
index 7868d10..5fc51d91 100644
--- a/ash/system/tray/system_tray_delegate.cc
+++ b/ash/system/tray/system_tray_delegate.cc
@@ -31,18 +31,6 @@
   return base::string16();
 }
 
-std::string SystemTrayDelegate::GetSupervisedUserManager() const {
-  return std::string();
-}
-
-base::string16 SystemTrayDelegate::GetSupervisedUserManagerName() const {
-  return base::string16();
-}
-
-base::string16 SystemTrayDelegate::GetSupervisedUserMessage() const {
-  return base::string16();
-}
-
 void SystemTrayDelegate::ShowEnterpriseInfo() {}
 
 void SystemTrayDelegate::ShowUserLogin() {}
@@ -82,12 +70,6 @@
   return false;
 }
 
-void SystemTrayDelegate::AddCustodianInfoTrayObserver(
-    CustodianInfoTrayObserver* observer) {}
-
-void SystemTrayDelegate::RemoveCustodianInfoTrayObserver(
-    CustodianInfoTrayObserver* observer) {}
-
 std::unique_ptr<SystemTrayItem> SystemTrayDelegate::CreateRotationLockTrayItem(
     SystemTray* tray) {
   return nullptr;
diff --git a/ash/system/tray/system_tray_delegate.h b/ash/system/tray/system_tray_delegate.h
index 5056e57..1f66cf6 100644
--- a/ash/system/tray/system_tray_delegate.h
+++ b/ash/system/tray/system_tray_delegate.h
@@ -25,7 +25,6 @@
 struct IMEInfo;
 struct IMEPropertyInfo;
 
-class CustodianInfoTrayObserver;
 class SystemTray;
 class SystemTrayItem;
 
@@ -37,15 +36,10 @@
 // SystemTrayDelegate is intended for delegating tasks in the System Tray to the
 // application (e.g. Chrome). These tasks should be limited to application
 // (browser) specific tasks. For non application specific tasks, where possible,
-// components/, chromeos/, device/, etc., code should be used directly. If more
-// than one related method is being added, consider adding an additional
-// specific delegate (e.g. CastConfigDelegate).
+// components/, chromeos/, device/, etc., code should be used directly.
 //
-// These methods should all have trivial default implementations for platforms
-// that do not implement the method (e.g. return false or nullptr). This
-// eliminates the need to propagate default implementations across the various
-// implementations of this class. Consumers of this delegate should handle the
-// default return value (e.g. nullptr).
+// DEPRECATED: This class is being replaced with SystemTrayController and
+// SessionController to support mash/mustash. Add new code to those classes.
 class ASH_EXPORT SystemTrayDelegate {
  public:
   SystemTrayDelegate();
@@ -67,19 +61,6 @@
   // Returns notification for enterprise enrolled devices.
   virtual base::string16 GetEnterpriseMessage() const;
 
-  // Returns the display email of the user that manages the current supervised
-  // user.
-  // TODO(jamescook): Migrate to SessionController. http://crbug.com/712799
-  virtual std::string GetSupervisedUserManager() const;
-
-  // Returns the name of the user that manages the current supervised user.
-  // TODO(jamescook): Migrate to SessionController. http://crbug.com/712799
-  virtual base::string16 GetSupervisedUserManagerName() const;
-
-  // Returns the notification for supervised users.
-  // TODO(jamescook): Migrate to SessionController. http://crbug.com/712799
-  virtual base::string16 GetSupervisedUserMessage() const;
-
   // Shows information about enterprise enrolled devices.
   virtual void ShowEnterpriseInfo();
 
@@ -121,13 +102,6 @@
   // Returns true when the Search key is configured to be treated as Caps Lock.
   virtual bool IsSearchKeyMappedToCapsLock();
 
-  // Adding observers that are notified when supervised info is being changed.
-  virtual void AddCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer);
-
-  virtual void RemoveCustodianInfoTrayObserver(
-      CustodianInfoTrayObserver* observer);
-
   // Creates a system tray item for display rotation lock.
   // TODO(jamescook): Remove this when mus has support for display management
   // and we have a DisplayManager equivalent. See http://crbug.com/548429
diff --git a/ash/test/test_system_tray_delegate.cc b/ash/test/test_system_tray_delegate.cc
index 0f19c35c..d3ea55c 100644
--- a/ash/test/test_system_tray_delegate.cc
+++ b/ash/test/test_system_tray_delegate.cc
@@ -40,12 +40,6 @@
   return Shell::Get()->session_controller()->login_status();
 }
 
-std::string TestSystemTrayDelegate::GetSupervisedUserManager() const {
-  if (!Shell::Get()->session_controller()->IsUserSupervised())
-    return std::string();
-  return "manager@chrome.com";
-}
-
 bool TestSystemTrayDelegate::GetSessionStartTime(
     base::TimeTicks* session_start_time) {
   // Just returns TimeTicks::Now(), so the remaining time is always the
diff --git a/ash/test/test_system_tray_delegate.h b/ash/test/test_system_tray_delegate.h
index 1a4cf6aa..b9e8f0c 100644
--- a/ash/test/test_system_tray_delegate.h
+++ b/ash/test/test_system_tray_delegate.h
@@ -33,7 +33,6 @@
 
   // SystemTrayDelegate:
   LoginStatus GetUserLoginStatus() const override;
-  std::string GetSupervisedUserManager() const override;
   bool GetSessionStartTime(base::TimeTicks* session_start_time) override;
   bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) override;
   void GetCurrentIME(IMEInfo* info) override;
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 4c9541cb..70cd53b6 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -2062,15 +2062,6 @@
   <message name="IDS_CREATE_SUPERVISED_USER_CREATION_CREATION_TIMEOUT_MESSAGE" desc="Text shown at the place of progess indicator if creation takes too long and should be cancelled.">
     This is taking much longer than usual. You can keep waiting, or cancel and try again later.
   </message>
-  <message name="IDS_USER_IS_SUPERVISED_BY_NOTICE" desc="Text for notifications showing that this user is supervised">
-    Usage and history of this user can be reviewed by the manager (<ph name="MANAGER_EMAIL">$1<ex>user@example.com</ex></ph>) on chrome.com.
-  </message>
-  <message name="IDS_CHILD_USER_IS_MANAGED_BY_ONE_PARENT_NOTICE" desc="Text for notifications showing that this child user is managed by parent's account.">
-    This is an account for kids managed by <ph name="MANAGER_EMAIL">$1<ex>user@example.com</ex></ph>
-  </message>
-  <message name="IDS_CHILD_USER_IS_MANAGED_BY_TWO_PARENTS_NOTICE" desc="Text for notifications showing that this child user is managed by two parents' accounts.">
-    This is an account for kids managed by <ph name="FIRST_PARENT_EMAIL">$1<ex>first@example.com</ex></ph> and <ph name="SECOND_PARENT_EMAIL">$2<ex>second@example.com</ex></ph>
-  </message>
   <message name="IDS_SUPERVISED_USER_EXPIRED_TOKEN_WARNING" desc="Warning text that is shown on login screen trying to sign in as a supervised user that has expired token">
     This supervised user may have been deleted or disabled by the manager. Please contact the manager if you would like to continue signing in as this user.
   </message>
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
index d731dd1a..3e220e4c 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.cc
@@ -103,6 +103,16 @@
   return user;
 }
 
+user_manager::User* FakeChromeUserManager::AddSupervisedUser(
+    const AccountId& account_id) {
+  user_manager::User* user =
+      user_manager::User::CreateSupervisedUser(account_id);
+  user->set_username_hash(ProfileHelper::GetUserIdHashByUserIdForTesting(
+      account_id.GetUserEmail()));
+  users_.push_back(user);
+  return user;
+}
+
 const user_manager::User* FakeChromeUserManager::AddPublicAccountUser(
     const AccountId& account_id) {
   user_manager::User* user =
@@ -126,6 +136,14 @@
   UserLoggedIn(account_id, ProfileHelper::GetUserIdHashByUserIdForTesting(
                                account_id.GetUserEmail()),
                false /* browser_restart */);
+
+  // NOTE: This does not match production. See function comment.
+  for (auto* user : users_) {
+    if (user->GetAccountId() == account_id) {
+      user->set_profile_is_created();
+      break;
+    }
+  }
 }
 
 BootstrapManager* FakeChromeUserManager::GetBootstrapManager() {
@@ -370,7 +388,6 @@
   for (auto* user : users_) {
     if (user->username_hash() == username_hash) {
       user->set_is_logged_in(true);
-      user->set_profile_is_created();
       logged_in_users_.push_back(user);
 
       if (!primary_user_)
@@ -378,6 +395,7 @@
       break;
     }
   }
+  // TODO(jamescook): This should set active_user_ and call NotifyOnLogin().
 }
 
 void FakeChromeUserManager::SwitchToLastActiveUser() {
diff --git a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
index 0e814ae9..8f5e7e360 100644
--- a/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
+++ b/chrome/browser/chromeos/login/users/fake_chrome_user_manager.h
@@ -28,14 +28,16 @@
   FakeChromeUserManager();
   ~FakeChromeUserManager() override;
 
-  // Create and add a kiosk app user.
+  // Create and add various types of users.
   user_manager::User* AddKioskAppUser(const AccountId& account_id);
   user_manager::User* AddArcKioskAppUser(const AccountId& account_id);
-
-  // Create and add a public account user.
+  user_manager::User* AddSupervisedUser(const AccountId& account_id);
   const user_manager::User* AddPublicAccountUser(const AccountId& account_id);
 
   // Calculates the user name hash and calls UserLoggedIn to login a user.
+  // Sets the user as having its profile created, but does not create a profile.
+  // NOTE: This does not match production, which first logs in the user, then
+  // creates the profile and updates the user later.
   void LoginUser(const AccountId& account_id);
 
   const user_manager::User* AddUser(const AccountId& account_id);
diff --git a/chrome/browser/ui/ash/session_controller_client.cc b/chrome/browser/ui/ash/session_controller_client.cc
index 693ad99..4bee31c 100644
--- a/chrome/browser/ui/ash/session_controller_client.cc
+++ b/chrome/browser/ui/ash/session_controller_client.cc
@@ -12,12 +12,15 @@
 #include "base/bind.h"
 #include "base/logging.h"
 #include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/chrome_notification_types.h"
 #include "chrome/browser/chromeos/login/user_flow.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/browser/ui/ash/multi_user/user_switch_util.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/grit/theme_resources.h"
@@ -72,6 +75,17 @@
         IDR_PROFILE_PICTURE_LOADING);
   }
 
+  if (user.IsSupervised()) {
+    Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(&user);
+    if (profile) {
+      SupervisedUserService* service =
+          SupervisedUserServiceFactory::GetForProfile(profile);
+      session->custodian_email = service->GetCustodianEmailAddress();
+      session->second_custodian_email =
+          service->GetSecondCustodianEmailAddress();
+    }
+  }
+
   chromeos::UserFlow* const user_flow =
       chromeos::ChromeUserManager::Get()->GetUserFlow(user.GetAccountId());
   session->should_enable_settings = user_flow->ShouldEnableSettings();
@@ -87,18 +101,16 @@
 
 }  // namespace
 
-SessionControllerClient::SessionControllerClient() : binding_(this) {
+SessionControllerClient::SessionControllerClient()
+    : binding_(this), weak_ptr_factory_(this) {
   SessionManager::Get()->AddObserver(this);
   UserManager::Get()->AddSessionStateObserver(this);
   UserManager::Get()->AddObserver(this);
 
   registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
                  content::NotificationService::AllSources());
-
-  ConnectToSessionControllerAndSetClient();
-  SendSessionInfoIfChanged();
-  // User sessions and their order will be sent via UserSessionStateObserver
-  // even for crash-n-restart.
+  registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED,
+                 content::NotificationService::AllSources());
 
   DCHECK(!g_instance);
   g_instance = this;
@@ -108,11 +120,24 @@
   DCHECK_EQ(this, g_instance);
   g_instance = nullptr;
 
+  if (supervised_user_profile_) {
+    SupervisedUserServiceFactory::GetForProfile(supervised_user_profile_)
+        ->RemoveObserver(this);
+  }
+
   SessionManager::Get()->RemoveObserver(this);
   UserManager::Get()->RemoveObserver(this);
   UserManager::Get()->RemoveSessionStateObserver(this);
 }
 
+void SessionControllerClient::Init() {
+  ConnectToSessionController();
+  session_controller_->SetClient(binding_.CreateInterfacePtrAndBind());
+  SendSessionInfoIfChanged();
+  // User sessions and their order will be sent via UserSessionStateObserver
+  // even for crash-n-restart.
+}
+
 // static
 SessionControllerClient* SessionControllerClient::Get() {
   return g_instance;
@@ -273,21 +298,69 @@
   SendSessionInfoIfChanged();
 }
 
+void SessionControllerClient::OnCustodianInfoChanged() {
+  DCHECK(supervised_user_profile_);
+  User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(
+      supervised_user_profile_);
+  if (user)
+    SendUserSession(*user);
+}
+
 void SessionControllerClient::Observe(
     int type,
     const content::NotificationSource& source,
     const content::NotificationDetails& details) {
-  DCHECK_EQ(chrome::NOTIFICATION_APP_TERMINATING, type);
-  session_controller_->NotifyChromeTerminating();
+  switch (type) {
+    case chrome::NOTIFICATION_APP_TERMINATING:
+      session_controller_->NotifyChromeTerminating();
+      break;
+    case chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED: {
+      Profile* profile = content::Details<Profile>(details).ptr();
+      OnLoginUserProfilePrepared(profile);
+      break;
+    }
+    default:
+      NOTREACHED() << "Unexpected notification " << type;
+      break;
+  }
 }
 
-void SessionControllerClient::ConnectToSessionControllerAndSetClient() {
+void SessionControllerClient::OnLoginUserProfilePrepared(Profile* profile) {
+  const User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  DCHECK(user);
+
+  if (profile->IsSupervised()) {
+    // There can be only one supervised user per session.
+    DCHECK(!supervised_user_profile_);
+    supervised_user_profile_ = profile;
+
+    // Watch for changes to supervised user manager/custodians.
+    SupervisedUserServiceFactory::GetForProfile(supervised_user_profile_)
+        ->AddObserver(this);
+  }
+
+  // Needed because the user-to-profile mapping isn't available until later,
+  // which is needed in UserToUserSession().
+  base::ThreadTaskRunnerHandle::Get()->PostTask(
+      FROM_HERE, base::Bind(&SessionControllerClient::SendUserSessionForProfile,
+                            weak_ptr_factory_.GetWeakPtr(), profile));
+}
+
+void SessionControllerClient::SendUserSessionForProfile(Profile* profile) {
+  DCHECK(profile);
+  const User* user = chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
+  DCHECK(user);
+  SendUserSession(*user);
+}
+
+void SessionControllerClient::ConnectToSessionController() {
+  // Tests may bind to their own SessionController.
+  if (session_controller_)
+    return;
+
   content::ServiceManagerConnection::GetForProcess()
       ->GetConnector()
       ->BindInterface(ash::mojom::kServiceName, &session_controller_);
-
-  // Set as |session_controller_|'s client.
-  session_controller_->SetClient(binding_.CreateInterfacePtrAndBind());
 }
 
 void SessionControllerClient::SendSessionInfoIfChanged() {
@@ -315,6 +388,9 @@
   if (!user_session)
     return;
 
+  // TODO(jamescook): Only send if it changed. This will require an Equals()
+  // method for gfx::ImageSkia to allow mojom::UserSession comparison.
+  // http://crbug.com/714689
   session_controller_->UpdateUserSession(std::move(user_session));
 }
 
diff --git a/chrome/browser/ui/ash/session_controller_client.h b/chrome/browser/ui/ash/session_controller_client.h
index bad771a1..7dc91e1 100644
--- a/chrome/browser/ui/ash/session_controller_client.h
+++ b/chrome/browser/ui/ash/session_controller_client.h
@@ -7,13 +7,18 @@
 
 #include "ash/public/interfaces/session_controller.mojom.h"
 #include "base/callback_forward.h"
+#include "base/gtest_prod_util.h"
 #include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "components/session_manager/core/session_manager_observer.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/browser/notification_observer.h"
 #include "content/public/browser/notification_registrar.h"
 #include "mojo/public/cpp/bindings/binding.h"
 
+class Profile;
+
 namespace ash {
 enum class AddUserSessionPolicy;
 }
@@ -30,11 +35,14 @@
       public user_manager::UserManager::UserSessionStateObserver,
       public user_manager::UserManager::Observer,
       public session_manager::SessionManagerObserver,
+      public SupervisedUserServiceObserver,
       public content::NotificationObserver {
  public:
   SessionControllerClient();
   ~SessionControllerClient() override;
 
+  void Init();
+
   static SessionControllerClient* Get();
 
   // Calls SessionController to start locking ash. |callback| will be invoked
@@ -64,6 +72,9 @@
   // session_manager::SessionManagerObserver:
   void OnSessionStateChanged() override;
 
+  // SupervisedUserServiceObserver:
+  void OnCustodianInfoChanged() override;
+
   // content::NotificationObserver:
   void Observe(int type,
                const content::NotificationSource& source,
@@ -81,9 +92,16 @@
   static void FlushForTesting();
 
  private:
-  // Connects or reconnects to the |session_controller_| interface and set
-  // this object as its client.
-  void ConnectToSessionControllerAndSetClient();
+  FRIEND_TEST_ALL_PREFIXES(SessionControllerClientTest, SupervisedUser);
+
+  // Called when the login profile is ready.
+  void OnLoginUserProfilePrepared(Profile* profile);
+
+  // Sends the user session info for a given profile.
+  void SendUserSessionForProfile(Profile* profile);
+
+  // Connects to the |session_controller_| interface.
+  void ConnectToSessionController();
 
   // Sends session info to ash.
   void SendSessionInfoIfChanged();
@@ -103,11 +121,16 @@
   // Whether the primary user session info is sent to ash.
   bool primary_user_session_sent_ = false;
 
-  // For observing NOTIFICATION_APP_TERMINATING.
+  // If the session is for a supervised user, the profile of that user.
+  // Chrome OS only supports a single supervised user in a session.
+  Profile* supervised_user_profile_ = nullptr;
+
   content::NotificationRegistrar registrar_;
 
   ash::mojom::SessionInfoPtr last_sent_session_info_;
 
+  base::WeakPtrFactory<SessionControllerClient> weak_ptr_factory_;
+
   DISALLOW_COPY_AND_ASSIGN(SessionControllerClient);
 };
 
diff --git a/chrome/browser/ui/ash/session_controller_client_unittest.cc b/chrome/browser/ui/ash/session_controller_client_unittest.cc
index 6610343..d67ace3 100644
--- a/chrome/browser/ui/ash/session_controller_client_unittest.cc
+++ b/chrome/browser/ui/ash/session_controller_client_unittest.cc
@@ -17,9 +17,12 @@
 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
 #include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/supervised_user/supervised_user_service.h"
+#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
 #include "chrome/common/pref_names.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/session_manager/core/session_manager.h"
 #include "components/signin/core/account_id/account_id.h"
 #include "components/user_manager/user_manager.h"
 #include "content/public/test/test_browser_thread_bundle.h"
@@ -45,6 +48,69 @@
       user_manager::UserManager::Get());
 }
 
+// A user manager that does not set profiles as loaded and notifies observers
+// when users being added to a session.
+class TestChromeUserManager : public FakeChromeUserManager {
+ public:
+  TestChromeUserManager() = default;
+  ~TestChromeUserManager() override = default;
+
+  // user_manager::UserManager:
+  void UserLoggedIn(const AccountId& account_id,
+                    const std::string& user_id_hash,
+                    bool browser_restart) override {
+    FakeChromeUserManager::UserLoggedIn(account_id, user_id_hash,
+                                        browser_restart);
+    active_user_ = const_cast<user_manager::User*>(FindUser(account_id));
+    NotifyOnLogin();
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestChromeUserManager);
+};
+
+// A session controller interface implementation that tracks sessions and users.
+class TestSessionController : public ash::mojom::SessionController {
+ public:
+  TestSessionController() : binding_(this) {}
+  ~TestSessionController() override {}
+
+  ash::mojom::SessionControllerPtr CreateInterfacePtrAndBind() {
+    return binding_.CreateInterfacePtrAndBind();
+  }
+
+  ash::mojom::SessionInfo* last_session_info() {
+    return last_session_info_.get();
+  }
+
+  ash::mojom::UserSession* last_user_session() {
+    return last_user_session_.get();
+  }
+
+  // ash::mojom::SessionController:
+  void SetClient(ash::mojom::SessionControllerClientPtr client) override {}
+  void SetSessionInfo(ash::mojom::SessionInfoPtr info) override {
+    last_session_info_ = info->Clone();
+  }
+  void UpdateUserSession(ash::mojom::UserSessionPtr user_session) override {
+    last_user_session_ = user_session->Clone();
+  }
+  void SetUserSessionOrder(
+      const std::vector<uint32_t>& user_session_order) override {}
+  void StartLock(const StartLockCallback& callback) override {}
+  void RunUnlockAnimation(const RunUnlockAnimationCallback& callback) override {
+  }
+  void NotifyChromeTerminating() override {}
+
+ private:
+  mojo::Binding<ash::mojom::SessionController> binding_;
+
+  ash::mojom::SessionInfoPtr last_session_info_;
+  ash::mojom::UserSessionPtr last_user_session_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestSessionController);
+};
+
 }  // namespace
 
 class SessionControllerClientTest : public testing::Test {
@@ -53,17 +119,19 @@
   ~SessionControllerClientTest() override {}
 
   void SetUp() override {
-    // Initialize the UserManager singleton to a fresh FakeChromeUserManager
-    // instance.
-    user_manager_ = new FakeChromeUserManager;
+    testing::Test::SetUp();
+
+    // Initialize the UserManager singleton.
+    user_manager_ = new TestChromeUserManager;
     user_manager_enabler_.reset(
         new chromeos::ScopedUserManagerEnabler(user_manager_));
 
-    testing::Test::SetUp();
+    profile_manager_.reset(
+        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
+    ASSERT_TRUE(profile_manager_->SetUp());
   }
 
   void TearDown() override {
-    testing::Test::TearDown();
     user_manager_enabler_.reset();
     user_manager_ = nullptr;
     // Clear our cached pointer to the PolicyCertVerifier.
@@ -77,6 +145,8 @@
     // PolicyCertService::OnTrustAnchorsChanged() which is called from
     // PolicyCertService::Shutdown()).
     base::RunLoop().RunUntilIdle();
+
+    testing::Test::TearDown();
   }
 
   // Add and log in a user to the session.
@@ -93,17 +163,20 @@
         .GetUserEmail();
   }
 
-  FakeChromeUserManager* user_manager() { return user_manager_; }
+  TestChromeUserManager* user_manager() { return user_manager_; }
 
+  // Adds a regular user with a profile.
   void InitForMultiProfile() {
-    profile_manager_.reset(
-        new TestingProfileManager(TestingBrowserProcess::GetGlobal()));
-    ASSERT_TRUE(profile_manager_->SetUp());
-
     const AccountId account_id(AccountId::FromUserEmail(kUser));
     const user_manager::User* user = user_manager()->AddUser(account_id);
 
     // Note that user profiles are created after user login in reality.
+    CreateTestingProfile(user);
+  }
+
+  // Calls private methods to create a testing profile.
+  void CreateTestingProfile(const user_manager::User* user) {
+    const AccountId& account_id = user->GetAccountId();
     user_profile_ =
         profile_manager_->CreateTestingProfile(account_id.GetUserEmail());
     user_profile_->set_profile_name(account_id.GetUserEmail());
@@ -115,12 +188,13 @@
   std::unique_ptr<policy::PolicyCertVerifier> cert_verifier_;
   std::unique_ptr<TestingProfileManager> profile_manager_;
   TestingProfile* user_profile_;
+  session_manager::SessionManager session_manager_;
 
  private:
   std::unique_ptr<chromeos::ScopedUserManagerEnabler> user_manager_enabler_;
 
   // Owned by |user_manager_enabler_|.
-  FakeChromeUserManager* user_manager_ = nullptr;
+  TestChromeUserManager* user_manager_ = nullptr;
 
   DISALLOW_COPY_AND_ASSIGN(SessionControllerClientTest);
 };
@@ -141,9 +215,11 @@
   UserAddedToSession("firstuser@test.com");
   UserAddedToSession("seconduser@test.com");
   UserAddedToSession("thirduser@test.com");
-  const ash::CycleUserDirection forward = ash::CycleUserDirection::NEXT;
+  user_manager()->SwitchActiveUser(
+      AccountId::FromUserEmail("firstuser@test.com"));
 
   // Cycle forward.
+  const ash::CycleUserDirection forward = ash::CycleUserDirection::NEXT;
   EXPECT_EQ("firstuser@test.com", GetActiveUserEmail());
   SessionControllerClient::DoCycleActiveUser(forward);
   EXPECT_EQ("seconduser@test.com", GetActiveUserEmail());
@@ -283,3 +359,64 @@
   EXPECT_EQ(ash::AddUserSessionPolicy::ERROR_NOT_ALLOWED_PRIMARY_USER,
             SessionControllerClient::GetAddUserSessionPolicy());
 }
+
+TEST_F(SessionControllerClientTest, SupervisedUser) {
+  // Create an object to test and connect it to our test interface.
+  SessionControllerClient client;
+  TestSessionController session_controller;
+  client.session_controller_ = session_controller.CreateInterfacePtrAndBind();
+  client.Init();
+  SessionControllerClient::FlushForTesting();
+
+  // Simulate the login screen. No user session yet.
+  session_manager_.SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  EXPECT_FALSE(session_controller.last_user_session());
+
+  // Simulate a supervised user logging in.
+  const AccountId account_id(AccountId::FromUserEmail("child@test.com"));
+  const user_manager::User* user =
+      user_manager()->AddSupervisedUser(account_id);
+  ASSERT_TRUE(user);
+
+  // Start session. This logs in the user and sends an active user notification.
+  // The hash must match the one used by FakeChromeUserManager.
+  session_manager_.CreateSession(
+      account_id, chromeos::ProfileHelper::GetUserIdHashByUserIdForTesting(
+                      "child@test.com"));
+  SessionControllerClient::FlushForTesting();
+
+  // The session controller received session info and user session.
+  EXPECT_LT(0u, session_controller.last_user_session()->session_id);
+  EXPECT_EQ(user_manager::USER_TYPE_SUPERVISED,
+            session_controller.last_user_session()->type);
+
+  // Simulate profile creation after login.
+  CreateTestingProfile(user);
+  user_profile_->SetSupervisedUserId("child-id");
+
+  // Simulate supervised user custodians.
+  PrefService* prefs = user_profile_->GetPrefs();
+  prefs->SetString(prefs::kSupervisedUserCustodianEmail, "parent1@test.com");
+  prefs->SetString(prefs::kSupervisedUserSecondCustodianEmail,
+                   "parent2@test.com");
+
+  // Simulate the notification that the profile is ready.
+  client.OnLoginUserProfilePrepared(user_profile_);
+  base::RunLoop().RunUntilIdle();  // For PostTask and mojo interface.
+
+  // The custodians were sent over the mojo interface.
+  EXPECT_EQ("parent1@test.com",
+            session_controller.last_user_session()->custodian_email);
+  EXPECT_EQ("parent2@test.com",
+            session_controller.last_user_session()->second_custodian_email);
+
+  // Simulate an update to the custodian information.
+  prefs->SetString(prefs::kSupervisedUserCustodianEmail, "parent3@test.com");
+  client.OnCustodianInfoChanged();
+  SessionControllerClient::FlushForTesting();
+
+  // The updated custodian was sent over the mojo interface.
+  EXPECT_EQ("parent3@test.com",
+            session_controller.last_user_session()->custodian_email);
+}
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
index edbf0cc..54beb0e 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.cc
@@ -39,7 +39,6 @@
 #include "chrome/browser/chromeos/login/help_app_launcher.h"
 #include "chrome/browser/chromeos/login/login_wizard.h"
 #include "chrome/browser/chromeos/login/ui/user_adding_screen.h"
-#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
 #include "chrome/browser/chromeos/profiles/multiprofiles_intro_dialog.h"
@@ -78,11 +77,6 @@
 #include "ui/chromeos/ime/input_method_menu_item.h"
 #include "ui/chromeos/ime/input_method_menu_manager.h"
 
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-#include "chrome/browser/supervised_user/supervised_user_service.h"
-#include "chrome/browser/supervised_user/supervised_user_service_factory.h"
-#endif
-
 namespace chromeos {
 
 namespace {
@@ -113,11 +107,6 @@
   return session_manager::SessionManager::Get()->IsInSecondaryLoginScreen();
 }
 
-bool IsUserSupervised() {
-  user_manager::User* user = user_manager::UserManager::Get()->GetActiveUser();
-  return user && user->IsSupervised();
-}
-
 }  // namespace
 
 SystemTrayDelegateChromeOS::SystemTrayDelegateChromeOS()
@@ -192,7 +181,6 @@
 
   BrowserList::RemoveObserver(this);
   StopObservingAppWindowRegistry();
-  StopObservingCustodianInfoChanges();
 
   policy::BrowserPolicyConnectorChromeOS* connector =
       g_browser_process->platform_part()->browser_policy_connector_chromeos();
@@ -220,30 +208,6 @@
   return base::string16();
 }
 
-std::string SystemTrayDelegateChromeOS::GetSupervisedUserManager() const {
-  if (!IsUserSupervised())
-    return std::string();
-  return SupervisedUserServiceFactory::GetForProfile(user_profile_)->
-      GetCustodianEmailAddress();
-}
-
-base::string16
-SystemTrayDelegateChromeOS::GetSupervisedUserManagerName() const {
-  if (!IsUserSupervised())
-    return base::string16();
-  return base::UTF8ToUTF16(SupervisedUserServiceFactory::GetForProfile(
-      user_profile_)->GetCustodianName());
-}
-
-base::string16 SystemTrayDelegateChromeOS::GetSupervisedUserMessage()
-    const {
-  if (!IsUserSupervised())
-    return base::string16();
-  if (user_manager::UserManager::Get()->IsLoggedInAsChildUser())
-    return GetChildUserMessage();
-  return GetLegacySupervisedUserMessage();
-}
-
 void SystemTrayDelegateChromeOS::ShowEnterpriseInfo() {
   // TODO(mash): Refactor out SessionStateDelegate and move to SystemTrayClient.
   ash::LoginStatus status = GetUserLoginStatus();
@@ -389,16 +353,6 @@
   return search_key_mapped_to_ == input_method::kCapsLockKey;
 }
 
-void SystemTrayDelegateChromeOS::AddCustodianInfoTrayObserver(
-    ash::CustodianInfoTrayObserver* observer) {
-  custodian_info_changed_observers_.AddObserver(observer);
-}
-
-void SystemTrayDelegateChromeOS::RemoveCustodianInfoTrayObserver(
-    ash::CustodianInfoTrayObserver* observer) {
-  custodian_info_changed_observers_.RemoveObserver(observer);
-}
-
 std::unique_ptr<ash::SystemTrayItem>
 SystemTrayDelegateChromeOS::CreateRotationLockTrayItem(ash::SystemTray* tray) {
   return base::MakeUnique<ash::TrayRotationLock>(tray);
@@ -412,17 +366,11 @@
   // Stop observing the AppWindowRegistry of the current |user_profile_|.
   StopObservingAppWindowRegistry();
 
-  // Stop observing custodian info changes of the current |user_profile_|.
-  StopObservingCustodianInfoChanges();
-
   user_profile_ = profile;
 
   // Start observing the AppWindowRegistry of the newly set |user_profile_|.
   extensions::AppWindowRegistry::Get(user_profile_)->AddObserver(this);
 
-  // Start observing custodian info changes of the newly set |user_profile_|.
-  SupervisedUserServiceFactory::GetForProfile(profile)->AddObserver(this);
-
   PrefService* prefs = profile->GetPrefs();
   user_pref_registrar_.reset(new PrefChangeRegistrar);
   user_pref_registrar_->Init(prefs);
@@ -458,7 +406,6 @@
   UpdateShowLogoutButtonInTray();
   UpdateLogoutDialogDuration();
   UpdatePerformanceTracing();
-  OnCustodianInfoChanged();
   search_key_mapped_to_ =
       profile->GetPrefs()->GetInteger(prefs::kLanguageRemapSearchKeyTo);
 }
@@ -523,16 +470,6 @@
     registry->RemoveObserver(this);
 }
 
-void SystemTrayDelegateChromeOS::StopObservingCustodianInfoChanges() {
-  if (!user_profile_)
-    return;
-
-  SupervisedUserService* service = SupervisedUserServiceFactory::GetForProfile(
-      user_profile_);
-  if (service)
-    service->RemoveObserver(this);
-}
-
 void SystemTrayDelegateChromeOS::NotifyIfLastWindowClosed() {
   if (!user_profile_)
     return;
@@ -654,14 +591,6 @@
   NotifyIfLastWindowClosed();
 }
 
-// Overridden from SupervisedUserServiceObserver.
-void SystemTrayDelegateChromeOS::OnCustodianInfoChanged() {
-  for (ash::CustodianInfoTrayObserver& observer :
-       custodian_info_changed_observers_) {
-    observer.OnCustodianInfoChanged();
-  }
-}
-
 void SystemTrayDelegateChromeOS::OnAccessibilityStatusChanged(
     const AccessibilityStatusEventDetails& details) {
   if (details.notification_type == ACCESSIBILITY_MANAGER_SHUTDOWN)
@@ -680,43 +609,6 @@
     const std::string& engine_id,
     const std::vector<input_method::InputMethodManager::MenuItem>& items) {}
 
-const base::string16
-SystemTrayDelegateChromeOS::GetLegacySupervisedUserMessage() const {
-  std::string user_manager_name = GetSupervisedUserManager();
-  return l10n_util::GetStringFUTF16(
-      IDS_USER_IS_SUPERVISED_BY_NOTICE,
-      base::UTF8ToUTF16(user_manager_name));
-}
-
-const base::string16
-SystemTrayDelegateChromeOS::GetChildUserMessage() const {
-#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
-  // TODO(jamescook): If supervised users are always enabled on Chrome OS then
-  // these ifdefs can be removed.
-  SupervisedUserService* service =
-      SupervisedUserServiceFactory::GetForProfile(user_profile_);
-  base::string16 first_custodian =
-      base::UTF8ToUTF16(service->GetCustodianEmailAddress());
-  base::string16 second_custodian =
-      base::UTF8ToUTF16(service->GetSecondCustodianEmailAddress());
-  LOG_IF(WARNING, first_custodian.empty()) <<
-      "Returning incomplete child user message as manager not known yet.";
-  if (second_custodian.empty()) {
-    return l10n_util::GetStringFUTF16(
-        IDS_CHILD_USER_IS_MANAGED_BY_ONE_PARENT_NOTICE, first_custodian);
-  } else {
-    return l10n_util::GetStringFUTF16(
-        IDS_CHILD_USER_IS_MANAGED_BY_TWO_PARENTS_NOTICE,
-        first_custodian,
-        second_custodian);
-  }
-#endif
-
-  LOG(WARNING) << "SystemTrayDelegateChromeOS::GetChildUserMessage call while "
-               << "ENABLE_SUPERVISED_USERS undefined.";
-  return base::string16();
-}
-
 ash::SystemTrayDelegate* CreateSystemTrayDelegate() {
   return new SystemTrayDelegateChromeOS();
 }
diff --git a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
index e7bade25..5cd8c8bb 100644
--- a/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
+++ b/chrome/browser/ui/ash/system_tray_delegate_chromeos.h
@@ -12,14 +12,12 @@
 #include <vector>
 
 #include "ash/accessibility_types.h"
-#include "ash/system/supervised/custodian_info_tray_observer.h"
 #include "ash/system/tray/ime_info.h"
 #include "ash/system/tray/system_tray_delegate.h"
 #include "base/macros.h"
 #include "base/observer_list.h"
 #include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
 #include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/supervised_user/supervised_user_service_observer.h"
 #include "chrome/browser/ui/browser_list_observer.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/prefs/pref_change_registrar.h"
@@ -44,7 +42,6 @@
       public policy::CloudPolicyStore::Observer,
       public chrome::BrowserListObserver,
       public extensions::AppWindowRegistry::Observer,
-      public SupervisedUserServiceObserver,
       public input_method::InputMethodManager::ImeMenuObserver {
  public:
   SystemTrayDelegateChromeOS();
@@ -55,9 +52,6 @@
   ash::LoginStatus GetUserLoginStatus() const override;
   std::string GetEnterpriseDomain() const override;
   base::string16 GetEnterpriseMessage() const override;
-  std::string GetSupervisedUserManager() const override;
-  base::string16 GetSupervisedUserManagerName() const override;
-  base::string16 GetSupervisedUserMessage() const override;
   void ShowEnterpriseInfo() override;
   void ShowUserLogin() override;
   void GetCurrentIME(ash::IMEInfo* info) override;
@@ -71,10 +65,6 @@
   bool GetSessionLengthLimit(base::TimeDelta* session_length_limit) override;
   void ActiveUserWasChanged() override;
   bool IsSearchKeyMappedToCapsLock() override;
-  void AddCustodianInfoTrayObserver(
-      ash::CustodianInfoTrayObserver* observer) override;
-  void RemoveCustodianInfoTrayObserver(
-      ash::CustodianInfoTrayObserver* observer) override;
   std::unique_ptr<ash::SystemTrayItem> CreateRotationLockTrayItem(
       ash::SystemTray* tray) override;
 
@@ -95,8 +85,6 @@
 
   void StopObservingAppWindowRegistry();
 
-  void StopObservingCustodianInfoChanges();
-
   // Notify observers if the current user has no more open browser or app
   // windows.
   void NotifyIfLastWindowClosed();
@@ -134,9 +122,6 @@
   // Overridden from extensions::AppWindowRegistry::Observer:
   void OnAppWindowRemoved(extensions::AppWindow* app_window) override;
 
-  // Overridden from SupervisedUserServiceObserver:
-  void OnCustodianInfoChanged() override;
-
   void OnAccessibilityStatusChanged(
       const AccessibilityStatusEventDetails& details);
 
@@ -148,10 +133,6 @@
       const std::vector<input_method::InputMethodManager::MenuItem>& items)
       override;
 
-  // helper methods used by GetSupervisedUserMessage.
-  const base::string16 GetLegacySupervisedUserMessage() const;
-  const base::string16 GetChildUserMessage() const;
-
   std::unique_ptr<content::NotificationRegistrar> registrar_;
   std::unique_ptr<PrefChangeRegistrar> local_state_registrar_;
   std::unique_ptr<PrefChangeRegistrar> user_pref_registrar_;
@@ -168,9 +149,6 @@
   std::unique_ptr<ash::NetworkingConfigDelegate> networking_config_delegate_;
   std::unique_ptr<AccessibilityStatusSubscription> accessibility_subscription_;
 
-  base::ObserverList<ash::CustodianInfoTrayObserver>
-      custodian_info_changed_observers_;
-
   DISALLOW_COPY_AND_ASSIGN(SystemTrayDelegateChromeOS);
 };
 
diff --git a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
index 2e1474d..e9c4df7 100644
--- a/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
+++ b/chrome/browser/ui/views/ash/chrome_browser_main_extra_parts_ash.cc
@@ -75,6 +75,7 @@
   }
 
   session_controller_client_ = base::MakeUnique<SessionControllerClient>();
+  session_controller_client_->Init();
 
   // Must be available at login screen, so initialize before profile.
   system_tray_client_ = base::MakeUnique<SystemTrayClient>();