chromium/chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_handler_unittest.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/settings/pages/multidevice/multidevice_handler.h"

#include <memory>

#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "ash/public/cpp/test/test_new_window_delegate.h"
#include "ash/webui/eche_app_ui/fake_apps_access_manager.h"
#include "base/command_line.h"
#include "base/memory/raw_ptr.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_command_line.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/ash/crosapi/browser_util.h"
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/ash/components/multidevice/remote_device_test_util.h"
#include "chromeos/ash/components/phonehub/fake_browser_tabs_model_provider.h"
#include "chromeos/ash/components/phonehub/fake_camera_roll_manager.h"
#include "chromeos/ash/components/phonehub/fake_multidevice_feature_access_manager.h"
#include "chromeos/ash/components/phonehub/feature_setup_connection_operation.h"
#include "chromeos/ash/components/phonehub/multidevice_feature_access_manager.h"
#include "chromeos/ash/components/phonehub/pref_names.h"
#include "chromeos/ash/components/phonehub/screen_lock_manager.h"
#include "chromeos/ash/components/standalone_browser/lacros_availability.h"
#include "chromeos/ash/components/standalone_browser/standalone_browser_features.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/fake_multidevice_setup_client.h"
#include "chromeos/ash/services/multidevice_setup/public/cpp/prefs.h"
#include "components/account_id/account_id.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
#include "components/sync/base/features.h"
#include "components/user_manager/scoped_user_manager.h"
#include "components/user_manager/user.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_web_ui.h"
#include "testing/gmock/include/gmock/gmock-matchers.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace ash::settings {

namespace {

constexpr char kDialogIntroActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents.IntroScreen";
constexpr char kDialogFinishOnPhoneActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents."
    "FinishSetupOnYourPhoneScreen";
constexpr char kDialogConnectingActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents.ConnectingToPhoneScreen";
constexpr char kDialogConnectionErrorActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents."
    "CouldNotEstablishConnectionScreen";
constexpr char kDialogConnectionTimeOutActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents.ConnectionLostScreen";
constexpr char kDialogSetAPinOrPasswordHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents.SetAPinOrPasswordScreen";
constexpr char kDialogSetupFinishedActionHistogram[] =
    "PhoneHub.PermissionsOnboarding.DialogScreenEvents.SetUpFinishedScreen";
constexpr char kSetupButtonInSettingsClikedHistogram[] =
    "PhoneHub.PermissionsOnboarding.SetUpMode.OnSettingsClicked";
constexpr char kDialogIntroScreenSetupModeHistogram[] =
    "PhoneHub.PermissionsOnboarding.SetUpMode.IntroScreenShown";
constexpr char kDialogSetUpFinishedScreenSetupModeHistogram[] =
    "PhoneHub.PermissionsOnboarding.SetUpMode.SetUpFinishedScreenShown";
constexpr char kManagedUserEmail[] = "[email protected]";

using ::testing::Optional;

class MockNewWindowDelegate : public testing::NiceMock<TestNewWindowDelegate> {
 public:
  // TestNewWindowDelegate:
  MOCK_METHOD(void,
              OpenUrl,
              (const GURL& url, OpenUrlFrom from, Disposition disposition),
              (override));
};

class TestMultideviceHandler : public MultideviceHandler {
 public:
  TestMultideviceHandler(
      PrefService* prefs,
      multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client,
      phonehub::MultideviceFeatureAccessManager*
          multidevice_feature_access_manager,
      eche_app::AppsAccessManager* apps_access_manager,
      phonehub::CameraRollManager* camera_roll_manager,
      phonehub::BrowserTabsModelProvider* browser_tabs_model_provider)
      : MultideviceHandler(prefs,
                           multidevice_setup_client,
                           multidevice_feature_access_manager,
                           apps_access_manager,
                           camera_roll_manager,
                           browser_tabs_model_provider) {}
  ~TestMultideviceHandler() override = default;

  // Make public for testing.
  using MultideviceHandler::AllowJavascript;
  using MultideviceHandler::RegisterMessages;
  using MultideviceHandler::set_web_ui;
};

// This class wraps the setup of a Lacros Only environment and makes it easy to
// reset the state after use by destroying the handle.
class ScopedLacrosOnlyHandle {
 public:
  ScopedLacrosOnlyHandle() {
    auto fake_user_manager = std::make_unique<ash::FakeChromeUserManager>();
    fake_user_manager_ = fake_user_manager.get();
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(fake_user_manager));

    SetLoggedInUser();
    SetLacrosAvailability();
  }

  ~ScopedLacrosOnlyHandle() { ResetLacrosAvailability(); }

 private:
  void SetLoggedInUser() {
    AccountId account_id = AccountId::FromUserEmail(kManagedUserEmail);
    const user_manager::User* user = fake_user_manager_->AddUser(account_id);
    fake_user_manager_->UserLoggedIn(account_id, user->username_hash(),
                                     /*browser_restart=*/false,
                                     /*is_child=*/false);
  }

  void SetLacrosAvailability() {
    policy::PolicyMap policy;
    policy.Set(policy::key::kLacrosAvailability, policy::POLICY_LEVEL_MANDATORY,
               policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
               base::Value(GetLacrosAvailabilityPolicyName(
                   standalone_browser::LacrosAvailability::kLacrosOnly)),
               /*external_data_fetcher=*/nullptr);
    crosapi::browser_util::CacheLacrosAvailability(policy);
  }

  void ResetLacrosAvailability() {
    crosapi::browser_util::ClearLacrosAvailabilityCacheForTest();
  }

  raw_ptr<ash::FakeChromeUserManager, DanglingUntriaged> fake_user_manager_ =
      nullptr;
  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};

multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
GenerateDefaultFeatureStatesMap() {
  return multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap{
      {multidevice_setup::mojom::Feature::kBetterTogetherSuite,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kInstantTethering,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kSmartLock,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kPhoneHub,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kPhoneHubNotifications,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kPhoneHubCameraRoll,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kPhoneHubTaskContinuation,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kWifiSync,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts},
      {multidevice_setup::mojom::Feature::kEche,
       multidevice_setup::mojom::FeatureState::
           kUnavailableNoVerifiedHost_NoEligibleHosts}};
}

void VerifyPageContentDict(
    const base::Value* value,
    multidevice_setup::mojom::HostStatus expected_host_status,
    const std::optional<multidevice::RemoteDeviceRef>& expected_host_device,
    const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
        feature_states_map,
    bool expected_is_nearby_share_disallowed_by_policy_,
    bool expected_is_phone_hub_apps_access_granted_,
    bool expected_is_camera_roll_file_permission_granted_,
    bool expected_is_camera_roll_access_status_granted_,
    bool expected_is_feature_setup_request_supported_,
    bool expected_is_lacros_tab_sync_enabled_) {
  ASSERT_TRUE(value->is_dict());
  const base::Value::Dict& page_content_dict = value->GetDict();

  std::optional<int> mode = page_content_dict.FindInt("mode");
  ASSERT_TRUE(mode);
  EXPECT_EQ(static_cast<int>(expected_host_status), *mode);

  std::optional<int> better_together_state =
      page_content_dict.FindInt("betterTogetherState");
  ASSERT_TRUE(better_together_state);
  auto it = feature_states_map.find(
      multidevice_setup::mojom::Feature::kBetterTogetherSuite);
  EXPECT_EQ(static_cast<int>(it->second), *better_together_state);

  std::optional<int> instant_tethering_state =
      page_content_dict.FindInt("instantTetheringState");
  ASSERT_TRUE(instant_tethering_state);
  it = feature_states_map.find(
      multidevice_setup::mojom::Feature::kInstantTethering);
  EXPECT_EQ(static_cast<int>(it->second), *instant_tethering_state);

  std::optional<int> smart_lock_state =
      page_content_dict.FindInt("smartLockState");
  ASSERT_TRUE(smart_lock_state);
  it = feature_states_map.find(multidevice_setup::mojom::Feature::kSmartLock);
  EXPECT_EQ(static_cast<int>(it->second), *smart_lock_state);

  std::optional<int> phone_hub_state =
      page_content_dict.FindInt("phoneHubState");
  ASSERT_TRUE(phone_hub_state);
  it = feature_states_map.find(multidevice_setup::mojom::Feature::kPhoneHub);
  EXPECT_EQ(static_cast<int>(it->second), *phone_hub_state);

  std::optional<int> phone_hub_notifications_state =
      page_content_dict.FindInt("phoneHubNotificationsState");
  ASSERT_TRUE(phone_hub_notifications_state);
  it = feature_states_map.find(
      multidevice_setup::mojom::Feature::kPhoneHubNotifications);
  EXPECT_EQ(static_cast<int>(it->second), *phone_hub_notifications_state);

  std::optional<int> phone_hub_camera_roll_state =
      page_content_dict.FindInt("phoneHubCameraRollState");
  ASSERT_TRUE(phone_hub_camera_roll_state);
  if (base::FeatureList::IsEnabled(ash::features::kPhoneHubCameraRoll)) {
    it = feature_states_map.find(
        multidevice_setup::mojom::Feature::kPhoneHubCameraRoll);
    EXPECT_EQ(static_cast<int>(it->second), *phone_hub_camera_roll_state);
  } else {
    EXPECT_EQ(
        static_cast<int>(
            multidevice_setup::mojom::FeatureState::kNotSupportedByChromebook),
        *phone_hub_camera_roll_state);
  }

  std::optional<int> phone_hub_task_continuation_state =
      page_content_dict.FindInt("phoneHubTaskContinuationState");
  ASSERT_TRUE(phone_hub_task_continuation_state);
  it = feature_states_map.find(
      multidevice_setup::mojom::Feature::kPhoneHubTaskContinuation);
  EXPECT_EQ(static_cast<int>(it->second), *phone_hub_task_continuation_state);

  std::optional<int> phone_hub_apps_state =
      page_content_dict.FindInt("phoneHubAppsState");
  ASSERT_TRUE(phone_hub_apps_state);
  if (base::FeatureList::IsEnabled(ash::features::kEcheSWA)) {
    it = feature_states_map.find(multidevice_setup::mojom::Feature::kEche);
    EXPECT_EQ(static_cast<int>(it->second), *phone_hub_apps_state);
  } else {
    EXPECT_EQ(
        static_cast<int>(
            multidevice_setup::mojom::FeatureState::kNotSupportedByChromebook),
        *phone_hub_apps_state);
  }

  std::optional<int> wifi_sync_state =
      page_content_dict.FindInt("wifiSyncState");
  ASSERT_TRUE(wifi_sync_state);
  it = feature_states_map.find(multidevice_setup::mojom::Feature::kWifiSync);
  EXPECT_EQ(static_cast<int>(it->second), *wifi_sync_state);

  const std::string* host_device_name =
      page_content_dict.FindString("hostDeviceName");
  if (expected_host_device) {
    ASSERT_TRUE(host_device_name);
    EXPECT_EQ(expected_host_device->name(), *host_device_name);
  } else {
    EXPECT_FALSE(host_device_name);
  }

  EXPECT_THAT(page_content_dict.FindBool("isNearbyShareDisallowedByPolicy"),
              Optional(expected_is_nearby_share_disallowed_by_policy_));

  EXPECT_THAT(page_content_dict.FindInt("appsAccessStatus"),
              Optional(expected_is_phone_hub_apps_access_granted_ ? 2 : 1));

  EXPECT_THAT(page_content_dict.FindBool("isCameraRollFilePermissionGranted"),
              Optional(expected_is_camera_roll_file_permission_granted_));

  EXPECT_THAT(
      page_content_dict.FindBool("isPhoneHubPermissionsDialogSupported"),
      Optional(features::IsEcheSWAEnabled() ||
               features::IsPhoneHubCameraRollEnabled()));

  EXPECT_THAT(page_content_dict.FindInt("cameraRollAccessStatus"),
              Optional(expected_is_camera_roll_access_status_granted_ ? 2 : 1));

  EXPECT_THAT(
      page_content_dict.FindBool("isPhoneHubFeatureCombinedSetupSupported"),
      Optional(expected_is_feature_setup_request_supported_));

  EXPECT_THAT(page_content_dict.FindBool("isLacrosTabSyncEnabled"),
              Optional(expected_is_lacros_tab_sync_enabled_));
}

}  // namespace

