Commit 44a2bf3d authored by Yuke Liao's avatar Yuke Liao Committed by Commit Bot

[lacros] Support mojo connection in testing environment

This CL supports setting up mojo connection between lacros and ash in
testing and debugging environment, and it is done via a unix domain
socket.

Unix domain socket is only used in testing, not production because it
allows a random process to connect to ash-chrome as long as it knows
the socket path.

Currently, only one lacros-chrome is allowed to connect to ash-chrome,
so in the testing environment, test launcher will run tests serially,
and will change to run in parallel once ash-chrome supports multiple
clients.

Bug: 1120582
Change-Id: I9736b03218f8fe937a7d642a35d6ee49b7e081f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2384543
Commit-Queue: Yuke Liao <liaoyuke@chromium.org>
Reviewed-by: default avatarHidehiko Abe <hidehiko@chromium.org>
Reviewed-by: default avatarJorge Lucangeli Obes <jorgelo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805537}
parent 3cf5434d
......@@ -957,6 +957,8 @@ source_set("chromeos") {
"crosapi/screen_manager_ash.h",
"crosapi/select_file_ash.cc",
"crosapi/select_file_ash.h",
"crosapi/test_mojo_connection_manager.cc",
"crosapi/test_mojo_connection_manager.h",
"crostini/ansible/ansible_management_service.cc",
"crostini/ansible/ansible_management_service.h",
"crostini/ansible/ansible_management_service_factory.cc",
......@@ -3156,6 +3158,7 @@ source_set("unit_tests") {
"concierge_helper_service_unittest.cc",
"crosapi/browser_util_unittest.cc",
"crosapi/message_center_ash_unittest.cc",
"crosapi/test_mojo_connection_manager_unittest.cc",
"crostini/ansible/ansible_management_service_unittest.cc",
"crostini/crostini_disk_unittest.cc",
"crostini/crostini_export_import_unittest.cc",
......
......@@ -30,6 +30,7 @@
#include "chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h"
#include "chrome/browser/chromeos/crosapi/browser_loader.h"
#include "chrome/browser/chromeos/crosapi/browser_util.h"
#include "chrome/browser/chromeos/crosapi/test_mojo_connection_manager.h"
#include "chrome/browser/component_updater/cros_component_manager.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/constants/chromeos_features.h"
......@@ -39,7 +40,6 @@
#include "google_apis/google_api_keys.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
// TODO(crbug.com/1101667): Currently, this source has log spamming
// by LOG(WARNING) for non critical errors to make it easy
......@@ -146,6 +146,15 @@ BrowserManager::BrowserManager(
// devices restart Chrome during login to apply flags. We don't want to run
// the flag-off cleanup logic until we know we have the final flag state.
session_manager::SessionManager::Get()->AddObserver(this);
std::string socket_path =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
chromeos::switches::kLacrosMojoSocketForTesting);
if (!socket_path.empty()) {
test_mojo_connection_manager_ =
std::make_unique<crosapi::TestMojoConnectionManager>(
base::FilePath(socket_path));
}
}
BrowserManager::~BrowserManager() {
......@@ -282,19 +291,11 @@ void BrowserManager::StartWithLogFile(base::ScopedFD logfd) {
mojo::PlatformChannel channel;
channel.PrepareToPassRemoteEndpoint(&options, &command_line);
// Queue messages to establish the mojo connection,
// so that the passed IPC is available already when lacros-chrome accepts
// the invitation.
// TODO(crbug.com/1115092): Pass the initialization parameter over
// mojo connection.
mojo::OutgoingInvitation invitation;
lacros_chrome_service_.Bind(
mojo::PendingRemote<crosapi::mojom::LacrosChromeService>(
invitation.AttachMessagePipe(0), /*version=*/0));
lacros_chrome_service_.set_disconnect_handler(base::BindOnce(
&BrowserManager::OnMojoDisconnected, weak_factory_.GetWeakPtr()));
lacros_chrome_service_->Init(crosapi::mojom::LacrosInitParams::New());
lacros_chrome_service_->RequestAshChromeServiceReceiver(
// TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
lacros_chrome_service_ = browser_util::SendMojoInvitationToLacrosChrome(
channel.TakeLocalEndpoint(),
base::BindOnce(&BrowserManager::OnMojoDisconnected,
weak_factory_.GetWeakPtr()),
base::BindOnce(&BrowserManager::OnAshChromeServiceReceiverReceived,
weak_factory_.GetWeakPtr()));
......@@ -310,12 +311,7 @@ void BrowserManager::StartWithLogFile(base::ScopedFD logfd) {
}
state_ = State::STARTING;
LOG(WARNING) << "Launched lacros-chrome with pid " << lacros_process_.Pid();
// Invite the lacros-chrome to the mojo universe.
channel.RemoteProcessLaunchAttempted();
mojo::OutgoingInvitation::Send(std::move(invitation),
lacros_process_.Handle(),
channel.TakeLocalEndpoint());
}
void BrowserManager::OnAshChromeServiceReceiverReceived(
......
......@@ -24,6 +24,7 @@ namespace crosapi {
class AshChromeServiceImpl;
class BrowserLoader;
class TestMojoConnectionManager;
// Manages the lifetime of lacros-chrome, and its loading status. This class is
// a part of ash-chrome.
......@@ -150,6 +151,11 @@ class BrowserManager : public session_manager::SessionManagerObserver {
// Instantiated on receiving the PendingReceiver from lacros-chrome.
std::unique_ptr<AshChromeServiceImpl> ash_chrome_service_;
// Helps set up and manage the mojo connections between lacros-chrome and
// ash-chrome in testing environment. Only applicable when
// '--lacros-mojo-socket-for-testing' is present in the command line.
std::unique_ptr<TestMojoConnectionManager> test_mojo_connection_manager_;
base::WeakPtrFactory<BrowserManager> weak_factory_{this};
};
......
......@@ -4,8 +4,12 @@
#include "chrome/browser/chromeos/crosapi/browser_util.h"
#include <utility>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/process/process_handle.h"
#include "base/strings/string_util.h"
#include "base/system/sys_info.h"
#include "chrome/common/channel_info.h"
......@@ -15,6 +19,8 @@
#include "components/user_manager/user_manager.h"
#include "components/user_manager/user_type.h"
#include "components/version_info/channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
using user_manager::User;
using version_info::Channel;
......@@ -89,5 +95,28 @@ bool IsLacrosAllowed(Channel channel) {
}
}
mojo::Remote<crosapi::mojom::LacrosChromeService>
SendMojoInvitationToLacrosChrome(
mojo::PlatformChannelEndpoint local_endpoint,
base::OnceClosure mojo_disconnected_callback,
base::OnceCallback<
void(mojo::PendingReceiver<crosapi::mojom::AshChromeService>)>
ash_chrome_service_callback) {
mojo::OutgoingInvitation invitation;
mojo::Remote<crosapi::mojom::LacrosChromeService> lacros_chrome_service;
lacros_chrome_service.Bind(
mojo::PendingRemote<crosapi::mojom::LacrosChromeService>(
invitation.AttachMessagePipe(0 /* token */), /*version=*/0));
lacros_chrome_service.set_disconnect_handler(
std::move(mojo_disconnected_callback));
lacros_chrome_service->Init(crosapi::mojom::LacrosInitParams::New());
lacros_chrome_service->RequestAshChromeServiceReceiver(
std::move(ash_chrome_service_callback));
mojo::OutgoingInvitation::Send(std::move(invitation),
base::kNullProcessHandle,
std::move(local_endpoint));
return lacros_chrome_service;
}
} // namespace browser_util
} // namespace crosapi
......@@ -5,12 +5,21 @@
#ifndef CHROME_BROWSER_CHROMEOS_CROSAPI_BROWSER_UTIL_H_
#define CHROME_BROWSER_CHROMEOS_CROSAPI_BROWSER_UTIL_H_
#include "base/callback_forward.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
class PrefRegistrySimple;
namespace base {
class FilePath;
} // namespace base
namespace mojo {
class PlatformChannelEndpoint;
} // namespace mojo
namespace version_info {
enum class Channel;
} // namespace version_info
......@@ -35,6 +44,19 @@ bool IsLacrosAllowed();
// As above, but takes a channel. Exposed for testing.
bool IsLacrosAllowed(version_info::Channel channel);
// Invite the lacros-chrome to the mojo universe.
// Queue messages to establish the mojo connection, so that the passed IPC is
// available already when lacros-chrome accepts the invitation.
// TODO(crbug.com/1115092): Pass the initialization parameter over mojo
// connection.
mojo::Remote<crosapi::mojom::LacrosChromeService>
SendMojoInvitationToLacrosChrome(
mojo::PlatformChannelEndpoint local_endpoint,
base::OnceClosure mojo_disconnected_callback,
base::OnceCallback<
void(mojo::PendingReceiver<crosapi::mojom::AshChromeService>)>
ash_chrome_service_callback);
} // namespace browser_util
} // namespace crosapi
......
// 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/crosapi/test_mojo_connection_manager.h"
#include <fcntl.h>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h"
#include "chrome/browser/chromeos/crosapi/browser_util.h"
#include "chromeos/constants/chromeos_switches.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/platform/socket_utils_posix.h"
#include "mojo/public/cpp/system/invitation.h"
namespace crosapi {
namespace {
// TODO(crbug.com/1124494): Refactor the code to share with ARC.
base::ScopedFD CreateSocketForTesting(const base::FilePath& socket_path) {
auto endpoint = mojo::NamedPlatformChannel({socket_path.value()});
base::ScopedFD socket_fd =
endpoint.TakeServerEndpoint().TakePlatformHandle().TakeFD();
if (!socket_fd.is_valid()) {
PLOG(ERROR) << "Failed to create socket file: " << socket_path;
return socket_fd;
}
if (!base::SetPosixFilePermissions(socket_path, 0660)) {
PLOG(ERROR) << "Could not set permissions on socket file: " << socket_path;
return base::ScopedFD();
}
return socket_fd;
}
} // namespace
TestMojoConnectionManager::TestMojoConnectionManager(
const base::FilePath& socket_path) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CreateSocketForTesting, socket_path),
base::BindOnce(&TestMojoConnectionManager::OnTestingSocketCreated,
weak_factory_.GetWeakPtr()));
}
TestMojoConnectionManager::~TestMojoConnectionManager() = default;
void TestMojoConnectionManager::OnTestingSocketCreated(
base::ScopedFD socket_fd) {
if (!socket_fd.is_valid())
return;
testing_socket_ = std::move(socket_fd);
testing_socket_watcher_ = base::FileDescriptorWatcher::WatchReadable(
testing_socket_.get(),
base::BindRepeating(&TestMojoConnectionManager::OnTestingSocketAvailable,
weak_factory_.GetWeakPtr()));
}
void TestMojoConnectionManager::OnTestingSocketAvailable() {
base::ScopedFD connection_fd;
if (!mojo::AcceptSocketConnection(testing_socket_.get(), &connection_fd,
/* check_peer_user = */ false) ||
!connection_fd.is_valid()) {
LOG(ERROR) << "Failed to Accept the socket";
return;
}
// TODO(crbug.com/1124490): Support multiple mojo connections from lacros.
mojo::PlatformChannel channel;
lacros_chrome_service_ = browser_util::SendMojoInvitationToLacrosChrome(
channel.TakeLocalEndpoint(),
base::BindOnce(&TestMojoConnectionManager::OnMojoDisconnected,
weak_factory_.GetWeakPtr()),
base::BindOnce(
&TestMojoConnectionManager::OnAshChromeServiceReceiverReceived,
weak_factory_.GetWeakPtr()));
std::vector<base::ScopedFD> fds;
fds.emplace_back(channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD());
// Version of protocol Chrome is using.
uint8_t protocol_version = 0;
struct iovec iov[] = {{&protocol_version, sizeof(protocol_version)}};
ssize_t result = mojo::SendmsgWithHandles(connection_fd.get(), iov,
sizeof(iov) / sizeof(iov[0]), fds);
if (result == -1) {
PLOG(ERROR) << "Failed to send file descriptors to the socket";
return;
}
}
void TestMojoConnectionManager::OnAshChromeServiceReceiverReceived(
mojo::PendingReceiver<crosapi::mojom::AshChromeService> pending_receiver) {
ash_chrome_service_ =
std::make_unique<AshChromeServiceImpl>(std::move(pending_receiver));
LOG(INFO) << "Connection to lacros-chrome is established.";
}
void TestMojoConnectionManager::OnMojoDisconnected() {
lacros_chrome_service_.reset();
ash_chrome_service_ = nullptr;
LOG(WARNING) << "Mojo to lacros-chrome is disconnected.";
}
} // namespace crosapi
// 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_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_H_
#include <memory>
#include <utility>
#include <vector>
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace crosapi {
class AshChromeServiceImpl;
// An extension of BrowserManager to help set up and manage the mojo connections
// between the test executable and ash-chrome in testing environment.
//
// In testing environment, the workflow is as following:
// - Ash-chrome creates a Unix domain socket.
// - Test executable connects to the Unix domain socket.
// - When ash-chrome accepts the connection, it creates a |PlatformChannel| and
// sends one end of it (as a FD) over the socket.
// - Test executable reads the FD from the socket and passes it to lacros-chrome
// when launching a test.
//
// The workflow works for debugging as well, a wapper script can play the role
// of the test executable above to obtain the FD, and passes it to lacros-chrome
// when launching it inside gdb.
class TestMojoConnectionManager {
public:
explicit TestMojoConnectionManager(const base::FilePath& socket_path);
TestMojoConnectionManager(const TestMojoConnectionManager&) = delete;
TestMojoConnectionManager& operator=(const TestMojoConnectionManager&) =
delete;
~TestMojoConnectionManager();
private:
// Called when the testing socket is created.
void OnTestingSocketCreated(base::ScopedFD socket_fd);
// Called when a client, such as a test launcher, attempts to connect.
void OnTestingSocketAvailable();
// Called when PendingReceiver of AshChromeService is passed from
// lacros-chrome.
void OnAshChromeServiceReceiverReceived(
mojo::PendingReceiver<crosapi::mojom::AshChromeService> pending_receiver);
// Called when the Mojo connection to lacros-chrome is disconnected.
// It may be "just a Mojo error" or "test is finished".
void OnMojoDisconnected();
// Proxy to LacrosChromeService mojo service in lacros-chrome.
// Available during lacros-chrome is running.
mojo::Remote<crosapi::mojom::LacrosChromeService> lacros_chrome_service_;
// Implementation of AshChromeService Mojo APIs.
// Instantiated on receiving the PendingReceiver from lacros-chrome.
std::unique_ptr<AshChromeServiceImpl> ash_chrome_service_;
// A socket for a client, such as a test launcher, to connect to.
base::ScopedFD testing_socket_;
// A watcher that watches |testing_scoket_| and invokes
// |OnTestingSocketAvailable| when it becomes readable.
std::unique_ptr<base::FileDescriptorWatcher::Controller>
testing_socket_watcher_;
base::WeakPtrFactory<TestMojoConnectionManager> weak_factory_{this};
};
} // namespace crosapi
#endif // CHROME_BROWSER_CHROMEOS_CROSAPI_TEST_MOJO_CONNECTION_MANAGER_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/crosapi/test_mojo_connection_manager.h"
#include <fcntl.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_path_watcher.h"
#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/process/launch.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/thread_pool.h"
#include "base/test/bind_test_util.h"
#include "base/test/multiprocess_test.h"
#include "base/test/task_environment.h"
#include "base/test/test_timeouts.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "mojo/public/cpp/platform/named_platform_channel.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/platform/socket_utils_posix.h"
#include "mojo/public/cpp/system/invitation.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/multiprocess_func_list.h"
namespace crosapi {
class TestLacrosChromeService : public crosapi::mojom::LacrosChromeService {
public:
TestLacrosChromeService(
mojo::PendingReceiver<mojom::LacrosChromeService> receiver,
base::RunLoop& run_loop)
: receiver_(this, std::move(receiver)), run_loop_(run_loop) {}
~TestLacrosChromeService() override = default;
void Init(crosapi::mojom::LacrosInitParamsPtr params) override {
init_is_called_ = true;
}
void RequestAshChromeServiceReceiver(
RequestAshChromeServiceReceiverCallback callback) override {
EXPECT_TRUE(init_is_called_);
std::move(callback).Run(ash_chrome_service_.BindNewPipeAndPassReceiver());
request_ash_chrome_service_is_called_ = true;
run_loop_.Quit();
}
void NewWindow(NewWindowCallback callback) override {}
bool init_is_called() { return init_is_called_; }
bool request_ash_chrome_service_is_called() {
return request_ash_chrome_service_is_called_;
}
private:
mojo::Receiver<mojom::LacrosChromeService> receiver_;
bool init_is_called_ = false;
bool request_ash_chrome_service_is_called_ = false;
base::RunLoop& run_loop_;
mojo::Remote<crosapi::mojom::AshChromeService> ash_chrome_service_;
};
class TestMojoConnectionManagerTest : public testing::Test {
public:
TestMojoConnectionManagerTest() = default;
~TestMojoConnectionManagerTest() override = default;
};
TEST_F(TestMojoConnectionManagerTest, ConnectWithLacrosChrome) {
// Use IO type to support the FileDescriptorWatcher API on POSIX.
base::test::TaskEnvironment task_environment{
base::test::TaskEnvironment::MainThreadType::IO};
// Ash-chrome queues an invitation, drop a socket and wait for connection.
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
std::string socket_path =
temp_dir.GetPath().MaybeAsASCII() + "/lacros.socket";
TestMojoConnectionManager test_mojo_connection_manager{
base::FilePath(socket_path)};
// Wait for the socket to be created before attempting to connect.
// There is no garanteen that |OnTestingSocketCreated| has run after the run
// loop is done, so this test should NOT depend on the assumption.
base::FilePathWatcher watcher;
base::RunLoop run_loop1;
watcher.Watch(base::FilePath(socket_path), false,
base::BindRepeating(base::BindLambdaForTesting(
[&run_loop1](const base::FilePath& path, bool error) {
EXPECT_FALSE(error);
run_loop1.Quit();
})));
run_loop1.Run();
// Test connects with ash-chrome via the socket.
auto channel = mojo::NamedPlatformChannel::ConnectToServer(socket_path);
ASSERT_TRUE(channel.is_valid());
base::ScopedFD socket_fd = channel.TakePlatformHandle().TakeFD();
uint8_t buf[32];
std::vector<base::ScopedFD> descriptors;
ssize_t size;
base::RunLoop run_loop2;
base::ThreadPool::PostTaskAndReply(
FROM_HERE, {base::MayBlock()},
base::BindOnce(base::BindLambdaForTesting([&]() {
// Mark the channel as blocking.
int flags = fcntl(socket_fd.get(), F_GETFL);
PCHECK(flags != -1);
fcntl(socket_fd.get(), F_SETFL, flags & ~O_NONBLOCK);
size = mojo::SocketRecvmsg(socket_fd.get(), buf, sizeof(buf),
&descriptors, true /*block*/);
})),
run_loop2.QuitClosure());
run_loop2.Run();
EXPECT_EQ(1, size);
EXPECT_EQ(0u, buf[0]);
ASSERT_EQ(1u, descriptors.size());
// Test launches lacros-chrome as child process.
base::LaunchOptions options;
options.fds_to_remap.emplace_back(descriptors[0].get(), descriptors[0].get());
base::CommandLine lacros_cmd(base::GetMultiProcessTestChildBaseCommandLine());
lacros_cmd.AppendSwitchASCII(mojo::PlatformChannel::kHandleSwitch,
base::NumberToString(descriptors[0].release()));
base::Process lacros_process =
base::SpawnMultiProcessTestChild("LacrosMain", lacros_cmd, options);
// lacros-chrome accepts the invitation to establish mojo connection with
// ash-chrome.
int rv = -1;
ASSERT_TRUE(base::WaitForMultiprocessTestChildExit(
lacros_process, TestTimeouts::action_timeout(), &rv));
lacros_process.Close();
EXPECT_EQ(0, rv);
}
// Another process that emulates the behavior of lacros-chrome.
MULTIPROCESS_TEST_MAIN(LacrosMain) {
base::test::SingleThreadTaskEnvironment task_environment;
mojo::IncomingInvitation invitation = mojo::IncomingInvitation::Accept(
mojo::PlatformChannel::RecoverPassedEndpointFromCommandLine(
*base::CommandLine::ForCurrentProcess()));
base::RunLoop run_loop;
TestLacrosChromeService test_lacros_chrome_service(
mojo::PendingReceiver<crosapi::mojom::LacrosChromeService>(
invitation.ExtractMessagePipe(0)),
run_loop);
run_loop.Run();
DCHECK(test_lacros_chrome_service.init_is_called());
DCHECK(test_lacros_chrome_service.request_ash_chrome_service_is_called());
return 0;
}
} // namespace crosapi
......@@ -431,6 +431,15 @@ const char kLacrosChromeAdditionalArgs[] = "lacros-chrome-additional-args";
// path should be to a directory that contains a binary named 'chrome'.
const char kLacrosChromePath[] = "lacros-chrome-path";
// If set, ash-chrome will drop a Unix domain socket to wait for a process to
// connect to it, and the connection will be used to request file descriptors
// from ash-chrome, and when the process forks to start a lacros-chrome, the
// obtained file descriptor will be used by lacros-chrome to set up the mojo
// connection with ash-chrome. There are mainly two use cases:
// 1. Test launcher to run browser tests in testing environment.
// 2. A terminal to start lacros-chrome with a debugger.
const char kLacrosMojoSocketForTesting[] = "lacros-mojo-socket-for-testing";
// Enables Chrome-as-a-login-manager behavior.
const char kLoginManager[] = "login-manager";
......
......@@ -173,6 +173,8 @@ COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kKernelnextRestrictVMs[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kLacrosChromeAdditionalArgs[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLacrosChromePath[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS)
extern const char kLacrosMojoSocketForTesting[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginManager[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginProfile[];
COMPONENT_EXPORT(CHROMEOS_CONSTANTS) extern const char kLoginUser[];
......
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