Commit a999290e authored by Toni Barzic's avatar Toni Barzic Committed by Commit Bot

Remove demo mode resources after sufficient amount of user activity

Have DemoModeResourcesRemover keep track of amount of time the user is
interacting with the device (using UserActivityDetector), and once the
total amount of active usage is over 8 hours, attempt demo mode
resources removal - substantial amount of active usage should be a good
signal that the device has been owned by a real user.

BUG=827368
TEST=unittests

Change-Id: Ib7b90da7da41bdcebbead0f0239579e727f07ae8
Reviewed-on: https://chromium-review.googlesource.com/1180603Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Commit-Queue: Toni Baržić <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584360}
parent 6f360d69
......@@ -14,9 +14,13 @@
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/post_task.h"
#include "base/time/default_tick_clock.h"
#include "base/time/tick_clock.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/idle_detector.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_app_launcher.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
......@@ -27,6 +31,7 @@
#include "components/user_manager/user.h"
#include "components/user_manager/user_type.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/base/user_activity/user_activity_detector.h"
namespace chromeos {
......@@ -38,6 +43,11 @@ DemoModeResourcesRemover* g_instance = nullptr;
// have been removed from the device.
constexpr char kDemoModeResourcesRemoved[] = "demo_mode_resources_removed";
// Key for the pref in local state that tracks accumulated device usage time in
// seconds.
constexpr char kAccumulatedUsagePref[] =
"demo_mode_resources_remover.accumulated_device_usage_s";
// Regex matching legacy demo retail mode domains.
constexpr char kLegacyDemoRetailModeDomainRegex[] =
"[[:alpha:]]{2}-retailmode.com";
......@@ -76,10 +86,24 @@ bool IsLegacyDemoRetailModeSession(const user_manager::User* user) {
} // namespace
DemoModeResourcesRemover::UsageAccumulationConfig::UsageAccumulationConfig()
: resources_removal_threshold(base::TimeDelta::FromHours(48)),
update_interval(base::TimeDelta::FromMinutes(5)),
idle_threshold(base::TimeDelta::FromSeconds(30)) {}
DemoModeResourcesRemover::UsageAccumulationConfig::UsageAccumulationConfig(
const base::TimeDelta& resources_removal_threshold,
const base::TimeDelta& update_interval,
const base::TimeDelta& idle_threshold)
: resources_removal_threshold(resources_removal_threshold),
update_interval(update_interval),
idle_threshold(idle_threshold) {}
// static
void DemoModeResourcesRemover::RegisterLocalStatePrefs(
PrefRegistrySimple* registry) {
registry->RegisterBooleanPref(kDemoModeResourcesRemoved, false);
registry->RegisterIntegerPref(kAccumulatedUsagePref, 0);
}
// static
......@@ -109,6 +133,9 @@ DemoModeResourcesRemover::~DemoModeResourcesRemover() {
CHECK_EQ(g_instance, this);
g_instance = nullptr;
if (usage_start_.has_value() && usage_end_.has_value())
UpdateDeviceUsage(*usage_end_ - *usage_start_);
ChromeUserManager::Get()->RemoveSessionStateObserver(this);
}
......@@ -132,12 +159,53 @@ void DemoModeResourcesRemover::ActiveUserChanged(
// mode domain.
if (g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->IsEnterpriseManaged() &&
!IsLegacyDemoRetailModeSession(user)) {
AttemptRemoval(RemovalReason::kEnterpriseEnrolled, RemovalCallback());
->IsEnterpriseManaged()) {
if (!IsLegacyDemoRetailModeSession(user))
AttemptRemoval(RemovalReason::kEnterpriseEnrolled, RemovalCallback());
return;
}
// Start tracking user activity, if it's already not in progress.
if (!user_activity_observer_.IsObserving(ui::UserActivityDetector::Get())) {
if (!AttemptRemovalIfUsageOverThreshold()) {
user_activity_observer_.Add(ui::UserActivityDetector::Get());
OnUserActivity(nullptr);
}
}
}
void DemoModeResourcesRemover::OnUserActivity(const ui::Event* event) {
base::TimeTicks now = tick_clock_->NowTicks();
if (!usage_start_.has_value()) {
usage_start_ = now;
usage_end_ = now;
return;
}
bool was_idle =
usage_end_.has_value() &&
(now - *usage_end_) > usage_accumulation_config_.idle_threshold;
base::TimeTicks interval_end = was_idle ? *usage_end_ : now;
base::TimeDelta duration = interval_end - *usage_start_;
// If enough time has passed, or the current usage interval was interrupted by
// idle period, record the usage.
if (was_idle || duration >= usage_accumulation_config_.update_interval) {
UpdateDeviceUsage(duration);
// Attempt to remove resources will stop observing user activity, so no need
// to start the next usage interval.
if (AttemptRemovalIfUsageOverThreshold())
return;
// Start tracking the next active usage interval.
usage_start_ = now;
}
usage_end_ = now;
}
void DemoModeResourcesRemover::AttemptRemoval(RemovalReason reason,
RemovalCallback callback) {
if (local_state_->GetBoolean(kDemoModeResourcesRemoved)) {
......@@ -172,9 +240,22 @@ void DemoModeResourcesRemover::AttemptRemoval(RemovalReason reason,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoModeResourcesRemover::OverrideTimeForTesting(
base::TickClock* tick_clock,
const UsageAccumulationConfig& config) {
tick_clock_ = tick_clock;
usage_start_ = base::nullopt;
usage_end_ = base::nullopt;
usage_accumulation_config_ = config;
}
DemoModeResourcesRemover::DemoModeResourcesRemover(PrefService* local_state)
: local_state_(local_state),
tick_clock_(base::DefaultTickClock::GetInstance()),
cryptohome_observer_(this),
user_activity_observer_(this),
weak_ptr_factory_(this) {
CHECK(!g_instance);
g_instance = this;
......@@ -183,14 +264,48 @@ DemoModeResourcesRemover::DemoModeResourcesRemover(PrefService* local_state)
ChromeUserManager::Get()->AddSessionStateObserver(this);
}
void DemoModeResourcesRemover::UpdateDeviceUsage(
const base::TimeDelta& duration) {
int accumulated_activity = local_state_->GetInteger(kAccumulatedUsagePref);
int64_t removal_threshold_s =
usage_accumulation_config_.resources_removal_threshold.InSeconds();
if (accumulated_activity < removal_threshold_s)
accumulated_activity += std::min(duration.InSeconds(), removal_threshold_s);
local_state_->SetInteger(kAccumulatedUsagePref, accumulated_activity);
usage_start_ = base::nullopt;
usage_end_ = base::nullopt;
}
bool DemoModeResourcesRemover::AttemptRemovalIfUsageOverThreshold() {
int accumulated_activity = local_state_->GetInteger(kAccumulatedUsagePref);
int64_t removal_threshold_s =
usage_accumulation_config_.resources_removal_threshold.InSeconds();
if (accumulated_activity < removal_threshold_s)
return false;
// Stop observing usage.
user_activity_observer_.RemoveAll();
AttemptRemoval(RemovalReason::kRegularUsage, RemovalCallback());
return true;
}
void DemoModeResourcesRemover::OnRemovalDone(RemovalResult result) {
DCHECK(removal_in_progress_);
removal_in_progress_ = false;
if (result == RemovalResult::kNotFound || result == RemovalResult::kSuccess) {
local_state_->SetBoolean(kDemoModeResourcesRemoved, true);
local_state_->ClearPref(kAccumulatedUsagePref);
cryptohome_observer_.RemoveAll();
ChromeUserManager::Get()->RemoveSessionStateObserver(this);
user_activity_observer_.RemoveAll();
usage_start_ = base::nullopt;
usage_end_ = base::nullopt;
}
UMA_HISTOGRAM_ENUMERATION("DemoMode.ResourcesRemoval.Result", result);
......
......@@ -12,13 +12,26 @@
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "components/user_manager/user_manager.h"
#include "ui/base/user_activity/user_activity_observer.h"
class PrefRegistrySimple;
class PrefService;
namespace base {
class TickClock;
class TimeDelta;
class TimeTicks;
} // namespace base
namespace ui {
class Event;
class UserActivityDetector;
} // namespace ui
namespace chromeos {
// Handles removal of pre-installed demo mode resources.
......@@ -31,11 +44,11 @@ namespace chromeos {
// of the following conditions are satisfied:
// * device is running low on disk space
// * device is enrolled in a non-demo-mode domain
// * TODO(crbug.com/827368): enough non-demo-mode user activity has been
// detected on the device
// * enough user activity has been detected on the device
class DemoModeResourcesRemover
: public CryptohomeClient::Observer,
public user_manager::UserManager::UserSessionStateObserver {
public user_manager::UserManager::UserSessionStateObserver,
public ui::UserActivityObserver {
public:
// The reason a removal was requested.
// DO NOT REORDER - used to report metrics.
......@@ -73,6 +86,30 @@ class DemoModeResourcesRemover
// partition.
using RemovalCallback = base::OnceCallback<void(RemovalResult result)>;
// Configures how DemoModeResourcesRemover tracks the amount of active
// device usage in order to determine when demo mode resources are not needed
// anymore.
struct UsageAccumulationConfig {
// Creates the config with default params used in production.
UsageAccumulationConfig();
UsageAccumulationConfig(const base::TimeDelta& resources_removal_threshold,
const base::TimeDelta& update_interval,
const base::TimeDelta& idle_threshold);
// Amount of accumulated device usage time that warrants demo mode resources
// removal. When this threshold is reached, demo mode resources removal will
// be attempted.
base::TimeDelta resources_removal_threshold;
// The interval in which accumulated usage time is updated in prefs (during
// active device usage).
base::TimeDelta update_interval;
// The amount of time without user activity after which the device is
// considered idle.
base::TimeDelta idle_threshold;
};
static void RegisterLocalStatePrefs(PrefRegistrySimple* registry);
// Gets the demo mode resources remover instance for this process - at most
......@@ -104,15 +141,37 @@ class DemoModeResourcesRemover
// user_manager::UserManager::UserSessionStateObserver:
void ActiveUserChanged(const user_manager::User* user) override;
// ui::UserActivityObserver:
void OnUserActivity(const ui::Event* event) override;
// Requests demo mode resources removal from the disk. If a removal operation
// is already in progress, this method will schedule the callback to be run
// with the result of the operation in progress.
void AttemptRemoval(RemovalReason reason, RemovalCallback callback);
// Allows tests to override the tick clock and configuration for accumulating
// the amount of active device usage.
void OverrideTimeForTesting(base::TickClock* tick_clock,
const UsageAccumulationConfig& config);
private:
// Defined here so it can be overridden in tests.
UsageAccumulationConfig usage_accumulation_config_;
// Use CreateIfNeeded() to create an instance.
explicit DemoModeResourcesRemover(PrefService* local_state);
// Updates the accumulated information about the amount of active device
// usage, which is used to detect when the device owned by a real user, and
// thus does not require demo mode resources.
void UpdateDeviceUsage(const base::TimeDelta& duration);
// If the amount of detected device usage is above the threshold for removing
// demo mode resources, attempts the resources removal. If resoruces removal
// is requested, stops observing device usage.
// Returns whether the resources removal was requested.
bool AttemptRemovalIfUsageOverThreshold();
// Passes as the callback to directory removal file operations.
void OnRemovalDone(RemovalResult result);
......@@ -124,8 +183,16 @@ class DemoModeResourcesRemover
// Callbacks for the resources removal operation, if one is in progress.
std::vector<RemovalCallback> removal_callbacks_;
const base::TickClock* tick_clock_;
// Used to track the duration of last unrecorded interval of user activity.
base::Optional<base::TimeTicks> usage_start_;
base::Optional<base::TimeTicks> usage_end_;
ScopedObserver<CryptohomeClient, DemoModeResourcesRemover>
cryptohome_observer_;
ScopedObserver<ui::UserActivityDetector, DemoModeResourcesRemover>
user_activity_observer_;
base::WeakPtrFactory<DemoModeResourcesRemover> weak_ptr_factory_;
......
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