class MultideviceHandlerTest : public testing::Test {
 public:
  MultideviceHandlerTest(const MultideviceHandlerTest&) = delete;
  MultideviceHandlerTest& operator=(const MultideviceHandlerTest&) = delete;

 protected:
  MultideviceHandlerTest()
      : test_device_(multidevice::CreateRemoteDeviceRefForTest()) {}
  ~MultideviceHandlerTest() override = default;

  // testing::Test:
  void SetUp() override {
    fake_multidevice_setup_client_ =
        std::make_unique<multidevice_setup::FakeMultiDeviceSetupClient>();
    fake_multidevice_feature_access_manager_ =
        std::make_unique<phonehub::FakeMultideviceFeatureAccessManager>(
            phonehub::MultideviceFeatureAccessManager::AccessStatus::
                kAvailableButNotGranted);
    fake_apps_access_manager_ =
        std::make_unique<eche_app::FakeAppsAccessManager>(
            phonehub::MultideviceFeatureAccessManager::AccessStatus::
                kAvailableButNotGranted);
    fake_camera_roll_manager_ =
        std::make_unique<phonehub::FakeCameraRollManager>();

    prefs_ = std::make_unique<TestingPrefServiceSimple>();
    RegisterNearbySharingPrefs(prefs_->registry());
    prefs_->SetBoolean(::prefs::kNearbySharingEnabledPrefName, true);
    NearbySharingServiceFactory::
        SetIsNearbyShareSupportedForBrowserContextForTesting(true);

    prefs_->registry()->RegisterBooleanPref(
        multidevice_setup::kInstantTetheringAllowedPrefName,
        /*default_value=*/true);
    prefs_->registry()->RegisterBooleanPref(ash::prefs::kEnableAutoScreenLock,
                                            false);
    prefs_->registry()->RegisterIntegerPref(
        phonehub::prefs::kScreenLockStatus,
        static_cast<int>(phonehub::ScreenLockManager::LockStatus::kLockedOff));

    InitializeNewWindowDelegate();
    CreateHandler();
  }

  void TearDown() override { new_window_provider_.reset(); }

  void CreateHandler() {
    handler_ = std::make_unique<TestMultideviceHandler>(
        prefs_.get(), fake_multidevice_setup_client_.get(),
        fake_multidevice_feature_access_manager_.get(),
        fake_apps_access_manager_.get(), fake_camera_roll_manager_.get(),
        &fake_browser_tabs_model_provider_);

    test_web_contents_ = content::WebContents::Create(
        content::WebContents::CreateParams(&test_profile_));
    test_web_ui_ = std::make_unique<content::TestWebUI>();
    test_web_ui_->set_web_contents(test_web_contents_.get());
    handler_->set_web_ui(test_web_ui_.get());

    handler_->RegisterMessages();
    handler_->AllowJavascript();
  }

  void InitializeNewWindowDelegate() {
    auto instance = std::make_unique<MockNewWindowDelegate>();
    auto primary = std::make_unique<MockNewWindowDelegate>();
    new_window_delegate_primary_ = primary.get();
    new_window_provider_ = std::make_unique<TestNewWindowDelegateProvider>(
        std::move(instance), std::move(primary));
  }

  void InitWithFeatures(
      const std::vector<base::test::FeatureRef>& enabled_features,
      const std::vector<base::test::FeatureRef>& disabled_features) {
    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

  void SetUpHandlerWithEmptyManagers() {
    handler_.reset();
    test_web_ui_.reset();
    handler_ = std::make_unique<TestMultideviceHandler>(
        prefs_.get(), fake_multidevice_setup_client_.get(), nullptr, nullptr,
        nullptr, nullptr);

    test_web_ui_ = std::make_unique<content::TestWebUI>();
    test_web_ui_->set_web_contents(test_web_contents_.get());
    handler_->set_web_ui(test_web_ui_.get());

    handler_->RegisterMessages();
    handler_->AllowJavascript();
  }

  void CallGetPageContentData() {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    base::Value::List args;
    args.Append("handlerFunctionName");
    test_web_ui()->HandleReceivedMessage("getPageContentData", args);

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIResponse", call_data.function_name());
    EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
    EXPECT_TRUE(call_data.arg2()->GetBool());
    VerifyPageContent(call_data.arg3());
  }

  void CallRemoveHostDevice() {
    size_t num_remote_host_device_calls_before_call =
        fake_multidevice_setup_client()->num_remove_host_device_called();
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("removeHostDevice", empty_args);
    EXPECT_EQ(num_remote_host_device_calls_before_call + 1u,
              fake_multidevice_setup_client()->num_remove_host_device_called());
  }

  void CallAttemptNotificationSetup(bool has_notification_access_been_granted) {
    fake_multidevice_feature_access_manager()
        ->SetNotificationAccessStatusInternal(
            has_notification_access_been_granted
                ? phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAccessGranted
                : phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAvailableButNotGranted,
            phonehub::MultideviceFeatureAccessManager::AccessProhibitedReason::
                kUnknown);
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("attemptNotificationSetup",
                                         empty_args);
  }

  void CallCancelNotificationSetup() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("cancelNotificationSetup", empty_args);
  }

