// Copyright 2021 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_METRICS_PER_USER_STATE_MANAGER_CHROMEOS_H_
#define CHROME_BROWSER_METRICS_PER_USER_STATE_MANAGER_CHROMEOS_H_
#include <optional>
#include <string>
#include "base/callback_list.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/settings/device_settings_service.h"
#include "chrome/browser/metrics/profile_pref_names.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/login/session/session_termination_manager.h"
#include "components/account_id/account_id.h"
#include "components/metrics/metrics_log_store.h"
#include "components/metrics/metrics_provider.h"
#include "components/metrics/metrics_service.h"
#include "components/metrics_services_manager/metrics_services_manager.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
namespace metrics {
// State manager for per-user metrics collection. Listens to user
// creation/deletion and manages user prefs accordingly.
//
// When a particular user is created (login), all metrics collected so far
// will be flushed to local state. Before a user is destroyed (logout), all
// metrics will be flushed to the user dir.
//
// It is assumed that there can only be at most one user logged in at once. This
// assumption is only true in Ash Chrome.
//
// This class integrates into the MetricsService in order to separately handle
// UMA reporting consent for unmanaged secondary users. Profile prefs are
// used to handle the consent for each user. These profile settings interact
// with the local state pref that controls the overall device reporting
// consent, and UMA uploading logic.
//
// Ownership status needs to be asynchronously retrieved first in order to know
// whether the device has no ownership yet, or whether the device is owned and
// we are controlling consent for a secondary user.
//
// This class does not manage the device owner reporting consent.
// Device owner consent is handled separately by
// |ash::StatsReportingController|. In the future, we may want to consider
// simplifying the code by using a single class to manage both device owner
// consent and secondary user consent.
class PerUserStateManagerChromeOS
: public user_manager::UserManager::UserSessionStateObserver,
public user_manager::UserManager::Observer,
public ash::SessionTerminationManager::Observer {
public:
// Callback to handle changes in user metrics consent.
using MetricsConsentHandler = base::RepeatingCallback<void(bool)>;
// Does not own params passed by pointer. Caller should ensure that the
// lifetimes of the weak pointers exceed that of |this|.
PerUserStateManagerChromeOS(
MetricsServiceClient* metrics_service_client,
user_manager::UserManager* user_manager,
PrefService* local_state,
const MetricsLogStore::StorageLimits& storage_limits,
const std::string& signing_key);
// Does not own |metrics_service_client| and |local_state|. Lifetime of
// these raw pointers should be handled by the caller.
PerUserStateManagerChromeOS(MetricsServiceClient* metrics_service_client,
PrefService* local_state);
PerUserStateManagerChromeOS(const PerUserStateManagerChromeOS& other) =
delete;
PerUserStateManagerChromeOS& operator=(
const PerUserStateManagerChromeOS& other) = delete;
~PerUserStateManagerChromeOS() override;
static void RegisterPrefs(PrefRegistrySimple* registry);
static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
// Returns the user_id of the current logged in user. If no user is logged in,
// returns std::nullopt. If a user has logged in and has opted-out, will
// return std::nullopt.
//
// If the user has opted-into metrics collection and is not ephemeral, then
// this will return the pseudo-anonymous identifier associated with the user.
std::optional<std::string> GetCurrentUserId() const;
// Returns the consent of the current logged in user only if current user's
// consent should be applied to metrics reporting.
//
// The cases in which this occurs are:
//
// 1) Regular non-owner users on non-managed devices.
// 2) Guest users.
//
// If no user is logged in, returns std::nullopt. True means that the user
// has opted-into metrics collection during the session and False means that
// the user has opted-out.
std::optional<bool> GetCurrentUserReportingConsentIfApplicable() const;
// Sets the metric consent for the current logged in user. If no user is
// logged in, no-ops.
//
// This method will reset the client id if a user toggles from a non-consent
// to consent state AND the user had consented to metrics collection in the
// past. This is to preserve the pseudo-anonymity of <user_id, client_id>
// identifier.
//
// This call should be used to toggle consent from the UI or during OOBE flow
// for the current user.
void SetCurrentUserMetricsConsent(bool metrics_consent);
// Returns true if |user| should have the ability to toggle user metrics
// collection for themselves.
//
// This will return false for managed device users as well as guest users.
bool IsUserAllowedToChangeConsent(user_manager::User* user) const;
// Adds an observer |callback| to be called when a user consent should be
// applied. This happens either when an applicable user logs in or an
// applicable user changes metrics consent.
base::CallbackListSubscription AddObserver(
const MetricsConsentHandler& callback);
// Sets behavior of IsReportingPolicyManaged() for testing.
//
// TODO(crbug/1269950): Investigate why ash::LoginManagerTest does not work
// with ash::ScopedStubInstallAttributes. Remove this function once resolved
// as it is hack to force PerUserStateManagerChromeOS to return a fixed value.
static void SetIsManagedForTesting(bool is_managed);
// Resets the logged in user state for testing.
void ResetStateForTesting();
protected:
// These methods are marked virtual to stub out for testing.
// Sets the user log store to use |log_store|. Default uses
// |metrics_service_client_| implementation.
virtual void SetUserLogStore(std::unique_ptr<UnsentLogStore> log_store);
// Unsets the user log store. Default uses |metrics_service_client_|
// implementation.
virtual void UnsetUserLogStore();
// Resets the client ID. Should be called when user consent is turned off->on
// and the user has opted-in metrics consent in the past. Default uses
// |metrics_service_client_| implementation.
virtual void ForceClientIdReset();
// Returns true if the reporting policy is managed.
virtual bool IsReportingPolicyManaged() const;
// Returns true if user log store has been set to be used to persist metric
// logs.
virtual bool HasUserLogStore() const;
// Returns true if the device is owned either by a policy or a local owner.
//
// Does not guarantee that the ownership status is known and will return false
// if the status is unknown.
//
// See //chrome/browser/ash/settings/device_settings_service.h for more
// details as to when a device is considered owned and how a device becomes
// owned.
virtual bool IsDeviceOwned() const;
// Returns true if the device status is known.
virtual bool IsDeviceStatusKnown() const;
// These methods are protected to avoid dependency on DeviceSettingsService
// during testing.
// Ensures that ownership status is known before proceeding with using
// profile prefs.
virtual void WaitForOwnershipStatus();
// Returns true if a user log store in the user cryptohome should be used for
// the current logged in user.
// Certain users (ie demo mode sessions with metrics consent on) should not
// use a user log store since the user log store will be stored on the
// temporary cryptohome and will be deleted at the end of the session.
// Demo mode sessions with metric consent on should be stored in local state
// to be persistent.
bool ShouldUseUserLogStore() const;
// Loads appropriate prefs from |current_user_| and creates new log storage
// using profile prefs.
//
// Will only be called when OwnershipStatus is known. This guarantees that
// we avoid race conditions where the ownership status is still unknown due
// to policy fetch on browser restart.
// The status will either be kOwnershipNone, or kOwnershipTaken.
void InitializeProfileMetricsState(
ash::DeviceSettingsService::OwnershipStatus status);
private:
// Possible states for |this|.
enum class State {
// Right after construction.
CONSTRUCTED = 0,
// ActiveUserChanged on non-trivial user. The profile for the user is not
// immediately created.
USER_LOGIN = 1,
// User profile has been created and ready to use.
USER_PROFILE_READY = 2,
// User log store has been initialized, if applicable. Per-user consent
// now can be changed if the user has permissions to change consent.
//
// Terminal state.
USER_LOG_STORE_HANDLED = 3,
};
// UserManager::UserSessionStateObserver:
void ActiveUserChanged(user_manager::User* active_user) override;
// UserManager::Observer:
void OnUserToBeRemoved(const AccountId& account_id) override;
// ash::SessionTerminationManager::Observer:
void OnSessionWillBeTerminated() override;
// Updates the current user ID to |new_user_id|. Updates both the profile pref
// as well as local state pref.
void UpdateCurrentUserId(const std::string& new_user_id);
// Resets the state of |this| to that of no logged in user.
void ResetState();
// Returns the prefs for the current logged in user.
PrefService* GetCurrentUserPrefs() const;
// Builds a unsent log store for |current_user_| and assigns it to be used as
// the primary log store for ongoing logs.
void AssignUserLogStore();
// Sets the reporting state for metrics collection. Notifies observers that
// user metrics consent has changed to |metrics_consent|.
void SetReportingState(bool metrics_consent);
// Notifies observers of the per-user state change |metrics_consent|.
void NotifyObservers(bool metrics_consent);
// Updates local state prefs based on |metrics_enabled|. If |metrics_enabled|
// is true,
//
// 1) Client ID will be reset if the user has ever had metrics reporting
// enabled. This is to preserve the pseudo-anonymous identifier
// <client_id, user_id>.
void UpdateLocalStatePrefs(bool metrics_enabled);
SEQUENCE_CHECKER(sequence_checker_);
base::RepeatingCallbackList<void(bool)> callback_list_;
// Raw pointer to Metrics service client that should own |this|.
const raw_ptr<MetricsServiceClient> metrics_service_client_;
// Raw pointer to user manager. User manager is used to listen to login/logout
// events as well as retrieve metadata about users. |user_manager_| should
// outlive |this|.
const raw_ptr<user_manager::UserManager> user_manager_;
// Raw pointer to local state prefs store.
const raw_ptr<PrefService> local_state_;
// Logs parameters that control log storage requirements and restrictions.
const MetricsLogStore::StorageLimits storage_limits_;
// Signing key to encrypt logs.
const std::string signing_key_;
// Pointer to the current logged-in user.
raw_ptr<user_manager::User> current_user_ = nullptr;
// Current state for |this|.
State state_ = State::CONSTRUCTED;
// Task runner. Used to persist state to daemon-store.
scoped_refptr<base::SequencedTaskRunner> task_runner_ = nullptr;
base::WeakPtrFactory<PerUserStateManagerChromeOS> weak_ptr_factory_{this};
};
} // namespace metrics
#endif // CHROME_BROWSER_METRICS_PER_USER_STATE_MANAGER_CHROMEOS_H_