// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_CROSAPI_BROWSER_LAUNCHER_H_
#define CHROME_BROWSER_ASH_CROSAPI_BROWSER_LAUNCHER_H_
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/scoped_file.h"
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "chrome/browser/ash/crosapi/crosapi_id.h"
#include "chromeos/ash/components/standalone_browser/lacros_selection.h"
#include "components/nacl/common/buildflags.h"
#include "components/policy/core/common/values_util.h"
#include "mojo/public/cpp/platform/platform_channel.h"
namespace base {
struct LaunchOptions;
} // namespace base
namespace user_manager {
class DeviceOwnershipWaiter;
} // namespace user_manager
namespace crosapi {
class PrimaryProfileCreationWaiter;
// Manages launching and terminating Lacros process.
// TODO(crbug.com/40286595): Extract launching logic from BrowserManager to
// BrowserLauncher.
class BrowserLauncher {
public:
BrowserLauncher();
BrowserLauncher(const BrowserLauncher&) = delete;
BrowserLauncher& operator=(const BrowserLauncher&) = delete;
~BrowserLauncher();
// Returns files to preload on launching at login screen.
static std::vector<base::FilePath> GetPreloadFiles(
const base::FilePath& lacros_dir);
// Parameters used to launch Lacros that are calculated on a background
// sequence.
struct LaunchParamsFromBackground {
public:
LaunchParamsFromBackground();
LaunchParamsFromBackground(LaunchParamsFromBackground&&);
LaunchParamsFromBackground& operator=(LaunchParamsFromBackground&&);
LaunchParamsFromBackground(const LaunchParamsFromBackground&) = delete;
LaunchParamsFromBackground& operator=(const LaunchParamsFromBackground&) =
delete;
~LaunchParamsFromBackground();
// An fd for a log file.
base::ScopedFD logfd;
// Sets true if Lacros uses resource file sharing.
bool enable_resource_file_sharing = false;
// Any additional args to start lacros with.
std::vector<std::string> lacros_additional_args;
};
// Parameters to handle command line and options used to launching Lacros.
struct LaunchParams {
public:
LaunchParams(base::CommandLine command_line, base::LaunchOptions options);
LaunchParams(LaunchParams&&);
LaunchParams& operator=(LaunchParams&&);
LaunchParams(const LaunchParams&) = delete;
LaunchParams& operator=(const LaunchParams&) = delete;
~LaunchParams();
base::CommandLine command_line;
base::LaunchOptions options;
};
// Results from `LaunchProcess` and needs to be passed to
// BrowserManager.
struct LaunchResults {
public:
LaunchResults();
LaunchResults(LaunchResults&&);
LaunchResults& operator=(LaunchResults&&);
LaunchResults(const LaunchResults&) = delete;
LaunchResults& operator=(const LaunchResults&) = delete;
~LaunchResults();
// ID for the current Crosapi connection.
// Available only when lacros-chrome is running.
CrosapiId crosapi_id;
// Time when the lacros process was launched.
base::TimeTicks lacros_launch_time;
};
// Reason of Lacros not being able to launch.
enum class LaunchFailureReason {
// Failed to launch due to unknown error.
kUnknown,
// Shutdown is requested from BrowserManager during the process launch.
kShutdownRequested,
};
using LaunchCompletionCallback = base::OnceCallback<void(
base::expected<LaunchResults, LaunchFailureReason>)>;
// Launches a process of the given options, which are expected to be Lacros's
// ones.
// `Launch` will:
// 1. Prepare launching on background thread to handle blocking resources.
// 2. Wait for device owner and primary user if it's launching in the user
// session.
// 3. Launch lacros process with the params prepared at Step 1.
//
// Following is explanation for Arguments.
// `chrome_path`: Initializes `command_line`.
// `launching_at_login_screen`: Whether lacros is launching at login screen.
// `postlogin_pipe_fd`: Pipe FDs through which Ash and Lacros exchange
// post-login parameters.
// `lacros_selection`: Whether "rootfs" or "stateful" lacros is selected.
// `mojo_disconnection_cb`: Callback function setting up mojo connection.
// `BrowserManager::OnMojoDisconnected` is called.
// `is_keep_alive_enabled`: Whether `keep_alive_features` is empty.
// `callback`: Callback function that will be called on launch process
// completion.
void Launch(const base::FilePath& chrome_path,
bool launching_at_login_screen,
ash::standalone_browser::LacrosSelection lacros_selection,
base::OnceClosure mojo_disconnection_cb,
bool is_keep_alive_enabled,
LaunchCompletionCallback callback);
// Writes post login data to the Lacros process, resets `postlogin_pipe_fd`
// and then executes a callback.
void ResumeLaunch(
base::OnceCallback<
void(base::expected<base::TimeTicks, LaunchFailureReason>)> callback);
// Returns true if process is valid.
bool IsProcessValid() const;
// Triggers termination synchronously if process is running.
// Does not block the thread because it does not wait for the process
// termination.
bool TriggerTerminate(int exit_code) const;
// Waits for termination of the running process asynchronously during the
// period given by the `timeout`, then invoke `callback`. On timeout, also
// tries to terminate the process by sending a signal.
// TODO(mayukoaiba): On calling this function, even before the termination
// procedure is completed (i.e. before `callback` is called), IsProcessValid
// will return false and LaunchProcess tries to create the next process, which
// may be confusing for callers. We should fix this issue.
void EnsureProcessTerminated(base::OnceClosure callback,
base::TimeDelta timeout);
// Records Shutdown() request from BrowserManager.
void Shutdown() { shutdown_requested_ = true; }
// Returns reference to `process_` for testing.
const base::Process& GetProcessForTesting() const;
// Provides public API to call LaunchProcessWithParameters for testing.
bool LaunchProcessForTesting(const LaunchParams& parameters);
// Provides public API to call CreateLaunchParamsForTesting for testing.
LaunchParams CreateLaunchParamsForTesting(
const base::FilePath& chrome_path,
const LaunchParamsFromBackground& params,
bool launching_at_login_screen,
std::optional<int> startup_fd,
std::optional<int> read_pipe_fd,
mojo::PlatformChannel& channel,
ash::standalone_browser::LacrosSelection lacros_selection);
// Creates postlogin pipe fd and returns the read fd. This is used to test
// ResumeLaunch. Note that the reader is on the same process and does not
// launch testing process.
base::ScopedFD CreatePostLoginPipeForTesting();
// Sets up additional flags for unit tests.
// This function overwrites `command_line` with the desired flags.
void SetUpAdditionalParametersForTesting(LaunchParamsFromBackground& params,
LaunchParams& parameters) const;
// Provides public API to call WaitForBackgroundWorkPreLaunch for testing.
void WaitForBackgroundWorkPreLaunchForTesting(
const base::FilePath& lacros_dir,
bool clear_shared_resource_file,
bool launching_at_login_screen,
base::OnceClosure callback,
LaunchParamsFromBackground& params);
// TODO(crbug.com/40275396): Remove this once we refactored to use the
// constructor.
void set_device_ownership_waiter_for_testing(
std::unique_ptr<user_manager::DeviceOwnershipWaiter>
device_ownership_waiter);
// Skips device ownership fetch. Use set_device_ownership_waiter_for_testing()
// above if possible. Use this method only if your test must set up the
// behavior before BrowserManager is initialized.
// TODO(crbug.com/40275396): Remove this and set it from constructor.
static void SkipDeviceOwnershipWaitForTesting(bool skip);
private:
// Waits for the prelaunch work running on background thread. `callback` is
// called on background work completion and the output result is stored in
// `params`.
void WaitForBackgroundWorkPreLaunch(const base::FilePath& lacros_dir,
bool clear_shared_resource_file,
bool launching_at_login_screen,
base::OnceClosure callback,
LaunchParamsFromBackground& params);
// Waits for the device owner being fetched from `UserManager` or the primary
// user profile being fully created and then executes a callback. Should NOT
// be called if Lacros is launching at the login screen since the device owner
// nor the profile is not available until login.
void WaitForDeviceOwnerFetchedAndThen(base::OnceClosure callback);
void WaitForPrimaryProfileAddedAndThen(base::OnceClosure callback);
// Launches lacros-chrome process after device owner and primary profile
// become ready.
void LaunchProcess(const base::FilePath& chrome_path,
std::unique_ptr<LaunchParamsFromBackground> params,
bool launching_at_login_screen,
ash::standalone_browser::LacrosSelection lacros_selection,
base::OnceClosure mojo_disconnection_cb,
bool is_keep_alive_enabled,
LaunchCompletionCallback callback);
LaunchParams CreateLaunchParams(
const base::FilePath& chrome_path,
const LaunchParamsFromBackground& params,
bool launching_at_login_screen,
std::optional<int> startup_fd,
std::optional<int> read_pipe_fd,
mojo::PlatformChannel& channel,
ash::standalone_browser::LacrosSelection lacros_selection);
// Launches a process , which is executed in `LaunchProcess`.
// This is also used for unittest.
bool LaunchProcessWithParameters(const LaunchParams& parameters);
// Writes post login data after waiting for device owner and profile to be
// ready.
void WritePostLoginData(
base::OnceCallback<
void(base::expected<base::TimeTicks, LaunchFailureReason>)> callback);
// Process handle for the lacros_chrome process.
base::Process process_;
// Pipe FDs through which Ash and Lacros exchange post-login parameters.
base::ScopedFD postlogin_pipe_fd_;
// Used to delay an action until the definitive device owner is fetched.
std::unique_ptr<user_manager::DeviceOwnershipWaiter> device_ownership_waiter_;
// Used to wait for the primary user profile to be fully created.
std::unique_ptr<PrimaryProfileCreationWaiter>
primary_profile_creation_waiter_;
// Tracks whether Shutdown() has been signalled by ash. This flag ensures any
// new or existing lacros startup tasks are not executed during shutdown.
bool shutdown_requested_ = false;
// True if this is the first time that lacros is being launched from this ash
// process. This value is used for resource sharing feature where ash deletes
// cached shared resource file after ash is rebooted. Note that this flag
// should not be reset on reloading as long as the ash process is not
// relaunched.
bool is_first_lacros_launch_ = true;
// Indicates whether the delegate has been used.
bool device_ownership_waiter_called_ = false;
base::WeakPtrFactory<BrowserLauncher> weak_factory_{this};
};
} // namespace crosapi
#endif // CHROME_BROWSER_ASH_CROSAPI_BROWSER_LAUNCHER_H_