  void CallAttemptAppsSetup(bool has_access_been_granted) {
    fake_apps_access_manager()->SetAccessStatusInternal(
        has_access_been_granted ? phonehub::MultideviceFeatureAccessManager::
                                      AccessStatus::kAccessGranted
                                : phonehub::MultideviceFeatureAccessManager::
                                      AccessStatus::kAvailableButNotGranted);
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("attemptAppsSetup", empty_args);
  }

  void CallCancelAppsSetup() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("cancelAppsSetup", empty_args);
  }

  void CallAttemptCameraRollSetup(bool has_camera_roll_access_been_granted) {
    fake_multidevice_feature_access_manager()
        ->SetCameraRollAccessStatusInternal(
            has_camera_roll_access_been_granted
                ? phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAccessGranted
                : phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAvailableButNotGranted);
    base::Value::List args;
    args.Append(/*camera_roll=*/true);
    args.Append(/*notifications=*/false);
    test_web_ui()->HandleReceivedMessage("attemptCombinedFeatureSetup", args);
  }

  void CallCancelCameraRollSetup() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("cancelCombinedFeatureSetup",
                                         empty_args);
  }

  void CalAttempFeatureSetupConnection() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("attemptFeatureSetupConnection",
                                         empty_args);
  }

  void CallCancelFeatureSetupConnection() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("cancelFeatureSetupConnection",
                                         empty_args);
  }

  void CallHandleShowBrowserSyncSettings() {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("showBrowserSyncSettings", empty_args);
  }

  void SimulateHostStatusUpdate(
      multidevice_setup::mojom::HostStatus host_status,
      const std::optional<multidevice::RemoteDeviceRef>& host_device) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_multidevice_setup_client_->SetHostStatusWithDevice(
        std::make_pair(host_status, host_device));
    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void SimulateFeatureStatesUpdate(
      const multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap&
          feature_states_map) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_multidevice_setup_client_->SetFeatureStates(feature_states_map);
    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void SimulateNearbyShareEnabledPrefChange(bool is_enabled, bool is_managed) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();
    size_t expected_call_count = call_data_count_before_call;
    bool did_managed_change =
        is_managed !=
        prefs_->IsManagedPreference(::prefs::kNearbySharingEnabledPrefName);
    bool did_enabled_change =
        is_enabled !=
        prefs_->GetBoolean(::prefs::kNearbySharingEnabledPrefName);

    if (is_managed) {
      prefs_->SetManagedPref(::prefs::kNearbySharingEnabledPrefName,
                             std::make_unique<base::Value>(is_enabled));
      EXPECT_TRUE(
          prefs_->IsManagedPreference(::prefs::kNearbySharingEnabledPrefName));
      if (did_managed_change) {
        ++expected_call_count;
      }
    } else {
      prefs_->RemoveManagedPref(::prefs::kNearbySharingEnabledPrefName);
      EXPECT_FALSE(
          prefs_->IsManagedPreference(::prefs::kNearbySharingEnabledPrefName));
      if (did_managed_change) {
        ++expected_call_count;
      }

      prefs_->SetBoolean(::prefs::kNearbySharingEnabledPrefName, is_enabled);
      if (did_enabled_change) {
        ++expected_call_count;
      }
    }
    EXPECT_EQ(is_enabled,
              prefs_->GetBoolean(::prefs::kNearbySharingEnabledPrefName));

    EXPECT_EQ(expected_call_count, test_web_ui()->call_data().size());

    if (expected_call_count == call_data_count_before_call) {
      return;
    }

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(expected_call_count - 1);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());

    expected_is_nearby_share_disallowed_by_policy_ = !is_enabled && is_managed;
    VerifyPageContent(call_data.arg2());
  }

  void SimulateAppsAccessStatusChanged(bool has_access_been_granted) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    phonehub::MultideviceFeatureAccessManager::AccessStatus apps_access_status =
        has_access_been_granted ? phonehub::MultideviceFeatureAccessManager::
                                      AccessStatus::kAccessGranted
                                : phonehub::MultideviceFeatureAccessManager::
                                      AccessStatus::kAvailableButNotGranted;
    fake_apps_access_manager()->SetAccessStatusInternal(apps_access_status);
    expected_is_phone_hub_apps_access_granted_ = has_access_been_granted;

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void SimulateCameraRollFilePermissionChanged(bool file_permission_granted) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_camera_roll_manager()->SetIsAndroidStorageGranted(
        file_permission_granted);
    expected_is_camera_roll_file_permission_granted_ = file_permission_granted;

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void SimulateCameraRollAccessstatusChanged(
      bool has_camera_roll_access_status_granted) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_multidevice_feature_access_manager()
        ->SetCameraRollAccessStatusInternal(
            has_camera_roll_access_status_granted
                ? phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAccessGranted
                : phonehub::MultideviceFeatureAccessManager::AccessStatus::
                      kAvailableButNotGranted);
    expected_is_camera_roll_access_status_granted_ =
        has_camera_roll_access_status_granted;
    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void SimulateLacrosTabSyncEnabledChanged(bool is_lacros_tab_sync_enabled) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_browser_tabs_model_provider_.NotifyBrowserTabsUpdated(
        is_lacros_tab_sync_enabled, {});
    if (base::FeatureList::IsEnabled(syncer::kChromeOSSyncedSessionSharing)) {
      expected_is_lacros_tab_sync_enabled_ = is_lacros_tab_sync_enabled;
    } else {
      expected_is_lacros_tab_sync_enabled_ = false;
    }

    ASSERT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.updateMultidevicePageContentData",
              call_data.arg1()->GetString());
    VerifyPageContent(call_data.arg2());
  }

  void CallRetryPendingHostSetup(bool success) {
    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("retryPendingHostSetup", empty_args);
    fake_multidevice_setup_client()->InvokePendingRetrySetHostNowCallback(
        success);
  }

  void CallSetFeatureEnabledState(multidevice_setup::mojom::Feature feature,
                                  bool enabled,
                                  const std::optional<std::string>& auth_token,
                                  bool success) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    base::Value::List args;
    args.Append("handlerFunctionName");
    args.Append(static_cast<int>(feature));
    args.Append(enabled);
    if (auth_token) {
      args.Append(*auth_token);
    }

    base::Value::List empty_args;
    test_web_ui()->HandleReceivedMessage("setFeatureEnabledState", args);
    fake_multidevice_setup_client()
        ->InvokePendingSetFeatureEnabledStateCallback(
            feature /* expected_feature */, enabled /* expected_enabled */,
            auth_token /* expected_auth_token */, success);

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIResponse", call_data.function_name());
    EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString());
    EXPECT_TRUE(call_data.arg2()->GetBool());
    EXPECT_EQ(success, call_data.arg3()->GetBool());
  }

  const content::TestWebUI::CallData& CallDataAtIndex(size_t index) {
    return *test_web_ui_->call_data()[index];
  }

  content::TestWebUI* test_web_ui() { return test_web_ui_.get(); }

  multidevice_setup::FakeMultiDeviceSetupClient*
  fake_multidevice_setup_client() {
    return fake_multidevice_setup_client_.get();
  }

  phonehub::FakeMultideviceFeatureAccessManager*
  fake_multidevice_feature_access_manager() {
    return fake_multidevice_feature_access_manager_.get();
  }

  eche_app::FakeAppsAccessManager* fake_apps_access_manager() {
    return fake_apps_access_manager_.get();
  }

  phonehub::FakeCameraRollManager* fake_camera_roll_manager() {
    return fake_camera_roll_manager_.get();
  }

  void SimulateNotificationOptInStatusChange(
      phonehub::NotificationAccessSetupOperation::Status status) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_multidevice_feature_access_manager()
        ->SetNotificationSetupOperationStatus(status);

    bool completed_successfully = status ==
                                  phonehub::NotificationAccessSetupOperation::
                                      Status::kCompletedSuccessfully;
    if (completed_successfully) {
      call_data_count_before_call++;
    }

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.onNotificationAccessSetupStatusChanged",
              call_data.arg1()->GetString());
    EXPECT_EQ(call_data.arg2()->GetInt(), static_cast<int32_t>(status));
  }

  bool IsNotificationAccessSetupOperationInProgress() {
    return fake_multidevice_feature_access_manager()
        ->IsNotificationSetupOperationInProgress();
  }

  void SimulateAppsOptInStatusChange(
      eche_app::AppsAccessSetupOperation::Status status) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_apps_access_manager()->SetAppsSetupOperationStatus(status);

    bool completed_successfully =
        status ==
        eche_app::AppsAccessSetupOperation::Status::kCompletedSuccessfully;
    if (completed_successfully) {
      call_data_count_before_call++;
    }

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.onAppsAccessSetupStatusChanged",
              call_data.arg1()->GetString());
    EXPECT_EQ(call_data.arg2()->GetInt(), static_cast<int32_t>(status));
  }

  bool IsAppsAccessSetupOperationInProgress() {
    return fake_apps_access_manager()->IsSetupOperationInProgress();
  }

  void SimulateCameraRollOptInStatusChange(
      phonehub::CombinedAccessSetupOperation::Status status) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    fake_multidevice_feature_access_manager()->SetCombinedSetupOperationStatus(
        status);

    bool completed_successfully =
        status ==
        phonehub::CombinedAccessSetupOperation::Status::kCompletedSuccessfully;
    if (completed_successfully) {
      call_data_count_before_call++;
    }

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.onCombinedAccessSetupStatusChanged",
              call_data.arg1()->GetString());
    EXPECT_EQ(call_data.arg2()->GetInt(), static_cast<int32_t>(status));
  }

  void SimulateFeatureSetupConnectionStatusChange(
      phonehub::FeatureSetupConnectionOperation::Status status) {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();
    fake_multidevice_feature_access_manager()
        ->SetFeatureSetupConnectionOperationStatus(status);

    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.onFeatureSetupConnectionStatusChanged",
              call_data.arg1()->GetString());
    EXPECT_EQ(call_data.arg2()->GetInt(), static_cast<int32_t>(status));
  }

  bool IsFeatureSetupConnectionOperationInProgress() {
    return fake_multidevice_feature_access_manager()
        ->IsFeatureSetupConnectionOperationInProgress();
  }

  void SimulateEnableScreenLockChanged() {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    prefs_->SetBoolean(ash::prefs::kEnableAutoScreenLock, true);

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.OnEnableScreenLockChanged",
              call_data.arg1()->GetString());
  }

  void SimulateScreenLockStatusChanged() {
    size_t call_data_count_before_call = test_web_ui()->call_data().size();

    prefs_->SetInteger(
        phonehub::prefs::kScreenLockStatus,
        static_cast<int>(phonehub::ScreenLockManager::LockStatus::kLockedOn));

    EXPECT_EQ(call_data_count_before_call + 1u,
              test_web_ui()->call_data().size());
    const content::TestWebUI::CallData& call_data =
        CallDataAtIndex(call_data_count_before_call);
    EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name());
    EXPECT_EQ("settings.OnScreenLockStatusChanged",
              call_data.arg1()->GetString());
    EXPECT_TRUE(call_data.arg2()->GetBool());
  }

  bool IsCameraRollAccessSetupOperationInProgress() {
    return fake_multidevice_feature_access_manager()
        ->IsCombinedSetupOperationInProgress();
  }

  MockNewWindowDelegate* new_window_delegate_primary() {
    return new_window_delegate_primary_;
  }

  const multidevice::RemoteDeviceRef test_device_;

  bool expected_is_nearby_share_disallowed_by_policy_ = false;
  bool expected_is_phone_hub_apps_access_granted_ = false;
  bool expected_is_camera_roll_file_permission_granted_ = true;
  bool expected_is_camera_roll_access_status_granted_ = false;
  bool expected_is_feature_setup_request_supported_ = false;
  bool expected_is_lacros_tab_sync_enabled_ = false;

 private:
  void VerifyPageContent(const base::Value* value) {
    VerifyPageContentDict(
        value, fake_multidevice_setup_client_->GetHostStatus().first,
        fake_multidevice_setup_client_->GetHostStatus().second,
        fake_multidevice_setup_client_->GetFeatureStates(),
        expected_is_nearby_share_disallowed_by_policy_,
        expected_is_phone_hub_apps_access_granted_,
        expected_is_camera_roll_file_permission_granted_,
        expected_is_camera_roll_access_status_granted_,
        expected_is_feature_setup_request_supported_,
        expected_is_lacros_tab_sync_enabled_);
  }

  content::BrowserTaskEnvironment task_environment_;
  TestingProfile test_profile_;
  std::unique_ptr<TestingPrefServiceSimple> prefs_;
  std::unique_ptr<content::WebContents> test_web_contents_;
  std::unique_ptr<content::TestWebUI> test_web_ui_;
  std::unique_ptr<multidevice_setup::FakeMultiDeviceSetupClient>
      fake_multidevice_setup_client_;
  std::unique_ptr<phonehub::FakeMultideviceFeatureAccessManager>
      fake_multidevice_feature_access_manager_;
  std::unique_ptr<eche_app::FakeAppsAccessManager> fake_apps_access_manager_;
  std::unique_ptr<phonehub::FakeCameraRollManager> fake_camera_roll_manager_;
  phonehub::FakeBrowserTabsModelProvider fake_browser_tabs_model_provider_;
  raw_ptr<MockNewWindowDelegate, DanglingUntriaged>
      new_window_delegate_primary_;
  std::unique_ptr<TestNewWindowDelegateProvider> new_window_provider_;

  multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice
      host_status_with_device_;
  multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
      feature_states_map_;

  std::unique_ptr<TestMultideviceHandler> handler_;
  base::test::ScopedFeatureList scoped_feature_list_;
};

