| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <memory> |
| #include <optional> |
| #include <string> |
| |
| #include "ash/components/arc/mojom/app.mojom.h" |
| #include "ash/components/arc/test/arc_util_test_support.h" |
| #include "ash/components/arc/test/connection_holder_util.h" |
| #include "ash/components/arc/test/fake_app_instance.h" |
| #include "base/json/json_writer.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/run_loop.h" |
| #include "base/strings/strcat.h" |
| #include "chrome/browser/ash/app_list/arc/arc_app_list_prefs.h" |
| #include "chrome/browser/ash/arc/arc_util.h" |
| #include "chrome/browser/ash/arc/session/arc_session_manager.h" |
| #include "chrome/browser/ash/child_accounts/child_user_service.h" |
| #include "chrome/browser/ash/child_accounts/child_user_service_factory.h" |
| #include "chrome/browser/ash/child_accounts/time_limits/app_activity_registry.h" |
| #include "chrome/browser/ash/child_accounts/time_limits/app_time_controller.h" |
| #include "chrome/browser/ash/child_accounts/time_limits/app_time_limit_utils.h" |
| #include "chrome/browser/ash/child_accounts/time_limits/app_time_limits_policy_builder.h" |
| #include "chrome/browser/ash/child_accounts/time_limits/app_types.h" |
| #include "chrome/browser/ash/login/test/logged_in_user_mixin.h" |
| #include "chrome/browser/ash/login/test/scoped_policy_update.h" |
| #include "chrome/browser/ash/login/test/user_policy_mixin.h" |
| #include "chrome/browser/ash/policy/core/user_policy_test_helper.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/test/base/mixin_based_in_process_browser_test.h" |
| #include "components/services/app_service/public/cpp/app_types.h" |
| #include "components/user_manager/user.h" |
| #include "components/user_manager/user_manager.h" |
| #include "content/public/test/browser_test.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash { |
| namespace app_time { |
| |
| namespace { |
| |
| arc::mojom::ArcPackageInfoPtr CreateArcAppPackage( |
| const std::string& package_name) { |
| auto package = arc::mojom::ArcPackageInfo::New(); |
| package->package_name = package_name; |
| package->package_version = 1; |
| package->last_backup_android_id = 1; |
| package->last_backup_time = 1; |
| package->sync = false; |
| return package; |
| } |
| |
| arc::mojom::AppInfoPtr CreateArcAppInfo(const std::string& package_name) { |
| return arc::mojom::AppInfo::New(package_name, package_name, |
| base::StrCat({package_name, ".", "activity"}), |
| true /* sticky */); |
| } |
| |
| } // namespace |
| |
| // Integration tests for Per-App Time Limits feature. |
| class AppTimeTest : public MixinBasedInProcessBrowserTest { |
| protected: |
| AppTimeTest() = default; |
| AppTimeTest(const AppTimeTest&) = delete; |
| AppTimeTest& operator=(const AppTimeTest&) = delete; |
| ~AppTimeTest() override = default; |
| |
| // MixinBasedInProcessBrowserTest: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| MixinBasedInProcessBrowserTest::SetUpCommandLine(command_line); |
| arc::SetArcAvailableCommandLineForTesting(command_line); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| MixinBasedInProcessBrowserTest::SetUpInProcessBrowserTestFixture(); |
| arc::ArcSessionManager::SetUiEnabledForTesting(false); |
| } |
| |
| void SetUpOnMainThread() override { |
| MixinBasedInProcessBrowserTest::SetUpOnMainThread(); |
| ASSERT_TRUE(embedded_test_server()->Started()); |
| logged_in_user_mixin_.LogInUser(); |
| |
| arc::SetArcPlayStoreEnabledForProfile(browser()->profile(), true); |
| arc_app_list_prefs_ = ArcAppListPrefs::Get(browser()->profile()); |
| EXPECT_TRUE(arc_app_list_prefs_); |
| |
| base::RunLoop run_loop; |
| arc_app_list_prefs_->SetDefaultAppsReadyCallback(run_loop.QuitClosure()); |
| run_loop.Run(); |
| |
| arc_app_instance_ = |
| std::make_unique<arc::FakeAppInstance>(arc_app_list_prefs_); |
| arc_app_list_prefs_->app_connection_holder()->SetInstance( |
| arc_app_instance_.get()); |
| WaitForInstanceReady(arc_app_list_prefs_->app_connection_holder()); |
| arc_app_instance_->set_icon_response_type( |
| arc::FakeAppInstance::IconResponseType::ICON_RESPONSE_SKIP); |
| } |
| |
| void TearDownOnMainThread() override { |
| arc_app_list_prefs_->app_connection_holder()->CloseInstance( |
| arc_app_instance_.get()); |
| arc_app_instance_.reset(); |
| arc::ArcSessionManager::Get()->Shutdown(); |
| } |
| |
| void UpdatePerAppTimeLimitsPolicy(const base::Value::Dict& policy) { |
| std::string policy_value; |
| base::JSONWriter::Write(policy, &policy_value); |
| |
| logged_in_user_mixin_.GetUserPolicyMixin() |
| ->RequestPolicyUpdate() |
| ->policy_payload() |
| ->mutable_perapptimelimits() |
| ->set_value(policy_value); |
| |
| logged_in_user_mixin_.GetUserPolicyTestHelper()->RefreshPolicyAndWait( |
| GetCurrentProfile()); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| AppActivityRegistry* GetAppRegistry() { |
| auto child_user_service = ChildUserService::TestApi( |
| ChildUserServiceFactory::GetForBrowserContext(GetCurrentProfile())); |
| EXPECT_TRUE(child_user_service.app_time_controller()); |
| auto app_time_controller = |
| AppTimeController::TestApi(child_user_service.app_time_controller()); |
| return app_time_controller.app_registry(); |
| } |
| |
| void InstallArcApp(const AppId& app_id) { |
| EXPECT_EQ(apps::AppType::kArc, app_id.app_type()); |
| const std::string& package_name = app_id.app_id(); |
| arc_app_instance_->SendPackageAdded( |
| CreateArcAppPackage(package_name)->Clone()); |
| |
| std::vector<arc::mojom::AppInfoPtr> apps; |
| apps.emplace_back(CreateArcAppInfo(package_name)); |
| |
| arc_app_instance_->SendPackageAppListRefreshed(package_name, apps); |
| |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| void Equals(const AppLimit& limit1, const AppLimit& limit2) { |
| EXPECT_EQ(limit1.restriction(), limit2.restriction()); |
| EXPECT_EQ(limit1.daily_limit(), limit2.daily_limit()); |
| // Compare JavaTime, because some precision is lost when serializing |
| // and deserializing. |
| EXPECT_EQ(limit1.last_updated().InMillisecondsSinceUnixEpoch(), |
| limit2.last_updated().InMillisecondsSinceUnixEpoch()); |
| } |
| |
| private: |
| Profile* GetCurrentProfile() { |
| const user_manager::UserManager* const user_manager = |
| user_manager::UserManager::Get(); |
| Profile* profile = |
| ProfileHelper::Get()->GetProfileByUser(user_manager->GetActiveUser()); |
| EXPECT_TRUE(profile); |
| |
| return profile; |
| } |
| |
| LoggedInUserMixin logged_in_user_mixin_{&mixin_host_, |
| LoggedInUserMixin::LogInType::kChild, |
| embedded_test_server(), this}; |
| |
| raw_ptr<ArcAppListPrefs, DanglingUntriaged> arc_app_list_prefs_ = nullptr; |
| std::unique_ptr<arc::FakeAppInstance> arc_app_instance_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(AppTimeTest, AppInstallation) { |
| const AppId app1(apps::AppType::kArc, "com.example.app1"); |
| AppActivityRegistry* app_registry = GetAppRegistry(); |
| EXPECT_FALSE(app_registry->IsAppInstalled(app1)); |
| |
| InstallArcApp(app1); |
| |
| ASSERT_TRUE(app_registry->IsAppInstalled(app1)); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AppTimeTest, PerAppTimeLimitsPolicyUpdates) { |
| // Install an app. |
| const AppId app1(apps::AppType::kArc, "com.example.app1"); |
| InstallArcApp(app1); |
| |
| AppActivityRegistry* app_registry = GetAppRegistry(); |
| AppActivityRegistry::TestApi app_registry_test(app_registry); |
| ASSERT_TRUE(app_registry->IsAppInstalled(app1)); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| EXPECT_FALSE(app_registry_test.GetAppLimit(app1)); |
| |
| // Block the app. |
| AppTimeLimitsPolicyBuilder block_policy; |
| const AppLimit block_limit = |
| AppLimit(AppRestriction::kBlocked, std::nullopt, base::Time::Now()); |
| block_policy.AddAppLimit(app1, block_limit); |
| block_policy.SetResetTime(6, 0); |
| |
| UpdatePerAppTimeLimitsPolicy(block_policy.value()); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app1)); |
| Equals(block_limit, app_registry_test.GetAppLimit(app1).value()); |
| |
| // Set time limit for the app - app should not paused. |
| AppTimeLimitsPolicyBuilder time_limit_policy; |
| const AppLimit time_limit = |
| AppLimit(AppRestriction::kTimeLimit, base::Hours(1), base::Time::Now()); |
| time_limit_policy.AddAppLimit(app1, time_limit); |
| time_limit_policy.SetResetTime(6, 0); |
| |
| UpdatePerAppTimeLimitsPolicy(time_limit_policy.value()); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app1)); |
| Equals(time_limit, app_registry_test.GetAppLimit(app1).value()); |
| |
| // Set time limit of zero - app should be paused. |
| AppTimeLimitsPolicyBuilder zero_time_limit_policy; |
| const AppLimit zero_limit = |
| AppLimit(AppRestriction::kTimeLimit, base::Hours(0), base::Time::Now()); |
| zero_time_limit_policy.AddAppLimit(app1, zero_limit); |
| zero_time_limit_policy.SetResetTime(6, 0); |
| |
| UpdatePerAppTimeLimitsPolicy(zero_time_limit_policy.value()); |
| EXPECT_FALSE(app_registry->IsAppAvailable(app1)); |
| EXPECT_TRUE(app_registry->IsAppTimeLimitReached(app1)); |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app1)); |
| Equals(zero_limit, app_registry_test.GetAppLimit(app1).value()); |
| |
| // Set time limit grater then zero again - app should be available again. |
| UpdatePerAppTimeLimitsPolicy(time_limit_policy.value()); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| EXPECT_FALSE(app_registry->IsAppTimeLimitReached(app1)); |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app1)); |
| Equals(time_limit, app_registry_test.GetAppLimit(app1).value()); |
| |
| // Remove app restrictions. |
| AppTimeLimitsPolicyBuilder no_limits_policy; |
| no_limits_policy.SetResetTime(6, 0); |
| |
| UpdatePerAppTimeLimitsPolicy(no_limits_policy.value()); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app1)); |
| EXPECT_FALSE(app_registry_test.GetAppLimit(app1)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(AppTimeTest, PerAppTimeLimitsPolicyMultipleEntries) { |
| // Install apps. |
| const AppId app1(apps::AppType::kArc, "com.example.app1"); |
| InstallArcApp(app1); |
| const AppId app2(apps::AppType::kArc, "com.example.app2"); |
| InstallArcApp(app2); |
| const AppId app3(apps::AppType::kArc, "com.example.app3"); |
| InstallArcApp(app3); |
| const AppId app4(apps::AppType::kArc, "com.example.app4"); |
| InstallArcApp(app4); |
| |
| AppActivityRegistry* app_registry = GetAppRegistry(); |
| AppActivityRegistry::TestApi app_registry_test(app_registry); |
| for (const auto& app : {app1, app2, app3, app4}) { |
| ASSERT_TRUE(app_registry->IsAppInstalled(app)); |
| EXPECT_TRUE(app_registry->IsAppAvailable(app)); |
| EXPECT_FALSE(app_registry_test.GetAppLimit(app)); |
| } |
| |
| // Send policy. |
| AppTimeLimitsPolicyBuilder policy; |
| policy.SetResetTime(6, 0); |
| policy.AddAppLimit(app2, AppLimit(AppRestriction::kBlocked, std::nullopt, |
| base::Time::Now())); |
| policy.AddAppLimit(app3, AppLimit(AppRestriction::kTimeLimit, |
| base::Minutes(15), base::Time::Now())); |
| policy.AddAppLimit(app4, AppLimit(AppRestriction::kTimeLimit, base::Hours(1), |
| base::Time::Now())); |
| |
| UpdatePerAppTimeLimitsPolicy(policy.value()); |
| |
| EXPECT_FALSE(app_registry_test.GetAppLimit(app1)); |
| |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app2)); |
| EXPECT_EQ(AppRestriction::kBlocked, |
| app_registry_test.GetAppLimit(app2)->restriction()); |
| |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app3)); |
| EXPECT_EQ(AppRestriction::kTimeLimit, |
| app_registry_test.GetAppLimit(app3)->restriction()); |
| |
| ASSERT_TRUE(app_registry_test.GetAppLimit(app4)); |
| EXPECT_EQ(AppRestriction::kTimeLimit, |
| app_registry_test.GetAppLimit(app4)->restriction()); |
| } |
| |
| } // namespace app_time |
| } // namespace ash |