0

Reland "demomode: Delete user created files between shopper sessions"

This is a reland of commit 7166891c62
Fixed MSAN tests.

Original change's description:
> demomode: Delete user created files between shopper sessions
>
> Delete all files under 'MyFiles' and reset demo media data when device
> is idle, which is the end of one shopper session.
>
> Bug: 396731490
> Change-Id: Ic9a2f69f33261661dc771a980f036f14580cf1b0
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6292333
> Reviewed-by: Li Lin <llin@chromium.org>
> Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
> Commit-Queue: Xiqi Ruan <xiqiruan@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1433918}

Bug: 396731490
Change-Id: I4ffbb58603780b538a2f0b2e9db1083be623775a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6367236
Reviewed-by: Hidehiko Abe <hidehiko@chromium.org>
Commit-Queue: Xiqi Ruan <xiqiruan@chromium.org>
Reviewed-by: Li Lin <llin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1437951}
This commit is contained in:
Xiqi Ruan
2025-03-25 23:16:28 -07:00
committed by Chromium LUCI CQ
parent ec228c06d4
commit dcf0508732
12 changed files with 292 additions and 26 deletions

@ -577,6 +577,12 @@ BASE_FEATURE(kDemoModeWallpaperUpdate,
"DemoModeWallpaperUpdate",
base::FEATURE_DISABLED_BY_DEFAULT);
// Controls whether clean up local files between shopper session when demo mode
// sign in is enable. No-op if demo mode sign in is disabled.
BASE_FEATURE(kDemoModeSignInFileCleanup,
"DemoModeSignInFileCleanup",
base::FEATURE_DISABLED_BY_DEFAULT);
// Toggle different display features based on user setting and power state
BASE_FEATURE(kDisplayPerformanceMode,
"DisplayPerformanceMode",
@ -3517,6 +3523,10 @@ bool IsDemoModeWallpaperUpdateEnabled() {
return base::FeatureList::IsEnabled(kDemoModeWallpaperUpdate);
}
bool IsDemoModeSignInFileCleanupEnabled() {
return base::FeatureList::IsEnabled(kDemoModeSignInFileCleanup);
}
bool IsDeskTemplateSyncEnabled() {
return base::FeatureList::IsEnabled(kDeskTemplateSync);
}

@ -178,6 +178,8 @@ COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kDemoModeSignIn);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kDemoModeWallpaperUpdate);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kDemoModeSignInFileCleanup);
COMPONENT_EXPORT(ASH_CONSTANTS) BASE_DECLARE_FEATURE(kDeskTemplateSync);
COMPONENT_EXPORT(ASH_CONSTANTS)
BASE_DECLARE_FEATURE(kDeviceActiveClient28DayActiveCheckMembership);
@ -1093,6 +1095,7 @@ COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeepLinkingEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeAppLandscapeLockedEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeSignInEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeWallpaperUpdateEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDemoModeSignInFileCleanupEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDeskTemplateSyncEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsDisplayPerformanceModeEnabled();
COMPONENT_EXPORT(ASH_CONSTANTS) bool IsInputDeviceSettingsSplitEnabled();

