// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include <stddef.h>
#include <stdint.h>
#include <cstring>
#include <memory>
#include <optional>
#include <string_view>
#include <utility>
#include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/platform_shared_memory_region.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/writable_shared_memory_region.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/components/dbus/arc/arc.pb.h"
#include "chromeos/ash/components/dbus/cryptohome/rpc.pb.h"
#include "chromeos/ash/components/dbus/login_manager/login_screen_storage.pb.h"
#include "chromeos/ash/components/dbus/login_manager/policy_descriptor.pb.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/dbus/session_manager/policy_descriptor.h"
#include "chromeos/dbus/common/blocking_method_caller.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "dbus/bus.h"
#include "dbus/error.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/login_manager/dbus-constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace ash {
namespace {
SessionManagerClient* g_instance = nullptr;
using RetrievePolicyResponseType =
SessionManagerClient::RetrievePolicyResponseType;
constexpr char kEmptyAccountId[] = "";
// The timeout used when starting the android container is 90 seconds
constexpr int kStartArcTimeout = 90 * 1000;
// TODO(b/205032502): Because upgrading the container from mini to full often
// takes more than 25 seconds, increasing it to 1 minute for now. Once we have
// the update metrics, update the timeout to a tighter value.
constexpr int kUpgradeTimeoutMs = 60 * 1000; // 60 seconds
// 10MB. It's the current restriction enforced by session manager.
const size_t kSharedMemoryDataSizeLimit = 10 * 1024 * 1024;
// Copy of values from login_manager::SessionManagerImpl.
// TODO(crbug.com/40071048): Move to system_api/dbus/service_constants.h
constexpr char kStopping[] = "stopping";
// Helper to get the enum type of RetrievePolicyResponseType based on error
// name.
RetrievePolicyResponseType GetPolicyResponseTypeByError(
std::string_view error_name) {
if (error_name == login_manager::dbus_error::kNone) {
return RetrievePolicyResponseType::SUCCESS;
} else if (error_name == login_manager::dbus_error::kGetServiceFail ||
error_name == login_manager::dbus_error::kSessionDoesNotExist) {
// TODO(crbug.com/41344863): Remove kSessionDoesNotExist case once Chrome OS
// has switched to kGetServiceFail.
return RetrievePolicyResponseType::GET_SERVICE_FAIL;
} else if (error_name == login_manager::dbus_error::kSigEncodeFail) {
return RetrievePolicyResponseType::POLICY_ENCODE_ERROR;
}
return RetrievePolicyResponseType::OTHER_ERROR;
}
// Creates a pipe that contains the given data. The data will be prefixed by a
// size_t sized variable containing the size of the data to read. Since we don't
// pass this pipe's read end anywhere, we can be sure that the only FD that can
// read from that pipe will be closed on browser's exit, therefore the password
// won't be leaked if the browser crashes.
base::ScopedFD CreatePasswordPipe(const std::string& data) {
// 64k of data, minus 64 bits for a preceding size. This number was chosen to
// fit all the data in a single pipe buffer and avoid blocking on write.
// (http://man7.org/linux/man-pages/man7/pipe.7.html)
const size_t kPipeDataSizeLimit = 1024 * 64 - sizeof(size_t);
int pipe_fds[2];
if (data.size() > kPipeDataSizeLimit ||
!base::CreateLocalNonBlockingPipe(pipe_fds)) {
DLOG(ERROR) << "Failed to create pipe";
return base::ScopedFD();
}
base::ScopedFD pipe_read_end(pipe_fds[0]);
base::ScopedFD pipe_write_end(pipe_fds[1]);
const size_t data_size = data.size();
base::WriteFileDescriptor(pipe_write_end.get(),
base::byte_span_from_ref(data_size));
base::WriteFileDescriptor(pipe_write_end.get(), data);
return pipe_read_end;
}
// Creates a read-only shared memory region that contains the given data.
base::ScopedFD CreateSharedMemoryRegionFDWithData(const std::string& data) {
if (data.size() > kSharedMemoryDataSizeLimit) {
LOG(ERROR) << "Couldn't create shared memory, data is too big.";
return base::ScopedFD();
}
auto region = base::WritableSharedMemoryRegion::Create(data.size());
base::WritableSharedMemoryMapping mapping = region.Map();
if (!mapping.IsValid())
return base::ScopedFD();
mapping.GetMemoryAsSpan<uint8_t>().copy_from(base::as_byte_span(data));
return base::WritableSharedMemoryRegion::TakeHandleForSerialization(
std::move(region))
.PassPlatformHandle()
.readonly_fd;
}
// Reads |secret_size| bytes from a given shared memory region. Puts result into
// |secret|. |fd| should point at a read-only shared memory region.
bool ReadSecretFromSharedMemory(base::ScopedFD fd,
size_t secret_size,
std::vector<uint8_t>* secret) {
if (secret_size > kSharedMemoryDataSizeLimit) {
LOG(ERROR) << "Couldn't read secret from the shared memory, "
"secret's size is too big.";
return false;
}
auto platform_region(base::subtle::PlatformSharedMemoryRegion::Take(
std::move(fd), base::subtle::PlatformSharedMemoryRegion::Mode::kReadOnly,
secret_size, base::UnguessableToken::Create()));
if (!platform_region.IsValid())
return false;
auto region =
base::ReadOnlySharedMemoryRegion::Deserialize(std::move(platform_region));
if (!region.IsValid())
return false;
base::ReadOnlySharedMemoryMapping mapping = region.Map();
if (!mapping.IsValid())
return false;
secret->resize(secret_size);
memcpy(secret->data(), mapping.memory(), secret->size());
return true;
}
} // namespace
// The SessionManagerClient implementation used in production.
class SessionManagerClientImpl : public SessionManagerClient {
public:
SessionManagerClientImpl() = default;
SessionManagerClientImpl(const SessionManagerClientImpl&) = delete;
SessionManagerClientImpl& operator=(const SessionManagerClientImpl&) = delete;
~SessionManagerClientImpl() override = default;
// SessionManagerClient overrides:
void SetStubDelegate(StubDelegate* delegate) override {
// Do nothing; this isn't a stub implementation.
}
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
bool HasObserver(const Observer* observer) const override {
return observers_.HasObserver(observer);
}
void WaitForServiceToBeAvailable(
chromeos::WaitForServiceToBeAvailableCallback callback) override {
session_manager_proxy_->WaitForServiceToBeAvailable(std::move(callback));
}
bool IsScreenLocked() const override { return screen_is_locked_; }
void EmitLoginPromptVisible() override {
SimpleMethodCallToSessionManager(
login_manager::kSessionManagerEmitLoginPromptVisible);
for (auto& observer : observers_)
observer.EmitLoginPromptVisibleCalled();
}
void EmitAshInitialized() override {
SimpleMethodCallToSessionManager(
login_manager::kSessionManagerEmitAshInitialized);
}
void RestartJob(int socket_fd,
const std::vector<std::string>& argv,
RestartJobReason reason,
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerRestartJob);
dbus::MessageWriter writer(&method_call);
writer.AppendFileDescriptor(socket_fd);
writer.AppendArrayOfStrings(argv);
writer.AppendUint32(static_cast<uint32_t>(reason));
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SaveLoginPassword(const std::string& password) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerSaveLoginPassword);
dbus::MessageWriter writer(&method_call);
base::ScopedFD fd = CreatePasswordPipe(password);
if (fd.get() == -1) {
LOG(WARNING) << "Could not create password pipe.";
return;
}
writer.AppendFileDescriptor(fd.get());
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void LoginScreenStorageStore(
const std::string& key,
const login_manager::LoginScreenStorageMetadata& metadata,
const std::string& data,
LoginScreenStorageStoreCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLoginScreenStorageStore);
dbus::MessageWriter writer(&method_call);
writer.AppendString(key);
const std::string metadata_blob = metadata.SerializeAsString();
writer.AppendArrayOfBytes(base::as_byte_span(metadata_blob));
writer.AppendUint64(data.size());
base::ScopedFD fd = CreateSharedMemoryRegionFDWithData(data);
if (!fd.is_valid()) {
std::string error = "Could not create shared memory.";
std::move(callback).Run(std::move(error));
return;
}
writer.AppendFileDescriptor(fd.get());
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnLoginScreenStorageStore,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void LoginScreenStorageRetrieve(
const std::string& key,
LoginScreenStorageRetrieveCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLoginScreenStorageRetrieve);
dbus::MessageWriter writer(&method_call);
writer.AppendString(key);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnLoginScreenStorageRetrieve,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void LoginScreenStorageListKeys(
LoginScreenStorageListKeysCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLoginScreenStorageListKeys);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnLoginScreenStorageListKeys,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void LoginScreenStorageDelete(const std::string& key) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLoginScreenStorageDelete);
dbus::MessageWriter writer(&method_call);
writer.AppendString(key);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void StartSession(
const cryptohome::AccountIdentifier& cryptohome_id) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartSession);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
writer.AppendString(""); // Unique ID is deprecated
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void StartSessionEx(const cryptohome::AccountIdentifier& cryptohome_id,
bool chrome_side_key_generation) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartSessionEx);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
writer.AppendString(""); // Unique ID is deprecated
writer.AppendBool(chrome_side_key_generation);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void EmitStartedUserSession(
const cryptohome::AccountIdentifier& cryptohome_id) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerEmitStartedUserSession);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void StopSession(login_manager::SessionStopReason reason) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStopSessionWithReason);
dbus::MessageWriter writer(&method_call);
writer.AppendUint32(static_cast<uint32_t>(reason));
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void LoadShillProfile(
const cryptohome::AccountIdentifier& cryptohome_id) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerLoadShillProfile);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void StartDeviceWipe(chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartDeviceWipe);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
for (auto& observer : observers_) {
observer.PowerwashRequested(/*admin_requested*/ false);
}
}
void StartRemoteDeviceWipe(
const enterprise_management::SignedData& signed_command) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartRemoteDeviceWipe);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(signed_command);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
for (auto& observer : observers_) {
observer.PowerwashRequested(/*admin_requested*/ true);
}
}
void ClearForcedReEnrollmentVpd(
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerClearForcedReEnrollmentVpd);
dbus::MessageWriter writer(&method_call);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void UnblockDevModeForEnrollment(
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerUnblockDevModeForEnrollment);
dbus::MessageWriter writer(&method_call);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void UnblockDevModeForCarrierLock(
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerUnblockDevModeForCarrierLock);
dbus::MessageWriter writer(&method_call);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void UnblockDevModeForInitialStateDetermination(
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::
kSessionManagerUnblockDevModeForInitialStateDetermination);
dbus::MessageWriter writer(&method_call);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void StartTPMFirmwareUpdate(const std::string& update_mode) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartTPMFirmwareUpdate);
dbus::MessageWriter writer(&method_call);
writer.AppendString(update_mode);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void RequestLockScreen() override {
SimpleMethodCallToSessionManager(login_manager::kSessionManagerLockScreen);
}
void NotifyLockScreenShown() override {
SimpleMethodCallToSessionManager(
login_manager::kSessionManagerHandleLockScreenShown);
}
void NotifyLockScreenDismissed() override {
SimpleMethodCallToSessionManager(
login_manager::kSessionManagerHandleLockScreenDismissed);
}
bool BlockingRequestBrowserDataMigration(
const cryptohome::AccountIdentifier& cryptohome_id,
const std::string& mode) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartBrowserDataMigration);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
writer.AppendString(mode);
auto result = blocking_method_caller_->CallMethodAndBlock(&method_call);
if (!result.has_value()) {
LOG(ERROR) << "BlockingRequestBrowserDataMigration failed :"
<< result.error().name() << ":" << result.error().message();
return false;
}
return true;
}
bool BlockingRequestBrowserDataBackwardMigration(
const cryptohome::AccountIdentifier& cryptohome_id) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartBrowserDataBackwardMigration);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
auto result = blocking_method_caller_->CallMethodAndBlock(&method_call);
if (!result.has_value()) {
LOG(ERROR) << "BlockingRequestBrowserDataBackwardMigration failed :"
<< result.error().name() << ":" << result.error().message();
return false;
}
return true;
}
void RetrieveActiveSessions(ActiveSessionsCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerRetrieveActiveSessions);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnRetrieveActiveSessions,
weak_ptr_factory_.GetWeakPtr(),
login_manager::kSessionManagerRetrieveActiveSessions,
std::move(callback)));
}
void RetrievePolicy(const login_manager::PolicyDescriptor& descriptor,
RetrievePolicyCallback callback) override {
CallRetrievePolicy(descriptor, std::move(callback));
}
RetrievePolicyResponseType BlockingRetrievePolicy(
const login_manager::PolicyDescriptor& descriptor,
std::string* policy_out) override {
return CallBlockingRetrievePolicy(descriptor, policy_out);
}
void StoreDevicePolicy(const std::string& policy_blob,
chromeos::VoidDBusMethodCallback callback) override {
login_manager::PolicyDescriptor descriptor =
ash::MakeChromePolicyDescriptor(login_manager::ACCOUNT_TYPE_DEVICE,
kEmptyAccountId);
CallStorePolicy(descriptor, policy_blob, std::move(callback));
}
void StorePolicyForUser(const cryptohome::AccountIdentifier& cryptohome_id,
const std::string& policy_blob,
chromeos::VoidDBusMethodCallback callback) override {
login_manager::PolicyDescriptor descriptor =
ash::MakeChromePolicyDescriptor(login_manager::ACCOUNT_TYPE_USER,
cryptohome_id.account_id());
CallStorePolicy(descriptor, policy_blob, std::move(callback));
}
void StoreDeviceLocalAccountPolicy(
const std::string& account_name,
const std::string& policy_blob,
chromeos::VoidDBusMethodCallback callback) override {
login_manager::PolicyDescriptor descriptor =
ash::MakeChromePolicyDescriptor(
login_manager::ACCOUNT_TYPE_DEVICE_LOCAL_ACCOUNT, account_name);
CallStorePolicy(descriptor, policy_blob, std::move(callback));
}
void StorePolicy(const login_manager::PolicyDescriptor& descriptor,
const std::string& policy_blob,
chromeos::VoidDBusMethodCallback callback) override {
CallStorePolicy(descriptor, policy_blob, std::move(callback));
}
bool SupportsBrowserRestart() const override { return true; }
void SetFlagsForUser(const cryptohome::AccountIdentifier& cryptohome_id,
const std::vector<std::string>& flags) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerSetFlagsForUser);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
writer.AppendArrayOfStrings(flags);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void SetFeatureFlagsForUser(
const cryptohome::AccountIdentifier& cryptohome_id,
const std::vector<std::string>& feature_flags,
const std::map<std::string, std::string>& origin_list_flags) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerSetFeatureFlagsForUser);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
writer.AppendArrayOfStrings(feature_flags);
dbus::MessageWriter dict_writer(nullptr);
writer.OpenArray("{ss}", &dict_writer);
for (const auto& origin_entry : origin_list_flags) {
dbus::MessageWriter entry_writer(nullptr);
dict_writer.OpenDictEntry(&entry_writer);
entry_writer.AppendString(origin_entry.first);
entry_writer.AppendString(origin_entry.second);
dict_writer.CloseContainer(&entry_writer);
}
writer.CloseContainer(&dict_writer);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
void GetServerBackedStateKeys(StateKeysCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerGetServerBackedStateKeys);
// Infinite timeout needed because the state keys are not generated as long
// as the time sync hasn't been done (which requires network).
// TODO(igorcov): Since this is a resource allocated that could last a long
// time, we will need to change the behavior to either listen to
// LastSyncInfo event from tlsdated or communicate through signals with
// session manager in this particular flow.
session_manager_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_INFINITE,
base::BindOnce(&SessionManagerClientImpl::OnGetServerBackedStateKeys,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void GetPsmDeviceActiveSecret(
PsmDeviceActiveSecretCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerGetPsmDeviceActiveSecret);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnGetPsmDeviceActiveSecret,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void StartArcMiniContainer(
const arc::StartArcMiniInstanceRequest& request,
chromeos::VoidDBusMethodCallback callback) override {
DCHECK(!callback.is_null());
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStartArcMiniContainer);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
session_manager_proxy_->CallMethod(
&method_call, kStartArcTimeout,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void UpgradeArcContainer(const arc::UpgradeArcContainerRequest& request,
chromeos::VoidDBusMethodCallback callback) override {
DCHECK(!callback.is_null());
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerUpgradeArcContainer);
dbus::MessageWriter writer(&method_call);
writer.AppendProtoAsArrayOfBytes(request);
session_manager_proxy_->CallMethod(
&method_call, kUpgradeTimeoutMs,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void StopArcInstance(const std::string& account_id,
bool should_backup_log,
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStopArcInstance);
dbus::MessageWriter writer(&method_call);
writer.AppendString(account_id);
writer.AppendBool(should_backup_log);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void SetArcCpuRestriction(
login_manager::ContainerCpuRestrictionState restriction_state,
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerSetArcCpuRestriction);
dbus::MessageWriter writer(&method_call);
writer.AppendUint32(restriction_state);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void EmitArcBooted(const cryptohome::AccountIdentifier& cryptohome_id,
chromeos::VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerEmitArcBooted);
dbus::MessageWriter writer(&method_call);
writer.AppendString(cryptohome_id.account_id());
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void GetArcStartTime(
chromeos::DBusMethodCallback<base::TimeTicks> callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerGetArcStartTimeTicks);
session_manager_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnGetArcStartTime,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void EnableAdbSideload(EnableAdbSideloadCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerEnableAdbSideload);
session_manager_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnEnableAdbSideload,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void QueryAdbSideload(QueryAdbSideloadCallback callback) override {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerQueryAdbSideload);
session_manager_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnQueryAdbSideload,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void Init(dbus::Bus* bus) {
session_manager_proxy_ = bus->GetObjectProxy(
login_manager::kSessionManagerServiceName,
dbus::ObjectPath(login_manager::kSessionManagerServicePath));
blocking_method_caller_ = std::make_unique<chromeos::BlockingMethodCaller>(
bus, session_manager_proxy_);
// Signals emitted on the session manager's interface.
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kOwnerKeySetSignal,
base::BindRepeating(&SessionManagerClientImpl::OwnerKeySetReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kPropertyChangeCompleteSignal,
base::BindRepeating(
&SessionManagerClientImpl::PropertyChangeCompleteReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kScreenIsLockedSignal,
base::BindRepeating(&SessionManagerClientImpl::ScreenIsLockedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kScreenIsUnlockedSignal,
base::BindRepeating(&SessionManagerClientImpl::ScreenIsUnlockedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kArcInstanceStopped,
base::BindRepeating(
&SessionManagerClientImpl::ArcInstanceStoppedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
session_manager_proxy_->ConnectToSignal(
login_manager::kSessionManagerInterface,
login_manager::kSessionStateChangedSignal,
base::BindRepeating(&SessionManagerClientImpl::SessionStateChanged,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&SessionManagerClientImpl::SignalConnected,
weak_ptr_factory_.GetWeakPtr()));
}
private:
// Makes a method call to the session manager with no arguments and no
// response.
void SimpleMethodCallToSessionManager(const std::string& method_name) {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
method_name);
session_manager_proxy_->CallMethod(&method_call,
dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::DoNothing());
}
// Called when the method call without result is completed.
void OnVoidMethod(chromeos::VoidDBusMethodCallback callback,
dbus::Response* response) {
std::move(callback).Run(response);
}
// Non-blocking call to Session Manager to retrieve policy.
void CallRetrievePolicy(const login_manager::PolicyDescriptor& descriptor,
RetrievePolicyCallback callback) {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerRetrievePolicyEx);
dbus::MessageWriter writer(&method_call);
const std::string descriptor_blob = descriptor.SerializeAsString();
// static_cast does not work due to signedness.
writer.AppendArrayOfBytes(base::as_byte_span(descriptor_blob));
session_manager_proxy_->CallMethodWithErrorResponse(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&SessionManagerClientImpl::OnRetrievePolicy,
weak_ptr_factory_.GetWeakPtr(),
descriptor.account_type(), std::move(callback)));
}
// Blocking call to Session Manager to retrieve policy.
RetrievePolicyResponseType CallBlockingRetrievePolicy(
const login_manager::PolicyDescriptor& descriptor,
std::string* policy_out) {
dbus::MethodCall method_call(
login_manager::kSessionManagerInterface,
login_manager::kSessionManagerRetrievePolicyEx);
dbus::MessageWriter writer(&method_call);
const std::string descriptor_blob = descriptor.SerializeAsString();
// static_cast does not work due to signedness.
writer.AppendArrayOfBytes(base::as_byte_span(descriptor_blob));
auto result = blocking_method_caller_->CallMethodAndBlock(&method_call);
RetrievePolicyResponseType response_type =
RetrievePolicyResponseType::SUCCESS;
if (!result.has_value() && result.error().IsValid()) {
response_type = GetPolicyResponseTypeByError(result.error().name());
}
if (response_type == RetrievePolicyResponseType::SUCCESS) {
ExtractPolicyResponseString(descriptor.account_type(),
result.has_value() ? result->get() : nullptr,
policy_out);
} else {
policy_out->clear();
}
return response_type;
}
void CallStorePolicy(const login_manager::PolicyDescriptor& descriptor,
const std::string& policy_blob,
chromeos::VoidDBusMethodCallback callback) {
dbus::MethodCall method_call(login_manager::kSessionManagerInterface,
login_manager::kSessionManagerStorePolicyEx);
dbus::MessageWriter writer(&method_call);
const std::string descriptor_blob = descriptor.SerializeAsString();
// static_cast does not work due to signedness.
writer.AppendArrayOfBytes(base::as_byte_span(descriptor_blob));
writer.AppendArrayOfBytes(base::as_byte_span(policy_blob));
// The timeout is intentionally chosen to be that big because on some
// devices the operation is slow and a short timeout would lead to
// unnecessary enrollment failures. See crbug.com/1155533 for context.
session_manager_proxy_->CallMethod(
&method_call, /*timeout_ms=*/180000,
base::BindOnce(&SessionManagerClientImpl::OnVoidMethod,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
// Called when kSessionManagerRetrieveActiveSessions method is complete.
void OnRetrieveActiveSessions(const std::string& method_name,
ActiveSessionsCallback callback,
dbus::Response* response) {
if (!response) {
std::move(callback).Run(std::nullopt);
return;
}
dbus::MessageReader reader(response);
dbus::MessageReader array_reader(nullptr);
if (!reader.PopArray(&array_reader)) {
LOG(ERROR) << method_name
<< " response is incorrect: " << response->ToString();
std::move(callback).Run(std::nullopt);
return;
}
ActiveSessionsMap sessions;
while (array_reader.HasMoreData()) {
dbus::MessageReader dict_entry_reader(nullptr);
std::string key;
std::string value;
if (!array_reader.PopDictEntry(&dict_entry_reader) ||
!dict_entry_reader.PopString(&key) ||
!dict_entry_reader.PopString(&value)) {
LOG(ERROR) << method_name
<< " response is incorrect: " << response->ToString();
} else {
sessions[key] = value;
}
}
std::move(callback).Run(std::move(sessions));
}
void OnLoginScreenStorageStore(LoginScreenStorageStoreCallback callback,
dbus::Response* response) {
std::move(callback).Run(std::nullopt);
}
void OnLoginScreenStorageRetrieve(LoginScreenStorageRetrieveCallback callback,
dbus::Response* response) {
if (!response) {
std::move(callback).Run(std::nullopt /* data */,
"LoginScreenStorageRetrieve() D-Bus method "
"returned an empty response");
return;
}
dbus::MessageReader reader(response);
base::ScopedFD result_fd;
uint64_t result_size;
if (!reader.PopUint64(&result_size) ||
!reader.PopFileDescriptor(&result_fd)) {
std::string error = "Invalid response: " + response->ToString();
std::move(callback).Run(std::nullopt /* data */, error);
return;
}
std::vector<uint8_t> result_data;
if (!ReadSecretFromSharedMemory(std::move(result_fd), result_size,
&result_data)) {
std::string error = "Couldn't read retrieved data from shared memory.";
std::move(callback).Run(std::nullopt /* data */, error);
return;
}
std::move(callback).Run(std::string(result_data.begin(), result_data.end()),
std::nullopt /* error */);
}
void OnLoginScreenStorageListKeys(LoginScreenStorageListKeysCallback callback,
dbus::Response* response) {
if (!response) {
// TODO(voit): Add more granular error handling: key is not found error vs
// general 'something went wrong' error.
std::move(callback).Run({} /* keys */,
"LoginScreenStorageListKeys() D-Bus method "
"returned an empty response");
return;
}
dbus::MessageReader reader(response);
std::vector<std::string> keys;
if (!reader.PopArrayOfStrings(&keys)) {
std::string error = "Invalid response: " + response->ToString();
std::move(callback).Run({} /* keys */, error);
return;
}
std::move(callback).Run(std::move(keys), std::nullopt /* error */);
}
// Reads an array of policy data bytes data as std::string.
void ExtractPolicyResponseString(
login_manager::PolicyAccountType account_type,
dbus::Response* response,
std::string* extracted) {
if (!response) {
LOG(ERROR) << "Failed to call RetrievePolicyEx for account type "
<< account_type;
return;
}
dbus::MessageReader reader(response);
const uint8_t* values = nullptr;
size_t length = 0;
if (!reader.PopArrayOfBytes(&values, &length)) {
LOG(ERROR) << "Invalid response: " << response->ToString();
return;
}
// static_cast does not work due to signedness.
extracted->assign(reinterpret_cast<const char*>(values), length);
}
// Called when kSessionManagerRetrievePolicy or
// kSessionManagerRetrievePolicyForUser method is complete.
void OnRetrievePolicy(login_manager::PolicyAccountType account_type,
RetrievePolicyCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error) {
if (!response) {
RetrievePolicyResponseType response_type =
GetPolicyResponseTypeByError(error ? error->GetErrorName() : "");
std::move(callback).Run(response_type, std::string());
return;
}
dbus::MessageReader reader(response);
std::string proto_blob;
ExtractPolicyResponseString(account_type, response, &proto_blob);
std::move(callback).Run(RetrievePolicyResponseType::SUCCESS, proto_blob);
}
// Called when the owner key set signal is received.
void OwnerKeySetReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
std::string result_string;
if (!reader.PopString(&result_string)) {
LOG(ERROR) << "Invalid signal: " << signal->ToString();
return;
}
const bool success = base::StartsWith(result_string, "success",
base::CompareCase::INSENSITIVE_ASCII);
for (auto& observer : observers_)
observer.OwnerKeySet(success);
}
// Called when the property change complete signal is received.
void PropertyChangeCompleteReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
std::string result_string;
if (!reader.PopString(&result_string)) {
LOG(ERROR) << "Invalid signal: " << signal->ToString();
return;
}
const bool success = base::StartsWith(result_string, "success",
base::CompareCase::INSENSITIVE_ASCII);
for (auto& observer : observers_)
observer.PropertyChangeComplete(success);
}
void ScreenIsLockedReceived(dbus::Signal* signal) {
screen_is_locked_ = true;
for (auto& observer : observers_)
observer.ScreenLockedStateUpdated();
}
void ScreenIsUnlockedReceived(dbus::Signal* signal) {
screen_is_locked_ = false;
for (auto& observer : observers_)
observer.ScreenLockedStateUpdated();
}
void ArcInstanceStoppedReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
uint32_t reason = 0;
if (!reader.PopUint32(&reason)) {
LOG(ERROR) << "Invalid signal: " << signal->ToString();
return;
}
for (auto& observer : observers_) {
observer.ArcInstanceStopped(
static_cast<login_manager::ArcContainerStopReason>(reason));
}
}
void SessionStateChanged(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
std::string result_string;
if (!reader.PopString(&result_string)) {
LOG(ERROR) << "Invalid signal: " << signal->ToString();
return;
}
if (result_string == kStopping) {
for (auto& observer : observers_) {
observer.SessionStopping();
}
}
}
// Called when the object is connected to the signal.
void SignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
LOG_IF(ERROR, !success) << "Failed to connect to " << signal_name;
}
// Called when kSessionManagerGetServerBackedStateKeys method is complete.
void OnGetServerBackedStateKeys(StateKeysCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (error_response &&
error_response->GetErrorName() ==
login_manager::dbus_error::kStateKeysRequestFail) {
// Session manager failed to generate state keys, report identifiers
// error.
return std::move(callback).Run(
base::unexpected(StateKeyErrorType::kMissingIdentifiers));
}
if (!response) {
// DBus call failed with unspecified error, report communication error.
return std::move(callback).Run(
base::unexpected(StateKeyErrorType::kCommunicationError));
}
dbus::MessageReader reader(response);
dbus::MessageReader array_reader(nullptr);
if (!reader.PopArray(&array_reader)) {
LOG(ERROR) << "Bad response (not an array of keys): "
<< response->ToString();
std::move(callback).Run(
base::unexpected(StateKeyErrorType::kInvalidResponse));
return;
}
std::vector<std::string> state_keys;
while (array_reader.HasMoreData()) {
const uint8_t* data = nullptr;
size_t size = 0;
if (!array_reader.PopArrayOfBytes(&data, &size)) {
LOG(ERROR) << "Bad response (not an array of bytes): "
<< response->ToString();
std::move(callback).Run(
base::unexpected(StateKeyErrorType::kInvalidResponse));
return;
}
if (size == 0) {
LOG(ERROR) << "Bad response (empty array of bytes): "
<< response->ToString();
std::move(callback).Run(
base::unexpected(StateKeyErrorType::kInvalidResponse));
return;
}
state_keys.emplace_back(reinterpret_cast<const char*>(data), size);
}
if (state_keys.empty()) {
// TODO(b/318708647): Session manager did not report an error but still
// responded with empty state keys. Report something else than missing
// identifiers.
std::move(callback).Run(
base::unexpected(StateKeyErrorType::kMissingIdentifiers));
return;
}
std::move(callback).Run(base::ok(std::move(state_keys)));
}
// Called when kSessionManagerGetPsmDeviceActiveSecret method is complete.
void OnGetPsmDeviceActiveSecret(PsmDeviceActiveSecretCallback callback,
dbus::Response* response) {
if (!response) {
LOG(ERROR) << "Failed to get response OnGetPsmDeviceActiveSecret.";
std::move(callback).Run(std::string());
return;
}
std::string psm_device_active_secret;
dbus::MessageReader reader(response);
if (!reader.PopString(&psm_device_active_secret)) {
LOG(ERROR) << "Received a non-string response from dbus.";
std::move(callback).Run(std::string());
return;
}
std::move(callback).Run(psm_device_active_secret);
}
void OnGetArcStartTime(chromeos::DBusMethodCallback<base::TimeTicks> callback,
dbus::Response* response) {
if (!response) {
std::move(callback).Run(std::nullopt);
return;
}
dbus::MessageReader reader(response);
int64_t ticks = 0;
if (!reader.PopInt64(&ticks)) {
LOG(ERROR) << "Invalid response: " << response->ToString();
std::move(callback).Run(std::nullopt);
return;
}
std::move(callback).Run(base::TimeTicks::FromInternalValue(ticks));
}
void OnEnableAdbSideload(EnableAdbSideloadCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error) {
if (!response) {
LOG(ERROR) << "Failed to call EnableAdbSideload: "
<< (error ? error->ToString() : "(null)");
if (error && error->GetErrorName() == DBUS_ERROR_NOT_SUPPORTED) {
std::move(callback).Run(AdbSideloadResponseCode::NEED_POWERWASH);
} else {
std::move(callback).Run(AdbSideloadResponseCode::FAILED);
}
return;
}
bool succeeded;
dbus::MessageReader reader(response);
if (!reader.PopBool(&succeeded)) {
LOG(ERROR) << "Failed to enable sideloading";
std::move(callback).Run(AdbSideloadResponseCode::FAILED);
return;
}
std::move(callback).Run(AdbSideloadResponseCode::SUCCESS);
}
void OnQueryAdbSideload(QueryAdbSideloadCallback callback,
dbus::Response* response,
dbus::ErrorResponse* error) {
if (!response) {
LOG(ERROR) << "Failed to call QueryAdbSideload: "
<< (error ? error->ToString() : "(null)");
if (error && error->GetErrorName() == DBUS_ERROR_NOT_SUPPORTED) {
std::move(callback).Run(AdbSideloadResponseCode::NEED_POWERWASH, false);
} else {
std::move(callback).Run(AdbSideloadResponseCode::FAILED, false);
}
return;
}
bool is_allowed;
dbus::MessageReader reader(response);
if (!reader.PopBool(&is_allowed)) {
LOG(ERROR) << "Failed to interpret the response";
std::move(callback).Run(AdbSideloadResponseCode::FAILED, false);
return;
}
std::move(callback).Run(AdbSideloadResponseCode::SUCCESS, is_allowed);
}
raw_ptr<dbus::ObjectProxy> session_manager_proxy_ = nullptr;
std::unique_ptr<chromeos::BlockingMethodCaller> blocking_method_caller_;
base::ObserverList<Observer>::UncheckedAndDanglingUntriaged observers_{
SessionManagerClient::kObserverListPolicy};
// Most recent screen-lock state received from session_manager.
bool screen_is_locked_ = false;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<SessionManagerClientImpl> weak_ptr_factory_{this};
};
SessionManagerClient::SessionManagerClient() {
DCHECK(!g_instance);
g_instance = this;
}
SessionManagerClient::~SessionManagerClient() {
DCHECK_EQ(this, g_instance);
g_instance = nullptr;
}
// static
void SessionManagerClient::Initialize(dbus::Bus* bus) {
DCHECK(bus);
(new SessionManagerClientImpl)->Init(bus);
}
// static
void SessionManagerClient::InitializeFake() {
// Do not create a new fake if it was initialized early in a browser test (for
// early setup calls dependent on SessionManagerClient).
if (!FakeSessionManagerClient::Get()) {
new FakeSessionManagerClient(
FakeSessionManagerClient::PolicyStorageType::kOnDisk);
}
}
// static
void SessionManagerClient::InitializeFakeInMemory() {
if (!FakeSessionManagerClient::Get()) {
new FakeSessionManagerClient(
FakeSessionManagerClient::PolicyStorageType::kInMemory);
}
}
// static
void SessionManagerClient::Shutdown() {
// Note `g_instance` could be nullptr when ScopedFakeSessionManagerClient is
// used.
delete g_instance;
}
// static
SessionManagerClient* SessionManagerClient::Get() {
return g_instance;
}
} // namespace ash