TEST_F(MultideviceHandlerTest, PageContentDataRequestedWithNullManagers) {
  SetUpHandlerWithEmptyManagers();

  base::Value::List args;
  args.Append("handlerFunctionName");
  test_web_ui()->HandleReceivedMessage("getPageContentData", args);
}

TEST_F(MultideviceHandlerTest, FeatureSetupConnectionFlow) {
  using Status = phonehub::FeatureSetupConnectionOperation::Status;

  CalAttempFeatureSetupConnection();
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnected);
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kCompletedSuccessfully);
  EXPECT_FALSE(IsFeatureSetupConnectionOperationInProgress());

  CalAttempFeatureSetupConnection();
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  CallCancelFeatureSetupConnection();
  EXPECT_FALSE(IsFeatureSetupConnectionOperationInProgress());

  CalAttempFeatureSetupConnection();
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kTimedOutConnecting);
  EXPECT_FALSE(IsFeatureSetupConnectionOperationInProgress());

  CalAttempFeatureSetupConnection();
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnected);
  EXPECT_TRUE(IsFeatureSetupConnectionOperationInProgress());

  SimulateFeatureSetupConnectionStatusChange(Status::kConnectionLost);
  EXPECT_FALSE(IsFeatureSetupConnectionOperationInProgress());
}