@ -50,6 +50,7 @@ static_library("demo_mode") {
"//chrome/browser/ash/policy/core",
"//chrome/browser/ash/policy/enrollment",
"//chrome/browser/ash/profiles",
"//chrome/browser/chromeos/extensions/login_screen/login/cleanup",
"//chrome/browser/ui:browser_list",
"//chrome/common",
"//chromeos/ash/components/dbus",

@ -29,6 +29,7 @@ include_rules = [
"+chrome/browser/browser_process_platform_part.h",
"+chrome/browser/chrome_browser_main_extra_parts.h",
"+chrome/browser/chrome_browser_main.h",
"+chrome/browser/chromeos/extensions/login_screen/login/cleanup",
"+chrome/browser/component_updater",
"+chrome/browser/extensions/external_loader.h",
"+chrome/browser/extensions/external_provider_impl.h",

@ -4,9 +4,15 @@
#include "chrome/browser/ash/login/demo_mode/demo_mode_idle_handler.h"
#include <optional>
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/metrics/demo_session_metrics_recorder.h"
#include "ash/public/cpp/wallpaper/wallpaper_controller.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/ash/experiences/idle_detector/idle_detector.h"
#include "components/prefs/pref_service.h"
@ -52,8 +58,10 @@ void ResetPrefs() {
} // namespace
DemoModeIdleHandler::DemoModeIdleHandler(DemoModeWindowCloser* window_closer)
: window_closer_(window_closer) {
DemoModeIdleHandler::DemoModeIdleHandler(
DemoModeWindowCloser* window_closer,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
: window_closer_(window_closer), file_cleaner_(blocking_task_runner) {
user_activity_observer_.Observe(ui::UserActivityDetector::Get());
}
@ -75,9 +83,26 @@ void DemoModeIdleHandler::OnUserActivity(const ui::Event* event) {
base::BindRepeating(&DemoModeIdleHandler::OnIdle,
weak_ptr_factory_.GetWeakPtr()),
/*tick_clock=*/nullptr);
idle_detector_->Start(kReLuanchDemoAppIdleDuration);
idle_detector_->Start(
idle_time_out_for_test_.value_or(kReLuanchDemoAppIdleDuration));
}
void DemoModeIdleHandler::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
void DemoModeIdleHandler::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
void DemoModeIdleHandler::SetIdleTimeoutForTest(
std::optional<base::TimeDelta> timeout) {
idle_time_out_for_test_ = std::move(timeout);
}
// This function is invoked on the task runner of timer. Post task properly on
// different thread.
void DemoModeIdleHandler::OnIdle() {
// Report shopper session dwell time metrics.
DemoSessionMetricsRecorder::Get()->ReportShopperSessionDwellTime();
@ -86,6 +111,11 @@ void DemoModeIdleHandler::OnIdle() {
idle_detector_.reset();
is_user_active_ = false;
if (features::IsDemoModeSignInFileCleanupEnabled()) {
// The IO tasks will be executed from non-UI thread by `file_cleaner_`.
CleanupLocalFiles();
}
window_closer_->StartClosingApps();
ResetPrefs();
@ -96,4 +126,27 @@ void DemoModeIdleHandler::OnIdle() {
// TODO(crbug.com/382360715): Restore network if changed by user.
}
void DemoModeIdleHandler::CleanupLocalFiles() {
// TODO(crbug.com/396731796): Maybe only do clean up when there's a change
// under "MyFiles". Unmount the mounted archives before the cleanup. Restore
// the default download location if user changed it.
// Note this won't work for emulator since "MyFiles" is not mounted from user
// data directory.
file_cleaner_.Cleanup(
base::BindOnce(&DemoModeIdleHandler::OnLocalFilesCleanupCompleted,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoModeIdleHandler::OnLocalFilesCleanupCompleted(
const std::optional<std::string>& error_message) {
if (error_message) {
LOG(ERROR) << "Cleanup local files on device idle failed: "
<< error_message.value();
return;
}
observers_.Notify(&Observer::OnLocalFilesCleanupCompleted);
}
} // namespace ash

@ -9,8 +9,11 @@
#include "base/functional/callback.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/scoped_observation.h"
#include "chrome/browser/ash/login/demo_mode/demo_mode_window_closer.h"
#include "chrome/browser/chromeos/extensions/login_screen/login/cleanup/files_cleanup_handler.h"
#include "ui/base/user_activity/user_activity_detector.h"
#include "ui/base/user_activity/user_activity_observer.h"
@ -23,7 +26,15 @@ class IdleDetector;
// and wait for next user.
class DemoModeIdleHandler : public ui::UserActivityObserver {
public:
explicit DemoModeIdleHandler(DemoModeWindowCloser* window_closer);
class Observer : public base::CheckedObserver {
public:
// Called on local files is cleaned up complete.
virtual void OnLocalFilesCleanupCompleted() = 0;
};
DemoModeIdleHandler(
DemoModeWindowCloser* window_closer,
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner);
DemoModeIdleHandler(const DemoModeIdleHandler&) = delete;
DemoModeIdleHandler& operator=(const DemoModeIdleHandler&) = delete;
~DemoModeIdleHandler() override;
@ -31,13 +42,29 @@ class DemoModeIdleHandler : public ui::UserActivityObserver {
// ui::UserActivityObserver:
void OnUserActivity(const ui::Event* event) override;
// Adds an observer to the observer list.
void AddObserver(Observer* observer);
// Removes an observer from the observer list.
void RemoveObserver(Observer* observer);
void SetIdleTimeoutForTest(std::optional<base::TimeDelta> timeout);
private:
// Called on idle timeout reaches. Could be invoked on non-UI thread when
// using `base::TestMockTimeTaskRunner` for test.
void OnIdle();
// Cleans up everything under "MyFiles" and reset "Downloads" folder to
// empty.
void CleanupLocalFiles();
// Populates demo files after cleanup.
void OnLocalFilesCleanupCompleted(
const std::optional<std::string>& error_message);
// True when the device is not idle.
bool is_user_active_ = false;
// Detect idle when attract loop is not playing. If the attract loop is well
// function and it is not playing, it indicates that a user is actively engage
// with device.
@ -46,9 +73,16 @@ class DemoModeIdleHandler : public ui::UserActivityObserver {
// Not owned:
raw_ptr<DemoModeWindowCloser> window_closer_;
// Cleaner for `MyFiles` directory:
chromeos::FilesCleanupHandler file_cleaner_;
std::optional<base::TimeDelta> idle_time_out_for_test_;
base::ScopedObservation<ui::UserActivityDetector, ui::UserActivityObserver>
user_activity_observer_{this};
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<DemoModeIdleHandler> weak_ptr_factory_{this};
};

@ -12,6 +12,8 @@
#include "ash/wallpaper/test_wallpaper_controller_client.h"
#include "ash/wallpaper/wallpaper_controller_impl.h"
#include "base/files/scoped_temp_dir.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
@ -54,8 +56,9 @@ class DemoModeIdleHandlerTest : public ChromeAshTestBase {
// OK to unretained `this` since the life cycle of `demo_mode_idle_handler_`
// is the same as the tests.
demo_mode_idle_handler_ =
std::make_unique<DemoModeIdleHandler>(window_closer_.get());
demo_mode_idle_handler_ = std::make_unique<DemoModeIdleHandler>(
window_closer_.get(),
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()}));
}
~DemoModeIdleHandlerTest() override = default;

@ -125,8 +125,6 @@ std::vector<std::string> GetIgnorePinPolicyApps() {
}
// Copies photos into the Downloads directory.
// TODO(michaelpg): Test this behavior (requires overriding the Downloads
// directory).
void InstallDemoMedia(const base::FilePath& offline_resources_path,
const base::FilePath& dest_path) {
if (offline_resources_path.empty()) {
@ -135,8 +133,10 @@ void InstallDemoMedia(const base::FilePath& offline_resources_path,
}
base::FilePath src_path = offline_resources_path.Append(kPhotosPath);
if (!base::CopyDirectory(src_path, dest_path, false /* recursive */))
if (!base::CopyDirectory(src_path, dest_path, false /* recursive */)) {
LOG(ERROR) << "Failed to install demo mode media.";
}
}
std::string GetSwitchOrDefault(std::string_view switch_string,
@ -536,7 +536,10 @@ void DemoSession::ActiveUserChanged(user_manager::User* active_user) {
DemoSession::DemoSession()
: ignore_pin_policy_offline_apps_(GetIgnorePinPolicyApps()),
remove_splash_screen_fallback_timer_(
std::make_unique<base::OneShotTimer>()) {
std::make_unique<base::OneShotTimer>()),
blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {
// SessionManager may be unset in unit tests.
if (session_manager::SessionManager::Get()) {
session_manager_observation_.Observe(
@ -547,6 +550,9 @@ DemoSession::DemoSession()
}
DemoSession::~DemoSession() {
// Reset observation before destroying `idle_handler_`.
idle_handler_observation_.Reset();
user_manager::UserManager::Get()->RemoveSessionStateObserver(this);
}
@ -579,10 +585,10 @@ void DemoSession::InstallDemoResources() {
DCHECK(profile);
const base::FilePath downloads =
file_manager::util::GetDownloadsFolderForProfile(profile);
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()},
base::BindOnce(&InstallDemoMedia, components_->resources_component_path(),
downloads));
auto install_media = base::BindOnce(
&InstallDemoMedia, components_->resources_component_path(), downloads);
blocking_task_runner_->PostTask(FROM_HERE, std::move(install_media));
}
void DemoSession::SetKeyboardBrightnessToOneHundredPercentFromCurrentLevel(
@ -721,6 +727,19 @@ base::FilePath DemoSession::GetDemoAppComponentPath() {
components_->default_app_component_path().value()));
}
DemoModeIdleHandler* DemoSession::GetIdleHandlerForTest() const {
return idle_handler_.get();
}
scoped_refptr<base::SequencedTaskRunner>
DemoSession::GetBlockingTaskRunnerForTest() {
return blocking_task_runner_;
}
void DemoSession::OnLocalFilesCleanupCompleted() {
InstallDemoResources();
}
void DemoSession::OnDemoAppComponentLoaded() {
const auto& app_component_version = components_->app_component_version();
SYSLOG(INFO) << "Demo mode app component version: "
@ -740,7 +759,9 @@ void DemoSession::OnDemoAppComponentLoaded() {
if (demo_mode::IsDemoAccountSignInEnabled()) {
CHECK(window_closer_);
idle_handler_ = std::make_unique<DemoModeIdleHandler>(window_closer_.get());
idle_handler_ = std::make_unique<DemoModeIdleHandler>(
window_closer_.get(), blocking_task_runner_);
idle_handler_observation_.Observe(idle_handler_.get());
}
}

@ -43,7 +43,8 @@ class DemoComponents;
// started and the state of demo mode resources.
class DemoSession : public session_manager::SessionManagerObserver,
public user_manager::UserManager::UserSessionStateObserver,
public chromeos::PowerManagerClient::Observer {
public chromeos::PowerManagerClient::Observer,
public DemoModeIdleHandler::Observer {
public:
// Type of demo mode configuration.
// Warning: DemoModeConfig is stored in local state. Existing entries should
@ -183,10 +184,18 @@ class DemoSession : public session_manager::SessionManagerObserver,
// if the splash screen is already removed or never shown.
void RemoveSplashScreen();
DemoModeIdleHandler* GetIdleHandlerForTest() const;
// Gets blocking task runner for test to ensure blocking tasks get flushed.
scoped_refptr<base::SequencedTaskRunner> GetBlockingTaskRunnerForTest();
private:
DemoSession();
~DemoSession() override;
// DemoModeIdleHandler::Observer:
void OnLocalFilesCleanupCompleted() override;
void OnDemoAppComponentLoaded();
// Get country code and full name in current language pair sorted by their
@ -195,7 +204,7 @@ class DemoSession : public session_manager::SessionManagerObserver,
GetSortedCountryCodeAndNamePairList();
// Installs resources for Demo Mode from the offline demo mode resources, such
// as apps and media.
// as photos and other media.
void InstallDemoResources();
// Find image path then show the splash screen.
@ -229,6 +238,9 @@ class DemoSession : public session_manager::SessionManagerObserver,
session_manager::SessionManagerObserver>
session_manager_observation_{this};
base::ScopedObservation<DemoModeIdleHandler, DemoModeIdleHandler::Observer>
idle_handler_observation_{this};
// The fallback timer that ensures the splash screen is removed in case the
// screensaver app takes an extra long time to be shown.
std::unique_ptr<base::OneShotTimer> remove_splash_screen_fallback_timer_;
@ -245,6 +257,10 @@ class DemoSession : public session_manager::SessionManagerObserver,
// sessions. Constructed while demo app is available.
std::unique_ptr<DemoModeIdleHandler> idle_handler_;
// Task runner for file cleanup and re-install demo mode resource at the end
// of shopper sessions.
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
base::WeakPtrFactory<DemoSession> weak_ptr_factory_{this};
};

@ -10,6 +10,11 @@
#include "ash/constants/ash_pref_names.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/scoped_observation.h"
#include "base/test/bind.h"
#include "base/test/run_until.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/login/demo_mode/demo_components.h"
#include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/ash/login/login_manager_test.h"
#include "chrome/browser/ash/login/test/device_state_mixin.h"
@ -28,6 +33,7 @@
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/test/base/browser_process_platform_part_test_api_chromeos.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/dbus/power/fake_power_manager_client.h"
#include "components/component_updater/ash/fake_component_manager_ash.h"
@ -37,6 +43,7 @@
#include "components/variations/active_field_trials.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/test_utils.h"
#include "net/base/url_util.h"
namespace ash {
@ -48,7 +55,12 @@ inline constexpr char kDemoModeAppUrl[] =
"chrome-untrusted://demo-mode-app/index.html";
inline constexpr char kGrowthCampaignsComponentName[] = "growth-campaigns";
inline constexpr char kDemoResourceComponentName[] = "demo-mode-resources";
inline constexpr char kCampaignsFileName[] = "campaigns.json";
inline constexpr char kDemoMediaDirName[] = "media/photos";
inline constexpr char kDemoPhotoName[] = "photo.jpg";
// inline constexpr base::TimeDelta kDemoIdleTimeout = base::Seconds(90);
void SetDemoConfigPref(DemoSession::DemoModeConfig demo_config) {
PrefService* prefs = g_browser_process->local_state();
@ -69,6 +81,8 @@ void CheckNoDemoMode() {
EXPECT_EQ(DemoSession::DemoModeConfig::kNone, DemoSession::GetDemoConfig());
}
} // namespace
// Tests locking device to policy::DEVICE_MODE_DEMO mode. It is an equivalent to
// going through online demo mode setup or using offline setup.
class DemoSessionDemoDeviceModeTest : public OobeBaseTest {
@ -226,17 +240,29 @@ class DemoLoginTestMainExtraParts : public ChromeBrowserMainExtraParts {
.AppendASCII(kGrowthCampaignsComponentName);
}
base::FilePath GetDemoResourceComponentPath() {
return components_temp_dir_.GetPath()
.AppendASCII("cros-components")
.AppendASCII(kDemoResourceComponentName);
}
void PostEarlyInitialization() override {
auto component_manager_ash =
base::MakeRefCounted<component_updater::FakeComponentManagerAsh>();
component_manager_ash->set_supported_components(
{"demo-mode-app", kGrowthCampaignsComponentName});
{"demo-mode-app", kGrowthCampaignsComponentName,
kDemoResourceComponentName});
component_manager_ash->ResetComponentState(
"demo-mode-app",
component_updater::FakeComponentManagerAsh::ComponentInfo(
component_updater::ComponentManagerAsh::Error::NONE,
base::FilePath("/dev/null"),
base::FilePath("/run/imageloader/demo-mode-app")));
component_manager_ash->ResetComponentState(
"demo-mode-resources",
component_updater::FakeComponentManagerAsh::ComponentInfo(
component_updater::ComponentManagerAsh::Error::NONE,
base::FilePath("/dev/null"), GetDemoResourceComponentPath()));
component_manager_ash->ResetComponentState(
"growth-campaigns",
component_updater::FakeComponentManagerAsh::ComponentInfo(
@ -281,6 +307,7 @@ class DemoSessionLoginTest : public LoginManagerTest,
content::BrowserMainParts* browser_main_parts) override {
auto extra_parts = std::make_unique<DemoLoginTestMainExtraParts>();
growth_campaigns_mounted_path_ = extra_parts->GetGrowthCampaignsPath();
demo_resource_mounted_path_ = extra_parts->GetDemoResourceComponentPath();
static_cast<ChromeBrowserMainParts*>(browser_main_parts)
->AddParts(std::move(extra_parts));
LoginManagerTest::CreatedBrowserMainParts(browser_main_parts);
@ -351,6 +378,10 @@ class DemoSessionLoginTest : public LoginManagerTest,
return growth_campaigns_mounted_path_;
}
base::FilePath& demo_resource_mounted_path() {
return demo_resource_mounted_path_;
}
LoginManagerMixin login_manager_mixin_{&mixin_host_};
DeviceStateMixin device_state_mixin_{
&mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_DEMO_MODE};
@ -358,6 +389,7 @@ class DemoSessionLoginTest : public LoginManagerTest,
base::OnceClosure on_browser_added_callback_;
static constexpr double kInitialBrightness = 20.0;
base::FilePath growth_campaigns_mounted_path_;
base::FilePath demo_resource_mounted_path_;
base::WeakPtrFactory<DemoSessionLoginTest> weak_ptr_factory_{this};
};
@ -586,5 +618,93 @@ IN_PROC_BROWSER_TEST_F(DemoSessionLoginWithGrowthCampaignTest,
variations::IsInSyntheticTrialGroup("CrOSGrowthStudy", "CampaignId3"));
}
} // namespace
class DemoSessionLoginIdleHandlerTest : public DemoSessionLoginTest {
public:
DemoSessionLoginIdleHandlerTest() {
scoped_feature_list_.InitWithFeatures(
{features::kDemoModeSignInFileCleanup}, {});
}
void SetUpOnMainThread() override {
demo_mode::SetForceEnableDemoAccountSignIn(true);
DemoSessionLoginTest::SetUpOnMainThread();
CreateTestMediaFile();
}
void CreateTestMediaFile() {
const base::FilePath media_dir =
demo_resource_mounted_path_.AppendASCII(kDemoMediaDirName);
CHECK(base::CreateDirectory(media_dir));
const base::FilePath photo(media_dir.Append(kDemoPhotoName));
CHECK(base::WriteFile(photo, "random text"));
}
void FlushIOTasks() {
base::RunLoop run_loop;
DemoSession::Get()->GetBlockingTaskRunnerForTest()->PostTask(
FROM_HERE, run_loop.QuitClosure());
run_loop.Run();
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
IN_PROC_BROWSER_TEST_F(DemoSessionLoginIdleHandlerTest, CleanUpLocalFiles) {
// Setup initial demo mode resources and start a user session:
OpenBrowserAndInstallSystemAppForActiveProfile();
// Ensure media of resource components gets installed.
FlushIOTasks();
// Verify the photo was copied to download folder.
auto* profile = ProfileManager::GetActiveUserProfile();
base::FilePath downloads_path =
file_manager::util::GetDownloadsFolderForProfile(profile);
base::FilePath photo_file = downloads_path.AppendASCII(kDemoPhotoName);
{
base::ScopedAllowBlockingForTesting allow_blocking;
EXPECT_TRUE(base::PathExists(photo_file));
}
// Shorten the timeout for testing.
base::TimeDelta idle_timeout = base::Seconds(2);
DemoSession::Get()->GetIdleHandlerForTest()->SetIdleTimeoutForTest(
idle_timeout);
// Mock user activity:
ui::UserActivityDetector::Get()->HandleExternalUserActivity();
base::FilePath user_created_dir_path;
{
base::ScopedAllowBlockingForTesting allow_blocking;
// Mock user creates a new folder under "MyFiles" and deletes the photo
// files.
base::ScopedTempDir user_created_dir;
base::FilePath my_files_path = profile->GetPath().AppendASCII("MyFiles");
EXPECT_TRUE(user_created_dir.CreateUniqueTempDirUnderPath(my_files_path));
EXPECT_TRUE(base::DirectoryExists(user_created_dir.GetPath()));
EXPECT_TRUE(base::DeleteFile(photo_file));
user_created_dir_path = user_created_dir.GetPath();
}
// Wait idle timeout + 1s buffer for invoking the file clean up task.
base::RunLoop run_loop{base::RunLoop::Type::kNestableTasksAllowed};
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), idle_timeout + base::Seconds(1));
run_loop.Run();
// Wait file clean up tasks to be finished.
FlushIOTasks();
{
base::ScopedAllowBlockingForTesting allow_blocking;
// Verify `user_created_dir` was deleted and photo was reset.
EXPECT_TRUE(base::test::RunUntil([&user_created_dir_path]() {
return !base::DirectoryExists(user_created_dir_path);
}));
EXPECT_TRUE(base::test::RunUntil(
[&photo_file]() { return base::PathExists(photo_file); }));
}
}
} // namespace ash

@ -65,11 +65,14 @@ bool EnsureDirectoryIsEmpty(const base::FilePath& directory_path,
} // namespace
FilesCleanupHandler::FilesCleanupHandler() {
task_runner_ = base::ThreadPool::CreateTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
}
FilesCleanupHandler::FilesCleanupHandler()
: FilesCleanupHandler(base::ThreadPool::CreateTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
FilesCleanupHandler::FilesCleanupHandler(
scoped_refptr<base::TaskRunner> task_runner)
: task_runner_(task_runner) {}
FilesCleanupHandler::~FilesCleanupHandler() = default;

@ -19,6 +19,7 @@ namespace chromeos {
class FilesCleanupHandler : public CleanupHandler {
public:
FilesCleanupHandler();
explicit FilesCleanupHandler(scoped_refptr<base::TaskRunner> task_runner);
~FilesCleanupHandler() override;
// CleanupHandler:
@ -34,4 +35,4 @@ class FilesCleanupHandler : public CleanupHandler {
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_FILES_CLEANUP_HANDLER_H_
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_LOGIN_SCREEN_LOGIN_CLEANUP_FILES_CLEANUP_HANDLER_H_