blob: e454d2182ae3424e1ea5e12611620171fc799fae [file] [log] [blame]
// 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_ASH_CROSAPI_BROWSER_MANAGER_H_
#define CHROME_BROWSER_ASH_CROSAPI_BROWSER_MANAGER_H_
#include <memory>
#include <set>
#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/process/process.h"
#include "base/time/time.h"
#include "chrome/browser/ash/crosapi/browser_manager_observer.h"
#include "chrome/browser/ash/crosapi/browser_service_host_observer.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/crosapi/crosapi_id.h"
#include "chrome/browser/ash/crosapi/environment_provider.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
#include "components/session_manager/core/session_manager_observer.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace component_updater {
class CrOSComponentManager;
} // namespace component_updater
namespace crosapi {
namespace mojom {
class Crosapi;
} // namespace mojom
class BrowserLoader;
class TestMojoConnectionManager;
using browser_util::LacrosSelection;
// Manages the lifetime of lacros-chrome, and its loading status. This class is
// a part of ash-chrome.
class BrowserManager : public session_manager::SessionManagerObserver,
public BrowserServiceHostObserver,
public policy::CloudPolicyStore::Observer {
public:
// Static getter of BrowserManager instance. In real use cases,
// BrowserManager instance should be unique in the process.
static BrowserManager* Get();
explicit BrowserManager(
scoped_refptr<component_updater::CrOSComponentManager> manager);
BrowserManager(const BrowserManager&) = delete;
BrowserManager& operator=(const BrowserManager&) = delete;
~BrowserManager() override;
// Returns true if the binary is ready to launch or already launched.
// Typical usage is to check IsReady(), then if it returns false,
// call SetLoadCompleteCallback() to be notified when the download completes.
bool IsReady() const;
// Returns true if Lacros is in running state.
// Virtual for testing.
virtual bool IsRunning() const;
// Return true if Lacros is running, launching or terminating.
// We do not want the multi-signin to be available when Lacros is
// running; therefore, we also have to exclude other states (e.g. if
// Lacros is launched and multi-signin is enabled, we would have
// Lacros running and multiple users signed in simultaneously).
// Virtual for testing.
virtual bool IsRunningOrWillRun() const;
// Sets a callback to be called when the binary download completes. The
// download may not be successful.
using LoadCompleteCallback = base::OnceCallback<void(bool success)>;
void SetLoadCompleteCallback(LoadCompleteCallback callback);
// Opens the browser window in lacros-chrome.
// If lacros-chrome is not yet launched, it triggers to launch. If this is
// called again during the setup phase of the launch process, it will be
// ignored. This needs to be called after loading. The condition can be
// checked IsReady(), and if not yet, SetLoadCompletionCallback can be used to
// wait for the loading.
// TODO(crbug.com/1101676): Notify callers the result of opening window
// request. Because of asynchronous operations crossing processes,
// there's no guarantee that the opening window request succeeds.
// Currently, its condition and result are completely hidden behind this
// class, so there's no way for callers to handle such error cases properly.
// This design often leads the flakiness behavior of the product and testing,
// so should be avoided.
void NewWindow(bool incognito);
// Returns true if crosapi interface supports NewFullscreenWindow API.
bool NewFullscreenWindowSupported() const;
using NewFullscreenWindowCallback =
base::OnceCallback<void(crosapi::mojom::CreationResult)>;
// Open a fullscreen browser window in lacros-chrome. The only tab will be
// navigated to the given `url` once the window is launched.
// NOTE: This method is used by Chrome OS web Kiosk session only. The behavior
// may change and it shouldn't be used by anybody else.
// Virtual for testing.
virtual void NewFullscreenWindow(const GURL& url,
NewFullscreenWindowCallback callback);
// Similar to NewWindow(), but opens a tab, instead.
// See crosapi::mojom::BrowserService::NewTab for more details
void NewTab();
// Opens the specified URL in lacros-chrome. If it is not running,
// it launches lacros-chrome with the given URL.
// See crosapi::mojom::BrowserService::OpenUrl for more details.
void OpenUrl(const GURL& url);
// Similar to NewWindow(), but restores a tab recently closed.
// See crosapi::mojom::BrowserService::RestoreTab for more details
void RestoreTab();
// Returns true if crosapi interface supports GetFeedbackData API.
bool GetFeedbackDataSupported() const;
using GetFeedbackDataCallback = base::OnceCallback<void(base::Value)>;
// Gathers Lacros feedback data.
// Virtual for testing.
virtual void GetFeedbackData(GetFeedbackDataCallback callback);
// Returns true if crosapi interface supports GetHistograms API.
bool GetHistogramsSupported() const;
using GetHistogramsCallback = base::OnceCallback<void(const std::string&)>;
// Gets Lacros histograms.
void GetHistograms(GetHistogramsCallback callback);
// Returns true if crosapi interface supports GetActiveTabUrl API.
bool GetActiveTabUrlSupported() const;
using GetActiveTabUrlCallback =
base::OnceCallback<void(const absl::optional<GURL>&)>;
// Gets Url of the active tab from lacros if there is any.
void GetActiveTabUrl(GetActiveTabUrlCallback callback);
void AddObserver(BrowserManagerObserver* observer);
void RemoveObserver(BrowserManagerObserver* observer);
const std::string& browser_version() const { return browser_version_; }
void set_browser_version(const std::string& version) {
browser_version_ = version;
}
// Set the data of device account policy. It is the serialized blob of
// PolicyFetchResponse received from the server, or parsed from the file after
// is was validated by Ash.
void SetDeviceAccountPolicy(const std::string& policy_blob);
// Parameters used to launch Lacros that are calculated on a background
// sequence. Public so that it can be used from private static functions.
struct LaunchParamsFromBackground {
public:
LaunchParamsFromBackground();
LaunchParamsFromBackground(LaunchParamsFromBackground&&);
LaunchParamsFromBackground(const LaunchParamsFromBackground&) = delete;
LaunchParamsFromBackground& operator=(const LaunchParamsFromBackground&) =
delete;
~LaunchParamsFromBackground();
// An fd for a log file.
base::ScopedFD logfd;
// Whether this version of Lacros supports the new account manager.
bool use_new_account_manager = false;
};
protected:
enum class State {
// Lacros is not initialized yet.
// Lacros-chrome loading depends on user type, so it needs to wait
// for user session.
NOT_INITIALIZED,
// User session started, and now it's mounting lacros-chrome.
MOUNTING,
// Lacros-chrome is unavailable. I.e., failed to load for some reason
// or disabled.
UNAVAILABLE,
// Lacros-chrome is loaded and ready for launching.
STOPPED,
// Lacros-chrome is creating a new log file to log to.
CREATING_LOG_FILE,
// Lacros-chrome is launching.
STARTING,
// Mojo connection to lacros-chrome is established so, it's in
// the running state.
RUNNING,
// Lacros-chrome is being terminated soon.
TERMINATING,
};
// Changes |state| value and potentitally notify observers of the change.
void SetState(State state);
// Posts CreateLogFile() and StartWithLogFile() to the thread pool.
// Virtual for tests.
virtual void Start(browser_util::InitialBrowserAction initial_browser_action);
private:
FRIEND_TEST_ALL_PREFIXES(BrowserManagerTest, LacrosKeepAlive);
// Remember the launch mode of Lacros.
void RecordLacrosLaunchMode();
// These ash features are allowed to request that Lacros stay running in the
// background.
enum class Feature {
kTestOnly,
kAppService,
};
// Any instance of this class will ensure that the Lacros browser will stay
// running in the background even when no windows are showing.
class ScopedKeepAlive {
public:
~ScopedKeepAlive();
private:
friend class BrowserManager;
// BrowserManager must outlive this instance.
ScopedKeepAlive(BrowserManager* manager, Feature feature);
BrowserManager* manager_;
Feature feature_;
};
// Ash features that want Lacros to stay running in the background must be
// marked as friends of this class so that lacros owners can audit usage.
std::unique_ptr<ScopedKeepAlive> KeepAlive(Feature feature);
struct BrowserServiceInfo {
BrowserServiceInfo(mojo::RemoteSetElementId mojo_id,
mojom::BrowserService* service,
uint32_t interface_version);
BrowserServiceInfo(const BrowserServiceInfo&);
BrowserServiceInfo& operator=(const BrowserServiceInfo&);
~BrowserServiceInfo();
// ID managed in BrowserServiceHostAsh, which is tied to the |service|.
mojo::RemoteSetElementId mojo_id;
// BrowserService proxy connected to lacros-chrome.
mojom::BrowserService* service;
// Supported interface version of the BrowserService in Lacros-chrome.
uint32_t interface_version;
};
enum class MaybeStartResult {
kNotStarted,
kStarting,
kRunning,
};
// Checks the precondition to start Lacros, and actually trigger to start
// if necessary.
// If the condition to start lacros is not met, kNotStarted is returned.
// If the condition to start lacros is met, and it is not yet started,
// or it is under starting, kStarting is returned.
// Otherwise, i.e., lacros is already running, kRunning is returned.
// |extra_args| will be passed to the argument to launch lacros.
MaybeStartResult MaybeStart(
browser_util::InitialBrowserAction initial_browser_action);
// Starts the lacros-chrome process and redirects stdout/err to file pointed
// by logfd.
void StartWithLogFile(
browser_util::InitialBrowserAction initial_browser_action,
LaunchParamsFromBackground params);
// BrowserServiceHostObserver:
void OnBrowserServiceConnected(CrosapiId id,
mojo::RemoteSetElementId mojo_id,
mojom::BrowserService* browser_service,
uint32_t browser_service_version) override;
void OnBrowserServiceDisconnected(CrosapiId id,
mojo::RemoteSetElementId mojo_id) override;
void OnBrowserRelaunchRequested(CrosapiId id) override;
// Called when the Mojo connection to lacros-chrome is disconnected.
// It may be "just a Mojo error" or "lacros-chrome crash".
// In either case, terminates lacros-chrome, because there's no longer a
// way to communicate with lacros-chrome.
void OnMojoDisconnected();
// Called when lacros-chrome is terminated and successfully wait(2)ed.
void OnLacrosChromeTerminated();
// session_manager::SessionManagerObserver:
void OnSessionStateChanged() override;
// Sets user policy to be propagated to Lacros and subsribes to the user
// policy updates in Ash.
void PrepareLacrosPolicies();
policy::CloudPolicyStore* GetDeviceAccountPolicyStore();
// policy::CloudPolicyStore::Observer:
void OnStoreLoaded(policy::CloudPolicyStore* store) override;
void OnStoreError(policy::CloudPolicyStore* store) override;
void OnStoreDestruction(policy::CloudPolicyStore* store) override;
// Called on load completion.
void OnLoadComplete(const base::FilePath& path, LacrosSelection selection);
// Methods for features to register and de-register for needing to keep Lacros
// alive.
void StartKeepAlive(Feature feature);
void StopKeepAlive(Feature feature);
// The implementation of keep-alive is simple: every time state_ becomes
// STOPPED, launch Lacros.
void LaunchForKeepAliveIfNecessary();
// If no features want to keep Lacros alive, and it's running in the
// background, then stop running Lacros.
void UnlauchForKeepAlive();
State state_ = State::NOT_INITIALIZED;
// May be null in tests.
scoped_refptr<component_updater::CrOSComponentManager> component_manager_;
std::unique_ptr<crosapi::BrowserLoader> browser_loader_;
// Path to the lacros-chrome disk image directory.
base::FilePath lacros_path_;
// Whether we are starting "rootfs" or "stateful" lacros.
absl::optional<LacrosSelection> lacros_selection_ = absl::nullopt;
// Version of the browser (e.g. lacros-chrome) displayed to user in feedback
// report, etc. It includes both browser version and channel in the format of:
// {browser version} {channel}
// For example, "87.0.0.1 dev", "86.0.4240.38 beta".
std::string browser_version_;
// Called when the binary download completes.
LoadCompleteCallback load_complete_callback_;
// Time when the lacros process was launched.
base::TimeTicks lacros_launch_time_;
// Process handle for the lacros-chrome process.
base::Process lacros_process_;
// ID for the current Crosapi connection.
// Available only when lacros-chrome is running.
absl::optional<CrosapiId> crosapi_id_;
absl::optional<CrosapiId> legacy_crosapi_id_;
// Proxy to BrowserService mojo service in lacros-chrome.
// Available during lacros-chrome is running.
absl::optional<BrowserServiceInfo> browser_service_;
// Remembers the request from Lacros-chrome whether it needs to be
// relaunched. Reset on new process start in any cases.
bool relaunch_requested_ = false;
// 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_;
// Used to pass ash-chrome specific flags/configurations to lacros-chrome.
std::unique_ptr<EnvironmentProvider> environment_provider_;
// The features that are currently registered to keep Lacros alive.
std::set<Feature> keep_alive_features_;
base::ObserverList<BrowserManagerObserver> observers_;
base::WeakPtrFactory<BrowserManager> weak_factory_{this};
};
} // namespace crosapi
#endif // CHROME_BROWSER_ASH_CROSAPI_BROWSER_MANAGER_H_