TEST_F(MultideviceHandlerTest, NotificationSetupFlow) {
  using Status = phonehub::NotificationAccessSetupOperation::Status;

  // Simulate success flow.
  CallAttemptNotificationSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(
      Status::kSentMessageToPhoneAndWaitingForResponse);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kCompletedSuccessfully);
  EXPECT_FALSE(IsNotificationAccessSetupOperationInProgress());

  // Simulate cancel flow.
  CallAttemptNotificationSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  CallCancelNotificationSetup();
  EXPECT_FALSE(IsNotificationAccessSetupOperationInProgress());

  // Simulate failure via time-out flow.
  CallAttemptNotificationSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kTimedOutConnecting);
  EXPECT_FALSE(IsNotificationAccessSetupOperationInProgress());

  // Simulate failure via connected then disconnected flow.
  CallAttemptNotificationSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsNotificationAccessSetupOperationInProgress());

  SimulateNotificationOptInStatusChange(Status::kConnectionDisconnected);
  EXPECT_FALSE(IsNotificationAccessSetupOperationInProgress());

  // If access has already been granted, a setup operation should not occur.
  CallAttemptNotificationSetup(/*has_access_been_granted=*/true);
  EXPECT_FALSE(IsNotificationAccessSetupOperationInProgress());
}

TEST_F(MultideviceHandlerTest, AppsSetupFlow) {
  InitWithFeatures(/* enabled_features */ {ash::features::kPhoneHub,
                                           ash::features::kEcheSWA},
                   /* disabled_features */ {});
  using Status = eche_app::AppsAccessSetupOperation::Status;

  // Simulate success flow.
  CallAttemptAppsSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(
      Status::kSentMessageToPhoneAndWaitingForResponse);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kCompletedSuccessfully);
  EXPECT_FALSE(IsAppsAccessSetupOperationInProgress());

  // Simulate cancel flow.
  CallAttemptAppsSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  CallCancelAppsSetup();
  EXPECT_FALSE(IsAppsAccessSetupOperationInProgress());

  // Simulate failure via time-out flow.
  CallAttemptAppsSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kTimedOutConnecting);
  EXPECT_FALSE(IsAppsAccessSetupOperationInProgress());

  // Simulate failure via connected then disconnected flow.
  CallAttemptAppsSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsAppsAccessSetupOperationInProgress());

  SimulateAppsOptInStatusChange(Status::kConnectionDisconnected);
  EXPECT_FALSE(IsAppsAccessSetupOperationInProgress());

  // If access has already been granted, a setup operation should not occur.
  CallAttemptAppsSetup(/*has_access_been_granted=*/true);
  EXPECT_FALSE(IsAppsAccessSetupOperationInProgress());
}

