blob: 13027a5d8342c6be5942316d33615324c3c66f7f [file] [log] [blame]
// Copyright 2016 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_ARC_SESSION_ARC_SESSION_MANAGER_H_
#define CHROME_BROWSER_ASH_ARC_SESSION_ARC_SESSION_MANAGER_H_
#include <memory>
#include <ostream>
#include <string>
#include <utility>
#include "ash/components/arc/session/arc_session_runner.h"
#include "ash/components/arc/session/arc_stop_reason.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/arc/arc_support_host.h"
#include "chrome/browser/ash/arc/policy/arc_android_management_checker.h"
#include "chrome/browser/ash/arc/session/adb_sideloading_availability_delegate_impl.h"
#include "chrome/browser/ash/arc/session/arc_activation_necessity_checker.h"
#include "chrome/browser/ash/arc/session/arc_app_id_provider_impl.h"
#include "chrome/browser/ash/arc/session/arc_requirement_checker.h"
#include "chrome/browser/ash/arc/session/arc_session_manager_observer.h"
#include "chrome/browser/ash/guest_os/public/guest_os_mount_provider_registry.h"
#include "chrome/browser/ash/policy/arc/android_management_client.h"
#include "chromeos/ash/components/dbus/concierge/concierge_client.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/abseil-cpp/absl/types/variant.h"
class ArcAppLauncher;
class Profile;
namespace arc {
// The file exists only when ARC container is in use.
constexpr const char kGeneratedBuildPropertyFilePath[] =
"/run/arc/host_generated/build.prop";
// The file exists only when ARCVM is in use.
constexpr const char kGeneratedCombinedPropertyFilePathVm[] =
"/run/arcvm/host_generated/combined.prop";
class ArcDataRemover;
class ArcDlcInstaller;
class ArcFastAppReinstallStarter;
class ArcPaiStarter;
class ArcProvisioningResult;
class ArcUiAvailabilityReporter;
enum class ProvisioningStatus;
enum class ArcStopReason;
// This class is responsible for handing stages of ARC life-cycle.
class ArcSessionManager : public ArcSessionRunner::Observer,
public ArcSupportHost::ErrorDelegate,
public ash::SessionManagerClient::Observer,
public ash::ConciergeClient::VmObserver,
public ArcRequirementChecker::Observer {
public:
// Represents each State of ARC session.
// NOT_INITIALIZED: represents the state that the Profile is not yet ready
// so that this service is not yet initialized, or Chrome is being shut
// down so that this is destroyed.
// STOPPED: ARC session is not running, or being terminated.
// CHECKING_REQUIREMENTS: Checking requirements before starting ARC.
// First, negotiates Google Play Store "Terms of Service" with a user. There
// are several ways for the negotiation, including opt-in flow, which shows
// "Terms of Service" page on ARC support app, and OOBE flow, which shows
// "Terms of Service" page as a part of Chrome OOBE flow. If user does not
// accept the Terms of Service, disables Google Play Store, which triggers
// RequestDisable() and the state will be set to STOPPED, then.
// Second, checks Android management status. Note that the status is checked
// for each ARC session starting, but this is the state only for the first
// boot case (= opt-in case). The second time and later the management check
// is running in parallel with ARC session starting, and in such a case,
// State is ACTIVE, instead.
// REMOVING_DATA_DIR: When ARC is disabled, the data directory is removed.
// While removing is processed, ARC cannot be started. This is the state.
// READY: ARC is ready to run, but not running yet. This state is skipped on
// the first boot case.
// ACTIVE: ARC is running.
// STOPPING: ARC is being shut down.
//
// State transition should be as follows:
//
// NOT_INITIALIZED -> STOPPED: when the primary Profile gets ready.
// ...(any)... -> NOT_INITIALIZED: when the Chrome is being shutdown.
// ...(any)... -> STOPPED: on error.
//
// In the first boot case:
// STOPPED -> CHECKING_REQUIREMENTS: On request to enable.
// CHECKING_REQUIREMENTS -> ACTIVE: when a user accepts "Terms Of Service"
// and the auth token is successfully fetched.
//
// In the second (or later) boot case:
// STOPPED -> READY: when arc.enabled preference is checked that it is true.
// Practically, this is when the primary Profile gets ready.
// READY -> ACTIVE: when activation is allowed.
//
// In the disabling case:
// CHECKING_REQUIREMENTS -> STOPPED
// READY -> STOPPED
// ACTIVE -> STOPPING -> (maybe REMOVING_DATA_DIR ->) STOPPED
// STOPPING: Eventually change the state to STOPPED. Do nothing
// immediately.
// REMOVING_DATA_DIR: Eventually state will become STOPPED. Do nothing
// immediately.
//
// TODO(hidehiko): Fix the state machine, and update the comment including
// relationship with |enable_requested_|.
enum class State {
NOT_INITIALIZED,
STOPPED,
CHECKING_REQUIREMENTS,
REMOVING_DATA_DIR,
READY,
ACTIVE,
STOPPING,
};
using ExpansionResult = std::pair<std::string /* salt on disk */,
bool /* expansion successful */>;
ArcSessionManager(std::unique_ptr<ArcSessionRunner> arc_session_runner,
std::unique_ptr<AdbSideloadingAvailabilityDelegateImpl>
adb_sideloading_availability_delegate);
ArcSessionManager(const ArcSessionManager&) = delete;
ArcSessionManager& operator=(const ArcSessionManager&) = delete;
~ArcSessionManager() override;
static ArcSessionManager* Get();
static void SetUiEnabledForTesting(bool enabled);
static void SetArcTermsOfServiceOobeNegotiatorEnabledForTesting(bool enabled);
static void EnableCheckAndroidManagementForTesting(bool enable);
// Returns true if ARC is allowed to run for the current session.
// TODO(hidehiko): The name is very close to IsArcAllowedForProfile(), but
// has different meaning. Clean this up.
bool IsAllowed() const;
// Start expanding the property files. Note that these property files are
// needed to start the mini instance. This function also tries to read
// /var/lib/misc/arc_salt when ARCVM is enabled.
void ExpandPropertyFilesAndReadSalt();
// Initializes ArcSessionManager. Before this runs, Profile must be set
// via SetProfile().
void Initialize();
// Set the device scale factor used to start the arc. This must be called
// before staring mini-ARC.
void SetDefaultDeviceScaleFactor(float default_device_scale_factor);
void Shutdown();
// Sets the |profile|, and sets up Profile related fields in this instance.
// IsArcAllowedForProfile() must return true for the given |profile|.
void SetProfile(Profile* profile);
Profile* profile() { return profile_; }
const Profile* profile() const { return profile_; }
State state() const { return state_; }
// Adds or removes observers.
void AddObserver(ArcSessionManagerObserver* observer);
void RemoveObserver(ArcSessionManagerObserver* observer);
// Notifies observers that Google Play Store enabled preference is changed.
// Note: ArcPlayStoreEnabledPreferenceHandler has the main responsibility to
// notify the event. However, due to life time, it is difficult for non-ARC
// services to subscribe the handler instance directly. Instead, they can
// subscribe to ArcSessionManager, and ArcSessionManager proxies the event.
void NotifyArcPlayStoreEnabledChanged(bool enabled);
// Called from ARC support platform app when user cancels signing.
void CancelAuthCode();
// Requests to enable ARC session. This starts ARC instance, or maybe starts
// Terms Of Service negotiation if they haven't been accepted yet.
// If it is already requested to enable, no-op.
// Currently, enabled/disabled is tied to whether Google Play Store is
// enabled or disabled. Please see also TODO of
// SetArcPlayStoreEnabledForProfile().
void RequestEnable();
// Allows changing the state from READY to ACTIVE. If the state is already
// READY, calling this method changes the state to ACTIVE.
void AllowActivation();
// Requests to disable ARC session. This stops ARC instance, or quits Terms
// Of Service negotiation if it is the middle of the process (e.g. closing
// UI for manual negotiation if it is shown). This does not remove user ARC
// data.
// If it is already requested to disable, no-op.
void RequestDisable();
// Requests to disable ARC session and remove ARC data.
// If it is already requested to disable, no-op.
void RequestDisableWithArcDataRemoval();
// Requests to remove the ARC data.
// If ARC is stopped, triggers to remove the data. Otherwise, queues to
// remove the data after ARC stops.
// A log statement with the removal reason must be added prior to calling
// this.
void RequestArcDataRemoval();
// Stops ARC instance without removing user ARC data.
// Unlike RequestDisable(), this doesn't clear user ARC prefs, and ARC is not
// supposed to restart within the same user session.
// NOTE: This method should be used only for the purpose of stopping ARC
// under low disk space.
// TODO(b/236325019): Remove this once ArcSessionManager officially supports
// a method to stop ARC without clearing user ARC prefs, or when we
// remove ArcDiskSpaceMonitor after Storage Balloon is ready.
void RequestStopOnLowDiskSpace();
// ArcSupportHost:::ErrorDelegate:
void OnWindowClosed() override;
void OnRetryClicked() override;
void OnSendFeedbackClicked() override;
void OnRunNetworkTestsClicked() override;
// StopArc(), then restart. Between them data clear may happens.
// This is a special method to support enterprise device lost case.
// This can be called only when ARC is running.
void StopAndEnableArc();
ArcSupportHost* support_host() { return support_host_.get(); }
// On provisioning completion (regardless of whether successfully done or
// not), this is called with its status. On success, is_success() of
// |result| returns true, otherwise ArcSignInResult can be retrieved from
// get() if sign-in result came from ARC or stop_reason()
// will indicate that ARC stopped prematurely and provisioning could
// not finish successfully. is_timedout() indicates that operation timed
// out.
void OnProvisioningFinished(const ArcProvisioningResult& result);
// A helper function that calls ArcSessionRunner's SetUserInfo.
void SetUserInfo();
// Trims VM's memory by moving it to zram.
// When the operation is done |callback| is called.
// If nonzero, |page_limit| defines the max number of pages to reclaim.
using TrimVmMemoryCallback = ArcSessionRunner::TrimVmMemoryCallback;
void TrimVmMemory(TrimVmMemoryCallback callback, int page_limit);
// Returns the time when ARC was pre-started (mini-ARC start), or a null time
// if ARC has not been pre-started yet.
base::TimeTicks pre_start_time() const { return pre_start_time_; }
// Returns the time when ARC was about to start, or a null time if ARC has
// not been started yet.
base::TimeTicks start_time() const { return start_time_; }
// Returns the time when the sign in process started, or a null time if
// signing in didn't happen during this session.
base::TimeTicks sign_in_start_time() const { return sign_in_start_time_; }
// Returns true if ARC requested to start.
bool enable_requested() const { return enable_requested_; }
// Returns PAI starter that is used to start Play Auto Install flow. It is
// available only on initial start.
ArcPaiStarter* pai_starter() { return pai_starter_.get(); }
// Returns Fast App Reinstall starter that is used to start Play Fast App
// Reinstall flow. It is available only on initial start.
ArcFastAppReinstallStarter* fast_app_resintall_starter() {
return fast_app_reinstall_starter_.get();
}
// Returns true if the current ARC run has started with skipping user ToS
// negotiation, because the user had accepted already or policy does not
// require ToS acceptance. Returns false in other cases, including one when
// ARC is not currently running.
bool skipped_terms_of_service_negotiation() const {
return skipped_terms_of_service_negotiation_;
}
void set_skipped_terms_of_service_negotiation_for_testing(
bool skipped_terms_of_service_negotiation) {
skipped_terms_of_service_negotiation_ =
skipped_terms_of_service_negotiation;
}
// Injectors for testing.
void SetArcSessionRunnerForTesting(
std::unique_ptr<ArcSessionRunner> arc_session_runner);
ArcSessionRunner* GetArcSessionRunnerForTesting();
void SetAttemptUserExitCallbackForTesting(
const base::RepeatingClosure& callback);
void SetAndroidManagementCheckerFactoryForTesting(
ArcRequirementChecker::AndroidManagementCheckerFactory
android_management_checker_factory) {
android_management_checker_factory_ = android_management_checker_factory;
}
// Returns whether the Play Store app is requested to be launched by this
// class. Should be used only for tests.
bool IsPlaystoreLaunchRequestedForTesting() const;
// Invoking StartArc() only for testing, e.g., to emulate accepting Terms of
// Service then passing Android management check successfully.
void StartArcForTesting();
// Invokes functions as if requirement checks are completed for testing.
void EmulateRequirementCheckCompletionForTesting() {
DCHECK(requirement_checker_);
requirement_checker_->EmulateRequirementCheckCompletionForTesting();
}
// Invokes OnExpandPropertyFilesAndReadSalt as if the expansion is done.
void OnExpandPropertyFilesAndReadSaltForTesting(bool result) {
OnExpandPropertyFilesAndReadSalt(ExpansionResult{{}, result});
}
void reset_property_files_expansion_result() {
property_files_expansion_result_.reset();
}
// ash::ConciergeClient::VmObserver overrides.
void OnVmStarted(
const vm_tools::concierge::VmStartedSignal& vm_signal) override;
void OnVmStopped(
const vm_tools::concierge::VmStoppedSignal& vm_signal) override;
// Getter for |vm_info_|.
// If ARCVM is not running, return absl::nullopt.
const absl::optional<vm_tools::concierge::VmInfo>& GetVmInfo() const;
// Getter for |serialno|.
std::string GetSerialNumber() const;
// Stops mini-ARC instance. This should only be called before login.
void StopMiniArcIfNecessary();
private:
// Reports statuses of OptIn flow to UMA.
class ScopedOptInFlowTracker;
// Requests to disable ARC session and allows to optionally remove ARC data.
// If ARC is already disabled, no-op.
void RequestDisable(bool remove_arc_data);
// RequestEnable() has a check in order not to trigger starting procedure
// twice. This method can be called to bypass that check when restarting.
// Returns true if ARC is started directly.
bool RequestEnableImpl();
// Called when activation necessity check is done.
void OnActivationNecessityChecked(bool result);
// Negotiates the terms of service to user, if necessary.
// Otherwise, move to StartAndroidManagementCheck().
void MaybeStartTermsOfServiceNegotiation();
// ArcRequirementChecker::Observer override:
void OnArcOptInManagementCheckStarted() override;
// Called when requirement checks are done.
void OnRequirementChecksDone(
ArcRequirementChecker::RequirementCheckResult result);
void ShutdownSession();
void ResetArcState();
void OnArcSignInTimeout();
// Starts requirement checks in background (in parallel with starting
// ARC). This is for secondary or later ARC enabling.
// The reason running them in parallel is for performance. The secondary or
// later ARC enabling is typically on "logging into Chrome" for the user who
// already opted in to use Google Play Store. In such a case, network is
// typically not yet ready. Thus, if we block ARC boot, it delays several
// seconds, which is not very user friendly.
void StartBackgroundRequirementChecks();
// Called when the background requirement checks are done.
void OnBackgroundRequirementChecksDone(
ArcRequirementChecker::BackgroundCheckResult result);
// Requests to start ARC instance. Also, updates the internal state to
// ACTIVE.
void StartArc();
// Calls StartArc() and starts background requirement checks.
void StartArcForRegularBoot();
// Requests to stop ARC instance. This resets two persistent flags:
// kArcSignedIn and kArcTermsAccepted, so that, in next enabling,
// it is started from Terms of Service negotiation.
// TODO(hidehiko): Introduce STOPPING state, and this function should
// transition to it.
void StopArc();
// ArcSessionRunner::Observer:
void OnSessionStopped(ArcStopReason reason, bool restarting) override;
void OnSessionRestarting() override;
// Starts to remove ARC data, if it is requested via RequestArcDataRemoval().
// On completion, OnArcDataRemoved() is called.
// If not requested, just skipping the data removal, and moves to
// MaybeReenableArc() directly.
void MaybeStartArcDataRemoval();
void OnArcDataRemoved(absl::optional<bool> success);
// On ARC session stopped and/or data removal completion, this is called
// so that, if necessary, ARC session is restarted.
// TODO(hidehiko): This can be removed after the racy state machine
// is fixed.
void MaybeReenableArc();
// Starts a timer to check if provisioning takes too long.
// The timer will not be set if this device was previously provisioned
// successfully.
void MaybeStartTimer();
// Starts mini-ARC and updates related information.
void StartMiniArc();
// Requests the support host (if it exists) to show the error, and notifies
// the observers.
void ShowArcSupportHostError(ArcSupportHost::ErrorInfo error_info,
bool should_show_send_feedback,
bool should_show_run_network_tests);
// ash::SessionManagerClient::Observer:
void EmitLoginPromptVisibleCalled() override;
// Called when the first part of ExpandPropertyFilesAndReadSalt is done.
void OnExpandPropertyFiles(bool result);
// Called when ExpandPropertyFilesAndReadSalt is done.
void OnExpandPropertyFilesAndReadSalt(ExpansionResult result);
std::unique_ptr<ArcSessionRunner> arc_session_runner_;
std::unique_ptr<AdbSideloadingAvailabilityDelegateImpl>
adb_sideloading_availability_delegate_;
// Unowned pointer. Keeps current profile.
Profile* profile_ = nullptr;
// Whether ArcSessionManager is requested to enable (starting to run ARC
// instance) or not.
bool enable_requested_ = false;
// Internal state machine. See also State enum class.
State state_ = State::NOT_INITIALIZED;
base::ObserverList<ArcSessionManagerObserver>::Unchecked observer_list_;
std::unique_ptr<ArcAppLauncher> playstore_launcher_;
bool reenable_arc_ = false;
bool provisioning_reported_ = false;
bool skipped_terms_of_service_negotiation_ = false;
bool activation_is_allowed_ = false;
base::OneShotTimer arc_sign_in_timer_;
std::unique_ptr<ArcSupportHost> support_host_;
std::unique_ptr<ArcDataRemover> data_remover_;
ArcRequirementChecker::AndroidManagementCheckerFactory
android_management_checker_factory_;
std::unique_ptr<ArcRequirementChecker> requirement_checker_;
std::unique_ptr<ArcActivationNecessityChecker> activation_necessity_checker_;
std::unique_ptr<ScopedOptInFlowTracker> scoped_opt_in_tracker_;
std::unique_ptr<ArcPaiStarter> pai_starter_;
std::unique_ptr<ArcFastAppReinstallStarter> fast_app_reinstall_starter_;
std::unique_ptr<ArcUiAvailabilityReporter> arc_ui_availability_reporter_;
// The time when the sign in process started.
base::TimeTicks sign_in_start_time_;
// The time when ARC was pre-started (mini-ARC start).
base::TimeTicks pre_start_time_;
// The time when ARC was about to start.
base::TimeTicks start_time_;
// Used to measure the activation delay.
std::unique_ptr<base::ElapsedTimer> activation_delay_elapsed_timer_;
base::RepeatingClosure attempt_user_exit_callback_;
ArcAppIdProviderImpl app_id_provider_;
// The content of /var/lib/misc/arc_salt. Empty if the file doesn't exist.
absl::optional<std::string> arc_salt_on_disk_;
absl::optional<bool> property_files_expansion_result_;
absl::optional<vm_tools::concierge::VmInfo> vm_info_;
std::unique_ptr<ArcDlcInstaller> arc_dlc_installer_;
absl::optional<guest_os::GuestOsMountProviderRegistry::Id>
arcvm_mount_provider_id_;
// Must be the last member.
base::WeakPtrFactory<ArcSessionManager> weak_ptr_factory_{this};
};
// Outputs the stringified |state| to |os|. This is only for logging purposes.
std::ostream& operator<<(std::ostream& os,
const ArcSessionManager::State& state);
} // namespace arc
#endif // CHROME_BROWSER_ASH_ARC_SESSION_ARC_SESSION_MANAGER_H_