Commit d7601b00 authored by Matt Menard's avatar Matt Menard Committed by Commit Bot

Create AffiliatedSessionService to manage activity

Allows affiliated user activity to be managed based on login,
logout, locking, unlocking, and suspending sessions for affiliated and primary users.

Bug: 1058453
Change-Id: If21f269c98220835cc89b0d1c4db40cf213d6db1
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2153324
Commit-Queue: Matt Menard <mattme@google.com>
Reviewed-by: default avatarAnqing Zhao <anqing@google.com>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Cr-Commit-Position: refs/heads/master@{#762135}
parent e6ec8c69
...@@ -2007,6 +2007,8 @@ source_set("chromeos") { ...@@ -2007,6 +2007,8 @@ source_set("chromeos") {
"policy/single_app_install_event_log.h", "policy/single_app_install_event_log.h",
"policy/status_collector/activity_storage.cc", "policy/status_collector/activity_storage.cc",
"policy/status_collector/activity_storage.h", "policy/status_collector/activity_storage.h",
"policy/status_collector/affiliated_session_service.cc",
"policy/status_collector/affiliated_session_service.h",
"policy/status_collector/app_info_generator.cc", "policy/status_collector/app_info_generator.cc",
"policy/status_collector/app_info_generator.h", "policy/status_collector/app_info_generator.h",
"policy/status_collector/child_activity_storage.cc", "policy/status_collector/child_activity_storage.cc",
...@@ -3104,6 +3106,7 @@ source_set("unit_tests") { ...@@ -3104,6 +3106,7 @@ source_set("unit_tests") {
"policy/secondary_google_account_signin_policy_handler_unittest.cc", "policy/secondary_google_account_signin_policy_handler_unittest.cc",
"policy/server_backed_state_keys_broker_unittest.cc", "policy/server_backed_state_keys_broker_unittest.cc",
"policy/single_app_install_event_log_unittest.cc", "policy/single_app_install_event_log_unittest.cc",
"policy/status_collector/affiliated_session_service_unittest.cc",
"policy/status_collector/app_info_generator_unittest.cc", "policy/status_collector/app_info_generator_unittest.cc",
"policy/status_collector/interval_map_unittest.cc", "policy/status_collector/interval_map_unittest.cc",
"policy/status_uploader_unittest.cc", "policy/status_uploader_unittest.cc",
......
// Copyright 2020 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 "chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
namespace policy {
namespace {
AffiliatedSessionService* g_instance = nullptr;
constexpr base::TimeDelta kMinimumSuspendDuration =
base::TimeDelta::FromMinutes(1);
bool IsPrimaryAndAffiliated(Profile* profile) {
user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(profile);
bool is_primary = chromeos::ProfileHelper::Get()->IsPrimaryProfile(profile);
bool is_affiliated = user && user->IsAffiliated();
if (!is_primary || !is_affiliated) {
VLOG(1) << "The profile for the primary user is not associated with an "
"affiliated user.";
}
return is_primary && is_affiliated;
}
} // namespace
AffiliatedSessionService::AffiliatedSessionService(base::Clock* clock)
: clock_(clock), session_manager_(session_manager::SessionManager::Get()) {
DCHECK(!g_instance);
session_manager_->AddObserver(this);
chromeos::PowerManagerClient::Get()->AddObserver(this);
is_session_locked_ = session_manager_->IsScreenLocked();
g_instance = this;
}
AffiliatedSessionService::~AffiliatedSessionService() {
DCHECK_EQ(g_instance, this);
g_instance = nullptr;
}
// static
AffiliatedSessionService* AffiliatedSessionService::Get() {
DCHECK(g_instance);
return g_instance;
}
void AffiliatedSessionService::AddObserver(
AffiliatedSessionService::Observer* observer) {
observers_.AddObserver(observer);
}
void AffiliatedSessionService::RemoveObserver(
AffiliatedSessionService::Observer* observer) {
observers_.RemoveObserver(observer);
}
void AffiliatedSessionService::OnSessionStateChanged() {
bool is_session_locked = session_manager_->IsScreenLocked();
if (is_session_locked_ == is_session_locked) {
return;
}
is_session_locked_ = is_session_locked;
if (is_session_locked_) {
for (auto& observer : observers_) {
observer.OnLocked();
}
} else {
for (auto& observer : observers_) {
observer.OnUnlocked();
}
}
}
void AffiliatedSessionService::OnUserProfileLoaded(
const AccountId& account_id) {
Profile* profile =
chromeos::ProfileHelper::Get()->GetProfileByAccountId(account_id);
if (!IsPrimaryAndAffiliated(profile)) {
return;
}
profile->AddObserver(this);
for (auto& observer : observers_) {
observer.OnAffiliatedLogin(profile);
}
}
void AffiliatedSessionService::OnProfileWillBeDestroyed(Profile* profile) {
is_session_locked_ = false;
if (!IsPrimaryAndAffiliated(profile)) {
return;
}
for (auto& observer : observers_) {
observer.OnAffiliatedLogout(profile);
}
profile->RemoveObserver(this);
}
void AffiliatedSessionService::SuspendDone(
const base::TimeDelta& sleep_duration) {
if (sleep_duration < kMinimumSuspendDuration) {
return;
}
for (auto& observer : observers_) {
observer.OnResumeActive(clock_->Now() - sleep_duration);
}
}
} // namespace policy
// Copyright 2020 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 CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observer.h"
#include "base/time/clock.h"
#include "base/time/default_clock.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_observer.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "components/account_id/account_id.h"
#include "components/session_manager/core/session_manager.h"
#include "components/session_manager/core/session_manager_observer.h"
namespace policy {
class AffiliatedSessionService : public session_manager::SessionManagerObserver,
public ProfileObserver,
public chromeos::PowerManagerClient::Observer {
public:
class Observer : public base::CheckedObserver {
public:
// Occurs when an affiliated primary user has logged in.
virtual void OnAffiliatedLogin(Profile* profile) {}
// Occurs when an affiliated primary user has logged out.
virtual void OnAffiliatedLogout(Profile* profile) {}
// Occurs when the active user has locked the user session.
virtual void OnLocked() {}
// Occurs when the active user has unlocked the user session.
virtual void OnUnlocked() {}
// Occurs when the device recovers from a suspend state, where
// |suspend_time| is the time when the suspend state
// first occurred. Short duration suspends are not reported.
virtual void OnResumeActive(base::Time suspend_time) {}
};
explicit AffiliatedSessionService(
base::Clock* clock = base::DefaultClock::GetInstance());
AffiliatedSessionService(const AffiliatedSessionService&) = delete;
AffiliatedSessionService& operator=(const AffiliatedSessionService&) = delete;
~AffiliatedSessionService() override;
static AffiliatedSessionService* Get();
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// session_manager::SessionManagerObserver::Observer
void OnSessionStateChanged() override;
void OnUserProfileLoaded(const AccountId& account_id) override;
// ProfileObserver
void OnProfileWillBeDestroyed(Profile* profile) override;
// chromeos::PowerManagerClient::Observer
void SuspendDone(const base::TimeDelta& sleep_duration) override;
private:
bool is_session_locked_;
base::Clock* clock_;
base::ObserverList<Observer> observers_;
session_manager::SessionManager* const session_manager_;
ScopedObserver<Profile, ProfileObserver> profile_observer_{this};
ScopedObserver<session_manager::SessionManager,
session_manager::SessionManagerObserver>
session_manager_observer_{this};
};
} // namespace policy
#endif // CHROME_BROWSER_CHROMEOS_POLICY_STATUS_COLLECTOR_AFFILIATED_SESSION_SERVICE_H_
// Copyright 2020 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 "chrome/browser/chromeos/policy/status_collector/affiliated_session_service.h"
#include "base/test/simple_test_clock.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/mock_user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/session_manager/core/session_manager.h"
#include "components/user_manager/scoped_user_manager.h"
#include "content/public/test/browser_task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace policy {
class AffiliatedSessionServiceTest
: public ::testing::Test,
public policy::AffiliatedSessionService::Observer {
protected:
using SessionState = session_manager::SessionState;
void SetUp() override {
chromeos::PowerManagerClient::InitializeFake();
auto user_manager = std::make_unique<chromeos::FakeChromeUserManager>();
user_manager_ = user_manager.get();
user_manager_enabler_ = std::make_unique<user_manager::ScopedUserManager>(
std::move(user_manager));
affiliated_session_service_ =
std::make_unique<AffiliatedSessionService>(&test_clock_);
}
void TearDown() override { chromeos::PowerManagerClient::Shutdown(); }
std::unique_ptr<TestingProfile> CreateProfile(AccountId account_id,
bool is_affiliated,
bool login) {
TestingProfile::Builder profile_builder;
profile_builder.SetProfileName(account_id.GetUserEmail());
auto profile = profile_builder.Build();
user_manager_->AddUserWithAffiliationAndTypeAndProfile(
account_id, is_affiliated, user_manager::UserType::USER_TYPE_REGULAR,
profile.get());
if (login) {
user_manager_->LoginUser(account_id, true);
}
return profile;
}
AffiliatedSessionService* affiliated_session_service() {
return AffiliatedSessionService::Get();
}
session_manager::SessionManager* session_manager() {
return &session_manager_;
}
chromeos::FakePowerManagerClient* power_manager_client() {
return chromeos::FakePowerManagerClient::Get();
}
base::SimpleTestClock* test_clock() { return &test_clock_; }
void OnAffiliatedLogin(Profile* profile) override { logged_in_ = profile; }
void OnAffiliatedLogout(Profile* profile) override { logged_out_ = profile; }
void OnLocked() override { locked_ = true; }
void OnUnlocked() override { unlocked_ = true; }
void OnResumeActive(base::Time time) override {
suspend_time_ = std::make_unique<base::Time>(time);
}
Profile* logged_in_ = nullptr;
Profile* logged_out_ = nullptr;
bool locked_ = false;
bool unlocked_ = false;
std::unique_ptr<base::Time> suspend_time_;
private:
content::BrowserTaskEnvironment task_environment_;
chromeos::FakeChromeUserManager* user_manager_;
std::unique_ptr<user_manager::ScopedUserManager> user_manager_enabler_;
session_manager::SessionManager session_manager_;
base::SimpleTestClock test_clock_;
std::unique_ptr<AffiliatedSessionService> affiliated_session_service_;
};
TEST_F(AffiliatedSessionServiceTest, OnSessionStateChanged) {
affiliated_session_service()->AddObserver(this);
session_manager()->SetSessionState(SessionState::LOCKED);
session_manager()->SetSessionState(SessionState::ACTIVE);
EXPECT_TRUE(locked_);
EXPECT_TRUE(unlocked_);
session_manager()->SetSessionState(SessionState::LOCKED);
EXPECT_TRUE(locked_);
locked_ = false;
unlocked_ = false;
session_manager()->SetSessionState(SessionState::LOCKED);
session_manager()->SetSessionState(SessionState::LOGIN_PRIMARY);
EXPECT_FALSE(locked_);
EXPECT_TRUE(unlocked_);
session_manager()->SetSessionState(SessionState::LOCKED);
EXPECT_TRUE(locked_);
locked_ = false;
unlocked_ = false;
session_manager()->SetSessionState(SessionState::ACTIVE);
EXPECT_TRUE(unlocked_);
}
TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedAffiliatedAndPrimary) {
AccountId affiliated_account_id =
AccountId::FromUserEmail("user0@managed.com");
std::unique_ptr<TestingProfile> affiliated_profile = CreateProfile(
affiliated_account_id, true /* affiliated */, true /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(affiliated_account_id);
EXPECT_TRUE(affiliated_profile->IsSameProfile(logged_in_));
}
TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedAffiliated) {
AccountId secondary_account_id =
AccountId::FromUserEmail("user3@managed.com");
std::unique_ptr<TestingProfile> secondary_profile = CreateProfile(
secondary_account_id, true /* affiliated */, false /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(secondary_account_id);
EXPECT_EQ(logged_in_, nullptr);
}
TEST_F(AffiliatedSessionServiceTest, OnUserProfileLoadedPrimary) {
AccountId unaffiliated_account_id =
AccountId::FromUserEmail("user2@managed.com");
std::unique_ptr<TestingProfile> unaffiliated_profile = CreateProfile(
unaffiliated_account_id, false /* affiliated */, true /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(unaffiliated_account_id);
EXPECT_EQ(logged_in_, nullptr);
}
TEST_F(AffiliatedSessionServiceTest,
OnProfileWillBeDestroyedAffiliatedAndPrimary) {
AccountId affiliated_account_id =
AccountId::FromUserEmail("user0@managed.com");
std::unique_ptr<TestingProfile> affiliated_profile = CreateProfile(
affiliated_account_id, true /* affiliated */, true /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(affiliated_account_id);
affiliated_profile->MaybeSendDestroyedNotification();
EXPECT_TRUE(affiliated_profile->IsSameProfile(logged_out_));
}
TEST_F(AffiliatedSessionServiceTest, OnProfileWillBeDestroyedAffiliated) {
AccountId secondary_account_id =
AccountId::FromUserEmail("user3@managed.com");
std::unique_ptr<TestingProfile> secondary_profile = CreateProfile(
secondary_account_id, true /* affiliated */, false /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(secondary_account_id);
secondary_profile->MaybeSendDestroyedNotification();
EXPECT_EQ(logged_out_, nullptr);
}
TEST_F(AffiliatedSessionServiceTest, OnProfileWillBeDestroyedPrimary) {
AccountId unaffiliated_account_id =
AccountId::FromUserEmail("user2@managed.com");
std::unique_ptr<TestingProfile> unaffiliated_profile = CreateProfile(
unaffiliated_account_id, false /* affiliated */, true /* login */);
affiliated_session_service()->AddObserver(this);
session_manager()->NotifyUserProfileLoaded(unaffiliated_account_id);
unaffiliated_profile->MaybeSendDestroyedNotification();
EXPECT_EQ(logged_out_, nullptr);
}
TEST_F(AffiliatedSessionServiceTest, SuspendDone) {
affiliated_session_service()->AddObserver(this);
test_clock()->SetNow(base::Time::Now());
base::TimeDelta sleep_duration = base::TimeDelta::FromHours(2);
power_manager_client()->SendSuspendDone(sleep_duration);
EXPECT_EQ(*suspend_time_, test_clock()->Now() - sleep_duration);
}
TEST_F(AffiliatedSessionServiceTest, RemoveObserver) {
AccountId account_id = AccountId::FromUserEmail("user0@managed.com");
std::unique_ptr<TestingProfile> profile =
CreateProfile(account_id, true /* affiliated */, true /* login */);
affiliated_session_service()->AddObserver(this);
affiliated_session_service()->RemoveObserver(this);
session_manager()->SetSessionState(SessionState::LOCKED);
session_manager()->SetSessionState(SessionState::ACTIVE);
session_manager()->SetSessionState(SessionState::LOCKED);
EXPECT_FALSE(locked_);
EXPECT_FALSE(unlocked_);
session_manager()->NotifyUserProfileLoaded(account_id);
EXPECT_FALSE(profile->IsSameProfile(logged_in_));
profile->MaybeSendDestroyedNotification();
EXPECT_FALSE(profile->IsSameProfile(logged_out_));
}
} // namespace policy
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