TEST_F(MultideviceHandlerTest, CameraRollSetupFlow) {
  using Status = phonehub::CombinedAccessSetupOperation::Status;
  fake_multidevice_feature_access_manager()
      ->SetFeatureSetupRequestSupportedInternal(true);

  // Simulate success flow.
  CallAttemptCameraRollSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(
      Status::kSentMessageToPhoneAndWaitingForResponse);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kCompletedSuccessfully);
  EXPECT_FALSE(IsCameraRollAccessSetupOperationInProgress());

  // Simulate cancel flow.
  CallAttemptCameraRollSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  CallCancelCameraRollSetup();
  EXPECT_FALSE(IsCameraRollAccessSetupOperationInProgress());

  // Simulate failure via time-out flow.
  CallAttemptCameraRollSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kTimedOutConnecting);
  EXPECT_FALSE(IsCameraRollAccessSetupOperationInProgress());

  // Simulate failure via connected then disconnected flow.
  CallAttemptCameraRollSetup(/*has_access_been_granted=*/false);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kConnecting);
  EXPECT_TRUE(IsCameraRollAccessSetupOperationInProgress());

  SimulateCameraRollOptInStatusChange(Status::kConnectionDisconnected);
  EXPECT_FALSE(IsCameraRollAccessSetupOperationInProgress());

  // If access has already been granted, a setup operation should not occur.
  CallAttemptCameraRollSetup(/*has_access_been_granted=*/true);
  EXPECT_FALSE(IsCameraRollAccessSetupOperationInProgress());
}

TEST_F(MultideviceHandlerTest, LacrosTabSyncStatusTest) {
  // Set up the environment to reflect a "Lacros Only" scenario and recreate the
  // handler under that setting.
  ScopedLacrosOnlyHandle lacros_only_handle;
  InitWithFeatures(
      /*enabled_features=*/{syncer::kChromeOSSyncedSessionSharing},
      /*disabled_features=*/{});
  base::test::ScopedCommandLine command_line;
  command_line.GetProcessCommandLine()->AppendSwitch(
      ash::switches::kEnableLacrosForTesting);
  CreateHandler();

  // Invokes a status change and verifies the expected enabled/disabled value.
  SimulateLacrosTabSyncEnabledChanged(/*is_lacros_tab_sync_enabled=*/false);
  SimulateLacrosTabSyncEnabledChanged(/*is_lacros_tab_sync_enabled=*/true);
}

TEST_F(MultideviceHandlerTest, LogUmaMetricsForSetupFlow) {
  fake_multidevice_feature_access_manager()
      ->SetFeatureSetupRequestSupportedInternal(true);
  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(kDialogIntroActionHistogram, 0);

  base::Value::List set_up_screen_args;
  set_up_screen_args.Append(/*irrelivant_set_up_dialog=*/0);
  set_up_screen_args.Append(/*action_cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectTotalCount(kDialogIntroActionHistogram, 0);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*intro_screen_index=*/1);
  set_up_screen_args.Append(/*learn_more=*/2);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogIntroActionHistogram,
                                     /*learn_more=*/2, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*intro_screen_index=*/1);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogIntroActionHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*intro_screen_index=*/1);
  set_up_screen_args.Append(/*next=*/5);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogIntroActionHistogram,
                                     /*done=*/5, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*finish_on_phone_screen_index=*/2);
  set_up_screen_args.Append(/*learn_more=*/2);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogFinishOnPhoneActionHistogram,
                                     /*learn_more=*/2, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*finish_on_phone_screen_index=*/2);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogFinishOnPhoneActionHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connecting_screen_index=*/3);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogConnectingActionHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connected_screen_index=*/6);
  set_up_screen_args.Append(/*done=*/4);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogSetupFinishedActionHistogram,
                                     /*done=*/4, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connection_error_screen_index=*/4);
  set_up_screen_args.Append(/*try_again=*/5);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogConnectionErrorActionHistogram,
                                     /*try_again=*/5, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connection_error_screen_index=*/4);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogConnectionErrorActionHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connection_time_out_screen_index=*/5);
  set_up_screen_args.Append(/*try_again=*/5);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogConnectionTimeOutActionHistogram,
                                     /*try_again=*/5, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*connection_time_out_screen_index=*/5);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogConnectionTimeOutActionHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*set_a_pin_screen_index=*/7);
  set_up_screen_args.Append(/*cancel=*/3);
  test_web_ui()->HandleReceivedMessage("logPhoneHubPermissionSetUpScreenAction",
                                       set_up_screen_args);
  histogram_tester.ExpectBucketCount(kDialogSetAPinOrPasswordHistogram,
                                     /*cancel=*/3, 1);

  set_up_screen_args.clear();
  set_up_screen_args.Append(/*camera_roll_setup=*/3);
  test_web_ui()->HandleReceivedMessage(
      "logPhoneHubPermissionSetUpButtonClicked", set_up_screen_args);
  histogram_tester.ExpectBucketCount(kSetupButtonInSettingsClikedHistogram,
                                     /*camera_roll_setup=*/3, 1);
}

TEST_F(MultideviceHandlerTest, LogUmaMetricsForIntroScreenSetupMode) {
  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(kDialogIntroScreenSetupModeHistogram, 0);

  base::Value::List set_up_mode_args;
  set_up_mode_args.Append(/*all_permissions=*/7);
  test_web_ui()->HandleReceivedMessage(
      "logPhoneHubPermissionOnboardingSetupMode", set_up_mode_args);
  histogram_tester.ExpectBucketCount(kDialogIntroScreenSetupModeHistogram,
                                     /*all_permissions=*/7, 1);
}

TEST_F(MultideviceHandlerTest, LogUmaMetricsForSetUpFinishedScreenSetupMode) {
  base::HistogramTester histogram_tester;
  histogram_tester.ExpectTotalCount(
      kDialogSetUpFinishedScreenSetupModeHistogram, 0);

  base::Value::List set_up_mode_args;
  set_up_mode_args.Append(/*notification_and_camera_roll=*/5);
  test_web_ui()->HandleReceivedMessage(
      "logPhoneHubPermissionOnboardingSetupResult", set_up_mode_args);
  histogram_tester.ExpectBucketCount(
      kDialogSetUpFinishedScreenSetupModeHistogram,
      /*notification_and_camera_roll=*/5, 1);
}

