[CrOS multi-profiles] Restore user sessions after crash

BUG=180903,238998,230464
TEST=Induce browser crash in multi-profile sessions, observe that all user sessions are restored
TEST=CrashRestoreSimpleTest, CrashRestoreComplexTest
NOTRY=true

Review URL: https://chromiumcodereview.appspot.com/15929005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@202647 0039d316-1c4b-4281-b951-d872f2087c98
parent 30460cb2
......@@ -137,6 +137,7 @@ class KioskAppLauncher::ProfileLoader : public LoginUtils::Delegate {
std::string(), // display email
false, // using_oauth
false, // has_cookies
false, // has_active_session
this);
}
......
......@@ -184,6 +184,7 @@ class StubLogin : public LoginStatusConsumer,
std::string(), // display_email
using_oauth,
false, // has_cookies
true, // has_active_session
this);
} else if (!pending_requests) {
delete this;
......
// Copyright 2013 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.
#include <string>
#include <vector>
#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "chrome/browser/chromeos/cros/cros_in_process_browser_test.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/common/chrome_switches.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "chromeos/dbus/mock_dbus_thread_manager_without_gmock.h"
#include "chromeos/dbus/session_manager_client.h"
#include "content/public/test/test_utils.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
const char kUserId1[] = "user1@example.com";
const char kUserId2[] = "user2@example.com";
const char kUserId3[] = "user3@example.com";
} // namespace
class CrashRestoreSimpleTest : public CrosInProcessBrowserTest {
protected:
CrashRestoreSimpleTest() {}
virtual ~CrashRestoreSimpleTest() {}
virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
command_line->AppendSwitch(::switches::kMultiProfiles);
command_line->AppendSwitchASCII(switches::kLoginUser, kUserId1);
command_line->AppendSwitchASCII(
switches::kLoginProfile,
CryptohomeClient::GetStubSanitizedUsername(kUserId1));
}
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
// Redirect session_manager DBus calls to FakeSessionManagerClient.
MockDBusThreadManagerWithoutGMock* dbus_thread_manager =
new MockDBusThreadManagerWithoutGMock();
session_manager_client_ =
dbus_thread_manager->fake_session_manager_client();
DBusThreadManager::InitializeForTesting(dbus_thread_manager);
session_manager_client_->StartSession(kUserId1);
}
FakeSessionManagerClient* session_manager_client_;
};
IN_PROC_BROWSER_TEST_F(CrashRestoreSimpleTest, RestoreSessionForOneUser) {
UserManager* user_manager = UserManager::Get();
User* user = user_manager->GetActiveUser();
ASSERT_TRUE(user);
EXPECT_EQ(kUserId1, user->email());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(kUserId1),
user->username_hash());
EXPECT_EQ(1UL, user_manager->GetLoggedInUsers().size());
}
// Observer that keeps track of user sessions restore event.
class UserSessionRestoreObserver :
public UserManager::UserSessionStateObserver {
public:
UserSessionRestoreObserver()
: running_loop_(false),
user_sessions_restored_(UserManager::Get()->UserSessionsRestored()) {
if (!user_sessions_restored_)
UserManager::Get()->AddSessionStateObserver(this);
}
virtual ~UserSessionRestoreObserver() {}
virtual void ActiveUserHashChanged(const std::string& hash) OVERRIDE {
}
virtual void PendingUserSessionsRestoreFinished() OVERRIDE {
user_sessions_restored_ = true;
UserManager::Get()->RemoveSessionStateObserver(this);
if (!running_loop_)
return;
message_loop_runner_->Quit();
running_loop_ = false;
}
// Wait until the user sessions are restored. If that happened between the
// construction of this object and this call or even before it was created
// then it returns immediately.
void Wait() {
if (user_sessions_restored_)
return;
running_loop_ = true;
message_loop_runner_ = new content::MessageLoopRunner();
message_loop_runner_->Run();
}
private:
bool running_loop_;
bool user_sessions_restored_;
scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
DISALLOW_COPY_AND_ASSIGN(UserSessionRestoreObserver);
};
class CrashRestoreComplexTest : public CrashRestoreSimpleTest {
protected:
CrashRestoreComplexTest() {}
virtual ~CrashRestoreComplexTest() {}
virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
CrashRestoreSimpleTest::SetUpInProcessBrowserTestFixture();
session_manager_client_->StartSession(kUserId2);
session_manager_client_->StartSession(kUserId3);
}
};
IN_PROC_BROWSER_TEST_F(CrashRestoreComplexTest, RestoreSessionForThreeUsers) {
{
UserSessionRestoreObserver restore_observer;
restore_observer.Wait();
}
UserManager* user_manager = UserManager::Get();
DCHECK(user_manager->UserSessionsRestored());
// User that is last in the user sessions map becomes active. This behavior
// will become better defined once each user gets a separate user desktop.
User* user = user_manager->GetActiveUser();
ASSERT_TRUE(user);
EXPECT_EQ(kUserId3, user->email());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(kUserId3),
user->username_hash());
const UserList& users = user_manager->GetLoggedInUsers();
ASSERT_EQ(3UL, users.size());
// User that becomes active moves to the beginning of the list.
EXPECT_EQ(kUserId3, users[0]->email());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(kUserId3),
users[0]->username_hash());
EXPECT_EQ(kUserId2, users[1]->email());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(kUserId2),
users[1]->username_hash());
EXPECT_EQ(kUserId1, users[2]->email());
EXPECT_EQ(CryptohomeClient::GetStubSanitizedUsername(kUserId1),
users[2]->username_hash());
}
} // namespace chromeos
......@@ -762,6 +762,7 @@ void ExistingUserController::OnLoginSuccess(
display_email_,
using_oauth,
has_cookies,
false, // Start session for user.
this);
display_email_.clear();
......
......@@ -298,7 +298,7 @@ IN_PROC_BROWSER_TEST_P(ExistingUserControllerTest, ExistingUserLogin) {
.WillOnce(WithArg<0>(CreateAuthenticator(kUsername, kPassword)));
EXPECT_CALL(*mock_login_utils_,
PrepareProfile(UserContext(kUsername, kPassword, "", kUsername),
_, _, _, _))
_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(&profile_prepared_cb_,
&base::Callback<void(void)>::Run));
......@@ -367,7 +367,7 @@ IN_PROC_BROWSER_TEST_P(ExistingUserControllerTest,
kPassword,
std::string(),
kNewUsername),
_, _, _, _))
_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(&profile_prepared_cb_,
&base::Callback<void(void)>::Run));
......@@ -491,7 +491,7 @@ class ExistingUserControllerPublicSessionTest
.WillOnce(WithArg<0>(CreateAuthenticator(username, password)));
EXPECT_CALL(*mock_login_utils_,
PrepareProfile(UserContext(username, password, "", username),
_, _, _, _))
_, _, _, _, _))
.Times(1)
.WillOnce(InvokeWithoutArgs(&profile_prepared_cb_,
&base::Callback<void(void)>::Run));
......
......@@ -146,6 +146,7 @@ class LoginUtilsImpl
const std::string& display_email,
bool using_oauth,
bool has_cookies,
bool has_active_session,
LoginUtils::Delegate* delegate) OVERRIDE;
virtual void DelegateDeleted(LoginUtils::Delegate* delegate) OVERRIDE;
virtual void CompleteOffTheRecordLogin(const GURL& start_url) OVERRIDE;
......@@ -320,15 +321,18 @@ void LoginUtilsImpl::PrepareProfile(
const std::string& display_email,
bool using_oauth,
bool has_cookies,
bool has_active_session,
LoginUtils::Delegate* delegate) {
BootTimesLoader* btl = BootTimesLoader::Get();
VLOG(1) << "Completing login for " << user_context.username;
btl->AddLoginTimeMarker("StartSession-Start", false);
DBusThreadManager::Get()->GetSessionManagerClient()->StartSession(
user_context.username);
btl->AddLoginTimeMarker("StartSession-End", false);
if (!has_active_session) {
btl->AddLoginTimeMarker("StartSession-Start", false);
DBusThreadManager::Get()->GetSessionManagerClient()->StartSession(
user_context.username);
btl->AddLoginTimeMarker("StartSession-End", false);
}
btl->AddLoginTimeMarker("UserLoggedIn-Start", false);
UserManager* user_manager = UserManager::Get();
......
......@@ -65,11 +65,14 @@ class LoginUtils {
// this value, shown in UI.
// |user_context.username_hash| defines when user homedir is mounted.
// Also see DelegateDeleted method.
// If |has_active_session| is true than this is a case of restoring user
// session after browser crash so no need to start new session.
virtual void PrepareProfile(
const UserContext& user_context,
const std::string& display_email,
bool using_oauth,
bool has_cookies,
bool has_active_session,
Delegate* delegate) = 0;
// Invalidates |delegate|, which was passed to PrepareProfile method call.
......
......@@ -435,9 +435,10 @@ class LoginUtilsTest : public testing::Test,
// Setting |kHasCookies| to false prevents ProfileAuthData::Transfer from
// waiting for an IO task before proceeding.
const bool kHasCookies = false;
const bool kHasActiveSession = false;
LoginUtils::Get()->PrepareProfile(
UserContext(username, "password", std::string(), username),
std::string(), kUsingOAuth, kHasCookies, this);
std::string(), kUsingOAuth, kHasCookies, kHasActiveSession, this);
device_settings_test_helper.Flush();
RunUntilIdle();
......
......@@ -30,9 +30,9 @@ class MockLoginUtils : public LoginUtils {
virtual ~MockLoginUtils();
MOCK_METHOD2(DoBrowserLaunch, void(Profile*, LoginDisplayHost*));
MOCK_METHOD5(PrepareProfile,
MOCK_METHOD6(PrepareProfile,
void(const UserContext&, const std::string&,
bool, bool, LoginUtils::Delegate*));
bool, bool, bool, LoginUtils::Delegate*));
MOCK_METHOD1(DelegateDeleted, void(LoginUtils::Delegate*));
MOCK_METHOD1(CompleteOffTheRecordLogin, void(const GURL&));
MOCK_METHOD1(SetFirstLoginPrefs, void(PrefService*));
......
......@@ -58,6 +58,7 @@ class MockUserManager : public UserManager {
MOCK_CONST_METHOD0(IsLoggedInAsKioskApp, bool(void));
MOCK_CONST_METHOD0(IsLoggedInAsStub, bool(void));
MOCK_CONST_METHOD0(IsSessionStarted, bool(void));
MOCK_CONST_METHOD0(UserSessionsRestored, bool(void));
MOCK_CONST_METHOD0(HasBrowserRestarted, bool(void));
MOCK_CONST_METHOD1(IsUserNonCryptohomeDataEphemeral,
bool(const std::string&));
......
......@@ -22,6 +22,7 @@ void TestLoginUtils::PrepareProfile(
const std::string& display_email,
bool using_oauth,
bool has_cookies,
bool has_active_session,
Delegate* delegate) {
DCHECK_EQ(expected_username_, credentials.username);
DCHECK_EQ(expected_password_, credentials.password);
......
......@@ -31,6 +31,7 @@ class TestLoginUtils : public LoginUtils {
const std::string& display_email,
bool using_oauth,
bool has_cookies,
bool has_active_session,
Delegate* delegate) OVERRIDE;
virtual void DelegateDeleted(Delegate* delegate) OVERRIDE;
......
......@@ -53,6 +53,9 @@ class UserManager {
// on user_id hash would be accessing up-to-date value.
virtual void ActiveUserHashChanged(const std::string& hash) = 0;
// Called when UserManager finishes restoring user sessions after crash.
virtual void PendingUserSessionsRestoreFinished() = 0;
protected:
virtual ~UserSessionStateObserver();
};
......@@ -258,6 +261,10 @@ class UserManager {
// or restart after crash.
virtual bool IsSessionStarted() const = 0;
// Returns true iff browser has been restarted after crash and UserManager
// finished restoring user sessions.
virtual bool UserSessionsRestored() const = 0;
// Returns merge session status.
virtual MergeSessionState GetMergeSessionState() const = 0;
......
......@@ -28,6 +28,7 @@
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/default_pinned_apps_field_trial.h"
#include "chrome/browser/chromeos/login/login_display.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/remove_user_delegate.h"
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
......@@ -188,6 +189,7 @@ UserManagerImpl::UserManagerImpl()
users_loaded_(false),
active_user_(NULL),
session_started_(false),
user_sessions_restored_(false),
is_current_user_owner_(false),
is_current_user_new_(false),
is_current_user_ephemeral_regular_user_(false),
......@@ -629,9 +631,13 @@ void UserManagerImpl::Observe(int type,
!IsLoggedInAsKioskApp()) {
Profile* profile = content::Source<Profile>(source).ptr();
if (!profile->IsOffTheRecord() &&
// TODO(nkostylev): We should observe all logged in user's profiles.
profile == ProfileManager::GetDefaultProfile()) {
DCHECK(NULL == observed_sync_service_);
// TODO(nkostylev): We should observe all logged in user's profiles.
// http://crbug.com/230860
if (!CommandLine::ForCurrentProcess()->
HasSwitch(::switches::kMultiProfiles)) {
DCHECK(NULL == observed_sync_service_);
}
observed_sync_service_ =
ProfileSyncServiceFactory::GetForProfile(profile);
if (observed_sync_service_)
......@@ -762,6 +768,11 @@ bool UserManagerImpl::IsSessionStarted() const {
return session_started_;
}
bool UserManagerImpl::UserSessionsRestored() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return user_sessions_restored_;
}
UserManager::MergeSessionState UserManagerImpl::GetMergeSessionState() const {
return merge_session_state_;
}
......@@ -846,6 +857,23 @@ void UserManagerImpl::NotifyLocalStateChanged() {
LocalStateChanged(this));
}
void UserManagerImpl::OnProfilePrepared(Profile* profile) {
LoginUtils::Get()->DoBrowserLaunch(profile,
NULL); // host_, not needed here
if (!CommandLine::ForCurrentProcess()->HasSwitch(::switches::kTestName)) {
// Did not log in (we crashed or are debugging), need to restore Sync.
// TODO(nkostylev): Make sure that OAuth state is restored correctly for all
// users once it is fully multi-profile aware. http://crbug.com/238987
// For now if we have other user pending sessions they'll override OAuth
// session restore for previous users.
LoginUtils::Get()->RestoreAuthenticationSession(profile);
}
// Restore other user sessions if any.
RestorePendingUserSessions();
}
void UserManagerImpl::EnsureUsersLoaded() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!g_browser_process || !g_browser_process->local_state())
......@@ -1505,6 +1533,14 @@ void UserManagerImpl::NotifyActiveUserHashChanged(const std::string& hash) {
ActiveUserHashChanged(hash));
}
void UserManagerImpl::NotifyPendingUserSessionsRestoreFinished() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
user_sessions_restored_ = true;
FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver,
session_state_observer_list_,
PendingUserSessionsRestoreFinished());
}
void UserManagerImpl::UpdateLoginState() {
if (!LoginState::IsInitialized())
return; // LoginState may not be intialized in tests.
......@@ -1545,13 +1581,70 @@ void UserManagerImpl::SetLRUUser(User* user) {
void UserManagerImpl::OnRestoreActiveSessions(
const SessionManagerClient::ActiveSessionsMap& sessions,
bool success) {
// TODO(nkostylev): Restore all user sessions (in the background).
// This requires first refactoring this flow out of LoginUtils.
// 1. UserManager::UserLoggedIn()
// 2. InitSessionRestoreStrategy() (OAuth)
// 2. ProfileManager::CreateDefaultProfileAsync()
// 3. InitProfilePreferences
// 4. chrome::NOTIFICATION_LOGIN_USER_PROFILE_PREPARED
if (!success) {
LOG(ERROR) << "Could not get list of active user sessions after crash.";
// If we could not get list of active user sessions it is safer to just
// sign out so that we don't get in the inconsistent state.
DBusThreadManager::Get()->GetSessionManagerClient()->StopSession();
return;
}
// One profile has been already loaded on browser start.
DCHECK(GetLoggedInUsers().size() == 1);
DCHECK(GetActiveUser());
std::string active_user_id = GetActiveUser()->email();
SessionManagerClient::ActiveSessionsMap::const_iterator it;
for (it = sessions.begin(); it != sessions.end(); ++it) {
if (active_user_id == it->first)
continue;
pending_user_sessions_[it->first] = it->second;
}
RestorePendingUserSessions();
}
void UserManagerImpl::RestorePendingUserSessions() {
if (pending_user_sessions_.empty()) {
NotifyPendingUserSessionsRestoreFinished();
return;
}
// Get next user to restore sessions and delete it from list.
SessionManagerClient::ActiveSessionsMap::const_iterator it =
pending_user_sessions_.begin();
std::string user_id = it->first;
std::string user_id_hash = it->second;
DCHECK(!user_id.empty());
DCHECK(!user_id_hash.empty());
pending_user_sessions_.erase(user_id);
// Check that this user is not logged in yet.
UserList logged_in_users = GetLoggedInUsers();
bool user_already_logged_in = false;
for (UserList::const_iterator it = logged_in_users.begin();
it != logged_in_users.end(); ++it) {
const User* user = (*it);
if (user->email() == user_id) {
user_already_logged_in = true;
break;
}
}
DCHECK(!user_already_logged_in);
if (!user_already_logged_in) {
// Will call OnProfilePrepared() once profile has been loaded.
LoginUtils::Get()->PrepareProfile(UserContext(user_id,
std::string(), // password
std::string(), // auth_code
user_id_hash),
std::string(), // display_email
false, // using_oauth
false, // has_cookies
true, // has_active_session
this);
} else {
RestorePendingUserSessions();
}
}
} // namespace chromeos
......@@ -13,6 +13,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_image_manager_impl.h"
#include "chrome/browser/chromeos/login/user_manager.h"
......@@ -40,6 +41,7 @@ class SessionLengthLimiter;
// Implementation of the UserManager.
class UserManagerImpl
: public UserManager,
public LoginUtils::Delegate,
public ProfileSyncServiceObserver,
public content::NotificationObserver,
public policy::DeviceLocalAccountPolicyService::Observer {
......@@ -93,6 +95,7 @@ class UserManagerImpl
virtual bool IsLoggedInAsKioskApp() const OVERRIDE;
virtual bool IsLoggedInAsStub() const OVERRIDE;
virtual bool IsSessionStarted() const OVERRIDE;
virtual bool UserSessionsRestored() const OVERRIDE;
virtual MergeSessionState GetMergeSessionState() const OVERRIDE;
virtual void SetMergeSessionState(MergeSessionState status) OVERRIDE;
virtual bool HasBrowserRestarted() const OVERRIDE;
......@@ -147,6 +150,10 @@ class UserManagerImpl
UserManagerImpl();
// LoginUtils::Delegate implementation:
// Used when restoring user sessions after crash.
virtual void OnProfilePrepared(Profile* profile) OVERRIDE;
// Loads |users_| from Local State if the list has not been loaded yet.
// Subsequent calls have no effect. Must be called on the UI thread.
void EnsureUsersLoaded();
......@@ -254,6 +261,9 @@ class UserManagerImpl
// Notifies observers that active user_id hash has changed.
void NotifyActiveUserHashChanged(const std::string& hash);
// Notifies observers that user pending sessions restore has finished.
void NotifyPendingUserSessionsRestoreFinished();
// Returns true if there is non-committed user creation transaction.
bool HasFailedLocallyManagedUserCreationTransaction();
......@@ -274,6 +284,12 @@ class UserManagerImpl
const SessionManagerClient::ActiveSessionsMap& sessions,
bool success);
// Called by OnRestoreActiveSessions() when there're user sessions in
// |pending_user_sessions_| that has to be restored one by one.
// Also called after first user session from that list is restored and so on.
// Process continues till |pending_user_sessions_| map is not empty.
void RestorePendingUserSessions();
// Interface to the signed settings store.
CrosSettings* cros_settings_;
......@@ -309,6 +325,10 @@ class UserManagerImpl
// True if SessionStarted() has been called.
bool session_started_;
// True is user sessions has been restored after crash.
// On a normal boot then login into user sessions this will be false.
bool user_sessions_restored_;
// Cached flag of whether currently logged-in user is owner or not.
// May be accessed on different threads, requires locking.
bool is_current_user_owner_;
......@@ -368,6 +388,10 @@ class UserManagerImpl
// Specific flows by user e-mail.
FlowMap specific_flows_;
// User sessions that have to be restored after browser crash.
// [user_id] > [user_id_hash]
SessionManagerClient::ActiveSessionsMap pending_user_sessions_;
DISALLOW_COPY_AND_ASSIGN(UserManagerImpl);
};
......
......@@ -144,4 +144,7 @@ void ProfileHelper::ActiveUserHashChanged(const std::string& hash) {
LOG(INFO) << "Switching to profile path: " << profile_path.value();
}
void ProfileHelper::PendingUserSessionsRestoreFinished() {
}
} // namespace chromeos
......@@ -77,6 +77,7 @@ class ProfileHelper : public BrowsingDataRemover::Observer,
// UserManager::UserSessionStateObserver implementation:
virtual void ActiveUserHashChanged(const std::string& hash) OVERRIDE;
virtual void PendingUserSessionsRestoreFinished() OVERRIDE;
// BrowsingDataRemover::Observer implementation:
virtual void OnBrowsingDataRemoverDone() OVERRIDE;
......
......@@ -1235,6 +1235,7 @@
'browser/chromeos/input_method/input_method_engine_ibus_browserttests.cc',
'browser/chromeos/kiosk_mode/mock_kiosk_mode_settings.cc',
'browser/chromeos/kiosk_mode/mock_kiosk_mode_settings.h',
'browser/chromeos/login/crash_restore_browsertest.cc',
'browser/chromeos/login/enrollment/enrollment_screen_browsertest.cc',
'browser/chromeos/login/enrollment/mock_enrollment_screen.cc',
'browser/chromeos/login/enrollment/mock_enrollment_screen.h',
......
......@@ -8,6 +8,7 @@
#include "base/location.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "chromeos/dbus/cryptohome_client.h"
namespace chromeos {
......@@ -47,6 +48,10 @@ void FakeSessionManagerClient::RestartEntd() {
}
void FakeSessionManagerClient::StartSession(const std::string& user_email) {
DCHECK_EQ(0UL, user_sessions_.count(user_email));
std::string user_id_hash =
CryptohomeClient::GetStubSanitizedUsername(user_email);
user_sessions_[user_email] = user_id_hash;
}
void FakeSessionManagerClient::StopSession() {
......@@ -71,10 +76,9 @@ void FakeSessionManagerClient::NotifyLockScreenDismissed() {
void FakeSessionManagerClient::RetrieveActiveSessions(
const ActiveSessionsCallback& callback) {
ActiveSessionsMap sessions;
MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(callback,
sessions,
user_sessions_,
true));
}
......
......@@ -93,6 +93,7 @@ class FakeSessionManagerClient : public chromeos::SessionManagerClient {
std::map<std::string, std::string> user_policies_;
std::map<std::string, std::string> device_local_account_policy_;
ObserverList<Observer> observers_;
SessionManagerClient::ActiveSessionsMap user_sessions_;
int emit_login_prompt_ready_call_count_;
int notify_lock_screen_shown_call_count_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment