| // Copyright 2022 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <memory> | |
| #include <utility> | |
| #include <base/logging.h> | |
| #include <dbus/bus.h> | |
| #include <dbus/error.h> | |
| #include <dbus/login_manager/dbus-constants.h> | |
| #include <dbus/message.h> | |
| #include <chromeos/dbus/service_constants.h> | |
| #include <google/protobuf/message_lite.h> | |
| #include <power_manager/dbus-proxies.h> | |
| #include <power_manager/proto_bindings/suspend.pb.h> | |
| #include "biod/session_state_manager.h" | |
| #include "biod/utils.h" | |
| namespace biod { | |
| using dbus::ObjectPath; | |
| using RetrievePrimarySessionResult = BiodMetrics::RetrievePrimarySessionResult; | |
| SessionStateManager::SessionStateManager(dbus::Bus* bus, | |
| BiodMetricsInterface* biod_metrics) | |
| : power_manager_proxy_( | |
| std::make_unique<org::chromium::PowerManagerProxy>(bus)), | |
| biod_metrics_(biod_metrics) { | |
| session_manager_proxy_ = bus->GetObjectProxy( | |
| login_manager::kSessionManagerServiceName, | |
| dbus::ObjectPath(login_manager::kSessionManagerServicePath)); | |
| session_manager_proxy_->ConnectToSignal( | |
| login_manager::kSessionManagerInterface, | |
| login_manager::kSessionStateChangedSignal, | |
| base::BindRepeating(&SessionStateManager::OnSessionStateChanged, | |
| base::Unretained(this)), | |
| base::BindOnce(&LogOnSignalConnected)); | |
| // Track org.chromium.SessionManager name owner changes. | |
| session_manager_proxy_->SetNameOwnerChangedCallback(base::BindRepeating( | |
| &SessionStateManager::OnSessionManagerNameOwnerChanged, | |
| base::Unretained(this))); | |
| } | |
| std::string SessionStateManager::GetPrimaryUser() const { | |
| return primary_user_; | |
| } | |
| bool SessionStateManager::RefreshPrimaryUser() { | |
| std::string old_primary_user = primary_user_; | |
| primary_user_.clear(); | |
| bool update_result = UpdatePrimaryUser(); | |
| if (old_primary_user.empty() && !primary_user_.empty()) { | |
| for (auto& observer : observers_) { | |
| observer.OnUserLoggedIn(primary_user_, false); | |
| } | |
| } else if (!old_primary_user.empty() && primary_user_.empty()) { | |
| for (auto& observer : observers_) { | |
| observer.OnUserLoggedOut(); | |
| } | |
| } | |
| return update_result; | |
| } | |
| std::optional<std::string> SessionStateManager::RetrievePrimaryUser() { | |
| std::string sanitized_username; | |
| dbus::MethodCall method_call( | |
| login_manager::kSessionManagerInterface, | |
| login_manager::kSessionManagerRetrievePrimarySession); | |
| base::Time start_time = base::Time::Now(); | |
| base::expected<std::unique_ptr<dbus::Response>, dbus::Error> response_result = | |
| session_manager_proxy_->CallMethodAndBlock( | |
| &method_call, dbus_constants::kDbusTimeoutMs); | |
| // Record RetrievePrimarySession duration. | |
| base::TimeDelta call_duration = base::Time::Now() - start_time; | |
| biod_metrics_->SendSessionRetrievePrimarySessionDuration( | |
| call_duration.InMilliseconds()); | |
| if (!response_result.has_value()) { | |
| dbus::Error error = std::move(response_result.error()); | |
| if (!error.IsValid()) { | |
| LOG(ERROR) << "Get invalid error when calling " | |
| << login_manager::kSessionManagerRetrievePrimarySession | |
| << " from " << login_manager::kSessionManagerInterface | |
| << " interface."; | |
| return std::nullopt; | |
| } | |
| std::string error_name = error.name(); | |
| LOG(ERROR) << "Calling " | |
| << login_manager::kSessionManagerRetrievePrimarySession | |
| << " from " << login_manager::kSessionManagerInterface | |
| << " interface finished with " << error_name << " error."; | |
| if (error_name == dbus_constants::kDBusErrorNoReply) { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorDBusNoReply); | |
| LOG(ERROR) << "Timeout while getting primary session"; | |
| } else if (error_name == dbus_constants::kDBusErrorServiceUnknown) { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorDBusServiceUnknown); | |
| LOG(ERROR) << "Can't find " << login_manager::kSessionManagerServiceName | |
| << " service. Maybe session_manager is not running?"; | |
| } else { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorUnknown); | |
| LOG(ERROR) << "Error details: " << error.message(); | |
| } | |
| return std::nullopt; | |
| } | |
| std::unique_ptr<dbus::Response> response = std::move(response_result.value()); | |
| if (!response.get()) { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorResponseMissing); | |
| LOG(ERROR) << "Cannot retrieve username for primary session."; | |
| return std::nullopt; | |
| } | |
| dbus::MessageReader response_reader(response.get()); | |
| std::string username; | |
| if (!response_reader.PopString(&username)) { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorParsing); | |
| LOG(ERROR) << "Primary session username bad format."; | |
| return std::nullopt; | |
| } | |
| if (!response_reader.PopString(&sanitized_username)) { | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kErrorParsing); | |
| LOG(ERROR) << "Primary session sanitized username bad format."; | |
| return std::nullopt; | |
| } | |
| biod_metrics_->SendSessionRetrievePrimarySessionResult( | |
| RetrievePrimarySessionResult::kSuccess); | |
| return sanitized_username; | |
| } | |
| bool SessionStateManager::UpdatePrimaryUser() { | |
| auto primary_user = RetrievePrimaryUser(); | |
| if (!primary_user) { | |
| LOG(ERROR) << "Error while retrieving primary user"; | |
| return false; | |
| } | |
| if (primary_user->empty()) { | |
| LOG(INFO) << "Primary user does not exist."; | |
| return false; | |
| } | |
| LOG(INFO) << "Primary user updated to " << LogSafeID(*primary_user) << "."; | |
| primary_user_.assign(std::move(*primary_user)); | |
| return true; | |
| } | |
| void SessionStateManager::OnSessionStateChanged(dbus::Signal* signal) { | |
| dbus::MessageReader signal_reader(signal); | |
| std::string state; | |
| CHECK(signal_reader.PopString(&state)); | |
| LOG(INFO) << "Session state changed to " << state << "."; | |
| if (state == dbus_constants::kSessionStateStarted) { | |
| if (!primary_user_.empty()) { | |
| LOG(INFO) << "Primary user already exists. Not updating primary user."; | |
| return; | |
| } | |
| if (UpdatePrimaryUser()) { | |
| for (auto& observer : observers_) { | |
| observer.OnUserLoggedIn(primary_user_, true); | |
| } | |
| } | |
| } else if (state == dbus_constants::kSessionStateStopped) { | |
| primary_user_.clear(); | |
| for (auto& observer : observers_) { | |
| observer.OnUserLoggedOut(); | |
| } | |
| } | |
| } | |
| void SessionStateManager::OnSessionManagerNameOwnerChanged( | |
| const std::string& old_owner, const std::string& new_owner) { | |
| LOG(INFO) << login_manager::kSessionManagerServiceName << " name owner was " | |
| << "changed from " << (old_owner.empty() ? "(empty)" : old_owner) | |
| << " to " << (new_owner.empty() ? "(empty)" : new_owner); | |
| // Do nothing when org.chromium.SessionManager service name is acquired. | |
| // When session_manager has started user is always logged out. | |
| // When user logs in, OnSessionStateChanged() callback will be called | |
| // accordingly. | |
| if (!new_owner.empty()) | |
| return; | |
| // When primary user is empty, it means that user was not logged in or | |
| // session_manager notified us that session state was changed to stopped | |
| // before dying. In either case there is nothing to do. | |
| if (primary_user_.empty()) | |
| return; | |
| LOG(WARNING) | |
| << "Name " << login_manager::kSessionManagerServiceName | |
| << " was released while user was logged in (primary user is set)." | |
| << " Clear primary user and perform user logout action."; | |
| primary_user_.clear(); | |
| for (auto& observer : observers_) { | |
| observer.OnUserLoggedOut(); | |
| } | |
| } | |
| void SessionStateManager::AddObserver(Observer* observer) { | |
| observers_.AddObserver(observer); | |
| } | |
| void SessionStateManager::RemoveObserver(Observer* observer) { | |
| observers_.RemoveObserver(observer); | |
| } | |
| } // namespace biod |