TEST_F(MultideviceHandlerTest, PageContentData) {
  InitWithFeatures(/* enabled_features */ {ash::features::kPhoneHub,
                                           ash::features::kEcheSWA},
                   /* disabled_features */ {});
  CallGetPageContentData();
  CallGetPageContentData();

  SimulateHostStatusUpdate(
      multidevice_setup::mojom::HostStatus::kEligibleHostExistsButNoHostSet,
      std::nullopt /* host_device */);
  SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus::
                               kHostSetLocallyButWaitingForBackendConfirmation,
                           test_device_);
  SimulateHostStatusUpdate(
      multidevice_setup::mojom::HostStatus::kHostSetButNotYetVerified,
      test_device_);
  SimulateHostStatusUpdate(multidevice_setup::mojom::HostStatus::kHostVerified,
                           test_device_);

  multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
      feature_states_map = GenerateDefaultFeatureStatesMap();
  feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
      multidevice_setup::mojom::FeatureState::kEnabledByUser;
  SimulateFeatureStatesUpdate(feature_states_map);

  feature_states_map[multidevice_setup::mojom::Feature::kBetterTogetherSuite] =
      multidevice_setup::mojom::FeatureState::kDisabledByUser;
  SimulateFeatureStatesUpdate(feature_states_map);

  SimulateNearbyShareEnabledPrefChange(/*is_enabled=*/true,
                                       /*is_managed=*/false);
  SimulateNearbyShareEnabledPrefChange(/*is_enabled=*/true,
                                       /*is_managed=*/true);
  SimulateNearbyShareEnabledPrefChange(/*is_enabled=*/false,
                                       /*is_managed=*/false);
  SimulateNearbyShareEnabledPrefChange(/*is_enabled=*/false,
                                       /*is_managed=*/true);
  SimulateNearbyShareEnabledPrefChange(/*is_enabled=*/false,
                                       /*is_managed=*/true);
  SimulateAppsAccessStatusChanged(/*has_access_been_granted=*/true);
  SimulateCameraRollFilePermissionChanged(/*file_permission_granted=*/false);
  SimulateCameraRollFilePermissionChanged(/*file_permission_granted=*/true);
  SimulateCameraRollAccessstatusChanged(
      /*has_camera_roll_access_been_granted=*/true);
  SimulateCameraRollAccessstatusChanged(
      /*has_camera_roll_access_been_granted=*/false);
}

TEST_F(MultideviceHandlerTest, RetryPendingHostSetup) {
  CallRetryPendingHostSetup(true /* success */);
  CallRetryPendingHostSetup(false /* success */);
}

TEST_F(MultideviceHandlerTest, SetFeatureEnabledState) {
  CallSetFeatureEnabledState(
      multidevice_setup::mojom::Feature::kBetterTogetherSuite,
      true /* enabled */, "authToken" /* auth_token */, true /* success */);
  CallSetFeatureEnabledState(
      multidevice_setup::mojom::Feature::kBetterTogetherSuite,
      false /* enabled */, "authToken" /* auth_token */, false /* success */);
  CallSetFeatureEnabledState(
      multidevice_setup::mojom::Feature::kBetterTogetherSuite,
      false /* enabled */, "authToken" /* auth_token */, true /* success */);
}

TEST_F(MultideviceHandlerTest, RemoveHostDevice) {
  CallRemoveHostDevice();
  CallRemoveHostDevice();
  CallRemoveHostDevice();
}

TEST_F(MultideviceHandlerTest, PageContentDataWhenEcheSWADisabled) {
  InitWithFeatures(
      /* enabled_features */ {ash::features::kPhoneHub},
      /* disabled_features */ {ash::features::kEcheSWA});

  multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
      feature_states_map = GenerateDefaultFeatureStatesMap();

  feature_states_map[multidevice_setup::mojom::Feature::kEche] =
      multidevice_setup::mojom::FeatureState::kProhibitedByPolicy;
  SimulateFeatureStatesUpdate(feature_states_map);
}

TEST_F(MultideviceHandlerTest, PageContentDataWhenPhoneHubCameraRollDisabled) {
  InitWithFeatures(
      /* enabled_features */ {ash::features::kPhoneHub},
      /* disabled_features */ {ash::features::kPhoneHubCameraRoll});

  multidevice_setup::MultiDeviceSetupClient::FeatureStatesMap
      feature_states_map = GenerateDefaultFeatureStatesMap();

  feature_states_map[multidevice_setup::mojom::Feature::kPhoneHubCameraRoll] =
      multidevice_setup::mojom::FeatureState::kProhibitedByPolicy;
  SimulateFeatureStatesUpdate(feature_states_map);
}

TEST_F(MultideviceHandlerTest, EnableScreenLockChanged) {
  InitWithFeatures(/* enabled_features */ {ash::features::kPhoneHub,
                                           ash::features::kEcheSWA},
                   {});
  SetUpHandlerWithEmptyManagers();

  SimulateEnableScreenLockChanged();
}

TEST_F(MultideviceHandlerTest, ScreenLockStatusChanged) {
  InitWithFeatures(/* enabled_features */ {ash::features::kPhoneHub,
                                           ash::features::kEcheSWA},
                   {});
  SetUpHandlerWithEmptyManagers();

  SimulateScreenLockStatusChanged();
}

TEST_F(MultideviceHandlerTest, ShowBrowserSyncSettings) {
  EXPECT_CALL(*new_window_delegate_primary(),
              OpenUrl(GURL("chrome://settings/syncSetup/advanced"),
                      ash::NewWindowDelegate::OpenUrlFrom::kUserInteraction,
                      ash::NewWindowDelegate::Disposition::kSwitchToTab));
  CallHandleShowBrowserSyncSettings();
}

}  // namespace ash::settings