chromium/chrome/browser/nearby_sharing/nearby_sharing_service_impl_unittest.cc

// 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 "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h"

#include <memory>
#include <string>
#include <utility>

#include "base/barrier_closure.h"
#include "base/containers/span.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "base/system/sys_info.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_restrictions.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/download/chrome_download_manager_delegate.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/download/download_core_service_impl.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/nearby_sharing/certificates/fake_nearby_share_certificate_manager.h"
#include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h"
#include "chrome/browser/nearby_sharing/certificates/test_util.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_features.h"
#include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h"
#include "chrome/browser/nearby_sharing/constants.h"
#include "chrome/browser/nearby_sharing/contacts/fake_nearby_share_contact_manager.h"
#include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h"
#include "chrome/browser/nearby_sharing/fast_initiation/fast_initiation_advertiser.h"
#include "chrome/browser/nearby_sharing/fast_initiation/fast_initiation_scanner.h"
#include "chrome/browser/nearby_sharing/local_device_data/fake_nearby_share_local_device_data_manager.h"
#include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h"
#include "chrome/browser/nearby_sharing/nearby_notification_manager.h"
#include "chrome/browser/nearby_sharing/nearby_share_feature_status.h"
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#include "chrome/browser/nearby_sharing/power_client.h"
#include "chrome/browser/nearby_sharing/wifi_network_configuration/fake_wifi_network_configuration_handler.h"
#include "chrome/browser/notifications/notification_display_service_factory.h"
#include "chrome/browser/notifications/notification_display_service_tester.h"
#include "chrome/browser/ui/ash/session/test_session_controller.h"
#include "chrome/services/sharing/nearby/decoder/advertisement_decoder.h"
#include "chrome/services/sharing/public/cpp/advertisement.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "chromeos/ash/components/feature_usage/feature_usage_metrics.h"
#include "chromeos/ash/components/nearby/common/connections_manager/fake_nearby_connection.h"
#include "chromeos/ash/components/nearby/common/connections_manager/fake_nearby_connections_manager.h"
#include "chromeos/ash/components/nearby/common/connections_manager/nearby_connections_manager.h"
#include "chromeos/ash/services/nearby/public/cpp/mock_nearby_process_manager.h"
#include "chromeos/ash/services/nearby/public/cpp/mock_nearby_sharing_decoder.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_connections_types.mojom.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom-shared.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_share_settings.mojom.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/test/browser_task_environment.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/test/mock_bluetooth_adapter.h"
#include "device/bluetooth/test/mock_bluetooth_low_energy_scan_session.h"
#include "net/base/mock_network_change_notifier.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/nearby/sharing/proto/rpc_resources.pb.h"
#include "third_party/nearby/sharing/proto/wire_format.pb.h"

using ::testing::_;
using testing::AtLeast;
using testing::NiceMock;
using testing::Return;

using NetConnectionType = net::NetworkChangeNotifier::ConnectionType;

using SendSurfaceState = NearbySharingService::SendSurfaceState;

using NearbyProcessShutdownReason =
    ash::nearby::NearbyProcessManager::NearbyProcessShutdownReason;

namespace {

class FakeFastInitiationAdvertiser : public FastInitiationAdvertiser {
 public:
  explicit FakeFastInitiationAdvertiser(
      scoped_refptr<device::BluetoothAdapter> adapter,
      bool should_succeed_on_start,
      base::OnceCallback<void()> on_stop_advertising_callback,
      base::OnceCallback<void()> on_destroy_callback)
      : FastInitiationAdvertiser(adapter),
        should_succeed_on_start_(should_succeed_on_start),
        on_stop_advertising_callback_(std::move(on_stop_advertising_callback)),
        on_destroy_callback_(std::move(on_destroy_callback)) {}

  ~FakeFastInitiationAdvertiser() override {
    std::move(on_destroy_callback_).Run();
  }

  void StartAdvertising(FastInitType type,
                        base::OnceCallback<void()> callback,
                        base::OnceCallback<void()> error_callback) override {
    ++start_advertising_call_count_;
    if (should_succeed_on_start_) {
      std::move(callback).Run();
    } else {
      std::move(error_callback).Run();
    }
  }

  void StopAdvertising(base::OnceCallback<void()> callback) override {
    std::move(on_stop_advertising_callback_).Run();
    std::move(callback).Run();
  }

  size_t start_advertising_call_count() {
    return start_advertising_call_count_;
  }

 private:
  bool should_succeed_on_start_;
  size_t start_advertising_call_count_ = 0u;
  base::OnceCallback<void()> on_stop_advertising_callback_;
  base::OnceCallback<void()> on_destroy_callback_;
};

class FakeFastInitiationAdvertiserFactory
    : public FastInitiationAdvertiser::Factory {
 public:
  explicit FakeFastInitiationAdvertiserFactory(bool should_succeed_on_start)
      : should_succeed_on_start_(should_succeed_on_start) {}

  std::unique_ptr<FastInitiationAdvertiser> CreateInstance(
      scoped_refptr<device::BluetoothAdapter> adapter) override {
    auto fake_fast_initiation_advertiser =
        std::make_unique<FakeFastInitiationAdvertiser>(
            adapter, should_succeed_on_start_,
            base::BindOnce(
                &FakeFastInitiationAdvertiserFactory::OnStopAdvertising,
                weak_ptr_factory_.GetWeakPtr()),
            base::BindOnce(&FakeFastInitiationAdvertiserFactory::
                               OnFastInitiationAdvertiserDestroyed,
                           weak_ptr_factory_.GetWeakPtr()));
    last_fake_fast_initiation_advertiser_ =
        fake_fast_initiation_advertiser.get();
    return std::move(fake_fast_initiation_advertiser);
  }

  void OnStopAdvertising() { stop_advertising_called_ = true; }

  void OnFastInitiationAdvertiserDestroyed() {
    fast_initiation_advertiser_destroyed_ = true;
    last_fake_fast_initiation_advertiser_ = nullptr;
  }

  size_t StartAdvertisingCount() {
    return last_fake_fast_initiation_advertiser_
               ? last_fake_fast_initiation_advertiser_
                     ->start_advertising_call_count()
               : 0;
  }

  bool StopAdvertisingCalledAndAdvertiserDestroyed() {
    return stop_advertising_called_ && fast_initiation_advertiser_destroyed_;
  }

 private:
  raw_ptr<FakeFastInitiationAdvertiser> last_fake_fast_initiation_advertiser_ =
      nullptr;
  bool should_succeed_on_start_ = false;
  bool stop_advertising_called_ = false;
  bool fast_initiation_advertiser_destroyed_ = false;
  base::WeakPtrFactory<FakeFastInitiationAdvertiserFactory> weak_ptr_factory_{
      this};
};

class FakeFastInitiationScanner : public FastInitiationScanner {
 public:
  FakeFastInitiationScanner(scoped_refptr<device::BluetoothAdapter> adapter,
                            base::OnceClosure destructor_callback)
      : FastInitiationScanner(adapter),
        destructor_callback_(std::move(destructor_callback)) {}

  ~FakeFastInitiationScanner() override {
    std::move(destructor_callback_).Run();
  }

  void StartScanning(base::RepeatingClosure devices_detected_callback,
                     base::RepeatingClosure devices_not_detected_callback,
                     base::OnceClosure scanner_invalidated_callback) override {
    ++start_scanning_call_count_;
    devices_detected_callback_ = std::move(devices_detected_callback);
    devices_not_detected_callback_ = std::move(devices_not_detected_callback);
    scanner_invalidated_callback_ = std::move(scanner_invalidated_callback);
  }

  void DevicesDetected() { devices_detected_callback_.Run(); }

  void DevicesNotDetected() { devices_not_detected_callback_.Run(); }

  void ScannerInvalidated() { std::move(scanner_invalidated_callback_).Run(); }

 private:
  base::OnceClosure destructor_callback_;
  base::RepeatingClosure devices_detected_callback_;
  base::RepeatingClosure devices_not_detected_callback_;
  base::OnceClosure scanner_invalidated_callback_;
  size_t start_scanning_call_count_ = 0u;
};

class FakeFastInitiationScannerFactory : public FastInitiationScanner::Factory {
 public:
  std::unique_ptr<FastInitiationScanner> CreateInstance(
      scoped_refptr<device::BluetoothAdapter> adapter) override {
    ++scanner_created_count_;
    auto scanner = std::make_unique<FakeFastInitiationScanner>(
        adapter,
        base::BindOnce(&FakeFastInitiationScannerFactory::OnScannerDestroyed,
                       weak_ptr_factory_.GetWeakPtr()));
    last_fake_fast_initiation_scanner_ = scanner.get();
    return std::move(scanner);
  }

  bool IsHardwareSupportAvailable() override {
    return is_hardware_support_available_;
  }

  void SetHardwareSupportAvailable(bool is_hardware_support_available) {
    is_hardware_support_available_ = is_hardware_support_available;
  }

  FakeFastInitiationScanner* last_fake_fast_initiation_scanner() {
    return last_fake_fast_initiation_scanner_;
  }
  size_t scanner_created_count() { return scanner_created_count_; }
  size_t scanner_destroyed_count() { return scanner_destroyed_count_; }

 private:
  void OnScannerDestroyed() { ++scanner_destroyed_count_; }

  raw_ptr<FakeFastInitiationScanner, DanglingUntriaged>
      last_fake_fast_initiation_scanner_ = nullptr;
  size_t scanner_created_count_ = 0u;
  size_t scanner_destroyed_count_ = 0u;
  bool is_hardware_support_available_ = true;

  base::WeakPtrFactory<FakeFastInitiationScannerFactory> weak_ptr_factory_{
      this};
};

class MockTransferUpdateCallback : public TransferUpdateCallback {
 public:
  ~MockTransferUpdateCallback() override = default;

  MOCK_METHOD(void,
              OnTransferUpdate,
              (const ShareTarget& shareTarget,
               const TransferMetadata& transferMetadata),
              (override));
};

class MockShareTargetDiscoveredCallback : public ShareTargetDiscoveredCallback {
 public:
  ~MockShareTargetDiscoveredCallback() override = default;

  MOCK_METHOD(void,
              OnShareTargetDiscovered,
              (ShareTarget shareTarget),
              (override));
  MOCK_METHOD(void, OnShareTargetLost, (ShareTarget shareTarget), (override));
};

class FakePowerClient : public PowerClient {
 public:
  // Make SetSuspended() public for testing.
  using PowerClient::SetSuspended;
};

class FakeArcNearbyShareSession {
 public:
  void OnCleanupCallbackStub() { callback_called = true; }
  bool CleanupCallbackCalled() { return callback_called; }

 private:
  bool callback_called = false;
};

}  // namespace

namespace NearbySharingServiceUnitTests {

constexpr base::TimeDelta kDelta = base::Milliseconds(100);

const char kProfileName[] = "profile_name";
const char kServiceId[] = "NearbySharing";
const char kDeviceName[] = "test_device_name";
const nearby_share::mojom::ShareTargetType kDeviceType =
    nearby_share::mojom::ShareTargetType::kPhone;
const char kEndpointId[] = "test_endpoint_id";
const char kTextPayload[] = "Test text payload";
const char kSsid[] = "Test_SSID";
const char kWifiPassword[] = "test_password";
const sharing::mojom::WifiCredentialsMetadata::SecurityType kWifiSecurityType =
    sharing::mojom::WifiCredentialsMetadata::SecurityType::kWpaPsk;

constexpr int64_t kFreeDiskSpace = 10000;

const std::vector<uint8_t> kValidV1EndpointInfo = {
    0, 0, 0, 0,  0,   0,   0,   0,   0,  0,   0,  0,  0,   0,
    0, 0, 0, 10, 100, 101, 118, 105, 99, 101, 78, 97, 109, 101};

const std::vector<uint8_t> kToken = {0, 1, 2};
const char kFourDigitToken[] = "1953";

const std::vector<uint8_t> kPrivateCertificateHashAuthToken = {
    0x8b, 0xcb, 0xa2, 0xf8, 0xe4, 0x06};
const std::vector<uint8_t> kIncomingConnectionSignedData = {
    0x30, 0x45, 0x02, 0x20, 0x4f, 0x83, 0x72, 0xbd, 0x02, 0x70, 0xd9, 0xda,
    0x62, 0x83, 0x5d, 0xb2, 0xdc, 0x6e, 0x3f, 0xa6, 0xa8, 0xa1, 0x4f, 0x5f,
    0xd3, 0xe3, 0xd9, 0x1a, 0x5d, 0x2d, 0x61, 0xd2, 0x6c, 0xdd, 0x8d, 0xa5,
    0x02, 0x21, 0x00, 0xd4, 0xe1, 0x1d, 0x14, 0xcb, 0x58, 0xf7, 0x02, 0xd5,
    0xab, 0x48, 0xe2, 0x2f, 0xcb, 0xc0, 0x53, 0x41, 0x06, 0x50, 0x65, 0x95,
    0x19, 0xa9, 0x22, 0x92, 0x00, 0x42, 0x01, 0x26, 0x25, 0xcb, 0x8c};
const std::vector<uint8_t> kOutgoingConnectionSignedData = {
    0x30, 0x45, 0x02, 0x21, 0x00, 0xf9, 0xc9, 0xa8, 0x89, 0x96, 0x6e, 0x5c,
    0xea, 0x0a, 0x60, 0x37, 0x3a, 0x84, 0x7d, 0xf5, 0x31, 0x82, 0x74, 0xb9,
    0xde, 0x3f, 0x64, 0x1b, 0xff, 0x4f, 0x54, 0x31, 0x1f, 0x9e, 0x63, 0x68,
    0xca, 0x02, 0x20, 0x52, 0x43, 0x46, 0xa7, 0x6f, 0xcb, 0x96, 0x50, 0x86,
    0xfd, 0x6f, 0x9f, 0x7e, 0x50, 0xa7, 0xa0, 0x9b, 0xdf, 0xae, 0x79, 0x42,
    0x47, 0xd9, 0x60, 0x71, 0x91, 0x7a, 0xbb, 0x81, 0x9b, 0x0d, 0x2e};

constexpr int kFilePayloadId = 111;
constexpr int kPayloadSize = 1;
constexpr int kWifiCredentialsId = 222;
constexpr int kWifiCredentialsPayloadId = 333;

const std::vector<int64_t> kValidIntroductionFramePayloadIds = {
    1, 2, 3, kFilePayloadId, kWifiCredentialsPayloadId};

constexpr size_t kMaxCertificateDownloadsDuringDiscovery = 3u;
constexpr base::TimeDelta kCertificateDownloadDuringDiscoveryPeriod =
    base::Seconds(10);

// We will run tests with the following feature flags enabled and disabled in
// all permutations. To add or a remove a feature you can just update this list.
const std::vector<base::test::FeatureRef> kTestFeatures = {};

bool FileExists(const base::FilePath& file_path) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  return base::PathExists(file_path);
}

nearby::connections::mojom::PayloadPtr GetFilePayloadPtr(int64_t payload_id) {
  base::ScopedAllowBlockingForTesting allow_blocking;
  base::FilePath path;
  base::CreateTemporaryFile(&path);
  base::File file(path, base::File::Flags::FLAG_CREATE_ALWAYS |
                            base::File::Flags::FLAG_READ |
                            base::File::Flags::FLAG_WRITE);

  return nearby::connections::mojom::Payload::New(
      payload_id,
      nearby::connections::mojom::PayloadContent::NewFile(
          nearby::connections::mojom::FilePayload::New(std::move(file))));
}

nearby::connections::mojom::PayloadPtr GetTextPayloadPtr(
    int64_t payload_id,
    const std::string& text) {
  return nearby::connections::mojom::Payload::New(
      payload_id, nearby::connections::mojom::PayloadContent::NewBytes(
                      nearby::connections::mojom::BytesPayload::New(
                          std::vector<uint8_t>(text.begin(), text.end()))));
}

nearby::connections::mojom::PayloadPtr GetWifiPayloadPtr(
    int64_t payload_id,
    const std::string& password) {
  nearby::sharing::service::proto::WifiCredentials credentials_proto;
  credentials_proto.set_password(password);
  const std::string& proto_string = credentials_proto.SerializeAsString();
  return nearby::connections::mojom::Payload::New(
      payload_id,
      nearby::connections::mojom::PayloadContent::NewBytes(
          nearby::connections::mojom::BytesPayload::New(
              std::vector<uint8_t>(proto_string.begin(), proto_string.end()))));
}

sharing::mojom::FramePtr GetIntroductionFrame(
    int text_metadata_count,
    int file_metadata_count,
    int wifi_credentials_metadata_count) {
  std::vector<sharing::mojom::TextMetadataPtr> mojo_text_metadatas;
  for (int i = 1; i <= text_metadata_count; i++) {
    mojo_text_metadatas.push_back(sharing::mojom::TextMetadata::New(
        "title " + base::NumberToString(i),
        static_cast<sharing::mojom::TextMetadata::Type>(i), /*payload_id=*/i,
        kPayloadSize, /*id=*/i));
  }

  std::vector<sharing::mojom::FileMetadataPtr> mojo_file_metadatas;
  for (int i = 1; i <= file_metadata_count; i++) {
    mojo_file_metadatas.push_back(sharing::mojom::FileMetadata::New(
        "unit_test_nearby_share_name_\x80",  // Filename contains non-ascii
                                             // char.
        sharing::mojom::FileMetadata::Type::kVideo, kFilePayloadId,
        kPayloadSize, "mime type", /*id=*/100));
  }

  std::vector<sharing::mojom::WifiCredentialsMetadataPtr>
      mojo_wifi_credentials_metadatas;
  for (int i = 1; i <= wifi_credentials_metadata_count; i++) {
    mojo_wifi_credentials_metadatas.push_back(
        sharing::mojom::WifiCredentialsMetadata::New(kSsid, kWifiSecurityType,
                                                     kWifiCredentialsPayloadId,
                                                     kWifiCredentialsId));
  }

  sharing::mojom::V1FramePtr mojo_v1frame =
      sharing::mojom::V1Frame::NewIntroduction(
          sharing::mojom::IntroductionFrame::New(
              std::move(mojo_file_metadatas), std::move(mojo_text_metadatas),
              /*required_package=*/std::nullopt,
              std::move(mojo_wifi_credentials_metadatas)));

  sharing::mojom::FramePtr mojo_frame =
      sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));
  return mojo_frame;
}

sharing::mojom::FramePtr GetConnectionResponseFrame(
    sharing::mojom::ConnectionResponseFrame::Status status) {
  sharing::mojom::V1FramePtr mojo_v1frame =
      sharing::mojom::V1Frame::NewConnectionResponse(
          sharing::mojom::ConnectionResponseFrame::New(status));

  sharing::mojom::FramePtr mojo_frame =
      sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));
  return mojo_frame;
}

sharing::mojom::FramePtr GetCancelFrame() {
  sharing::mojom::V1FramePtr mojo_v1frame =
      sharing::mojom::V1Frame::NewCancelFrame(
          sharing::mojom::CancelFrame::New());

  sharing::mojom::FramePtr mojo_frame =
      sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));
  return mojo_frame;
}

std::vector<std::unique_ptr<Attachment>> CreateTextAttachments(
    std::vector<std::string> texts) {
  std::vector<std::unique_ptr<Attachment>> attachments;
  for (auto& text : texts) {
    attachments.push_back(std::make_unique<TextAttachment>(
        TextAttachment::Type::kText, std::move(text), /*title=*/std::nullopt,
        /*mime_type=*/std::nullopt));
  }
  return attachments;
}

std::vector<std::unique_ptr<Attachment>> CreateFileAttachments(
    std::vector<base::FilePath> file_paths) {
  std::vector<std::unique_ptr<Attachment>> attachments;
  for (auto& file_path : file_paths) {
    attachments.push_back(
        std::make_unique<FileAttachment>(std::move(file_path)));
  }
  return attachments;
}

class MockBluetoothAdapterWithIntervals : public device::MockBluetoothAdapter {
 public:
  MOCK_METHOD2(OnSetAdvertisingInterval, void(int64_t, int64_t));

  void SetAdvertisingInterval(
      const base::TimeDelta& min,
      const base::TimeDelta& max,
      base::OnceClosure callback,
      AdvertisementErrorCallback error_callback) override {
    std::move(callback).Run();
    OnSetAdvertisingInterval(min.InMilliseconds(), max.InMilliseconds());
  }

 protected:
  ~MockBluetoothAdapterWithIntervals() override = default;
};

class NearbySharingServiceImplTestBase : public testing::Test {
 public:
  explicit NearbySharingServiceImplTestBase(size_t feature_mask)
      : task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
    CreateFeatureList(feature_mask);
    RegisterNearbySharingPrefs(prefs_.registry());
  }

  ~NearbySharingServiceImplTestBase() override = default;

  void SetUp() override {
    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
    ASSERT_TRUE(profile_manager_.SetUp());
    network_notifier_ = net::test::MockNetworkChangeNotifier::Create();

    NearbyShareLocalDeviceDataManagerImpl::Factory::SetFactoryForTesting(
        &local_device_data_manager_factory_);
    NearbyShareContactManagerImpl::Factory::SetFactoryForTesting(
        &contact_manager_factory_);
    NearbyShareCertificateManagerImpl::Factory::SetFactoryForTesting(
        &certificate_manager_factory_);

    mock_bluetooth_adapter_ =
        base::MakeRefCounted<NiceMock<MockBluetoothAdapterWithIntervals>>();
    ON_CALL(*mock_bluetooth_adapter_, IsPresent())
        .WillByDefault(Invoke(
            this, &NearbySharingServiceImplTestBase::IsBluetoothPresent));
    ON_CALL(*mock_bluetooth_adapter_, IsPowered())
        .WillByDefault(Invoke(
            this, &NearbySharingServiceImplTestBase::IsBluetoothPowered));
    ON_CALL(*mock_bluetooth_adapter_, AddObserver(_))
        .WillByDefault(Invoke(
            this, &NearbySharingServiceImplTestBase::AddAdapterObserver));
    ON_CALL(*mock_bluetooth_adapter_, OnSetAdvertisingInterval(_, _))
        .WillByDefault(Invoke(
            this, &NearbySharingServiceImplTestBase::OnSetAdvertisingInterval));
    ON_CALL(*mock_bluetooth_adapter_, StartLowEnergyScanSession(_, _))
        .WillByDefault(Invoke(
            this,
            &NearbySharingServiceImplTestBase::StartLowEnergyScanSession));
    device::BluetoothAdapterFactory::SetAdapterForTesting(
        mock_bluetooth_adapter_);

    session_controller_ = std::make_unique<TestSessionController>();

    EXPECT_CALL(mock_nearby_process_manager(), GetNearbyProcessReference)
        .WillRepeatedly(
            [&](ash::nearby::NearbyProcessManager::NearbyProcessStoppedCallback
                    process_stopped_callback) {
              process_stopped_callback_ = std::move(process_stopped_callback);
              auto mock_reference_ptr =
                  std::make_unique<ash::nearby::MockNearbyProcessManager::
                                       MockNearbyProcessReference>();

              EXPECT_CALL(*(mock_reference_ptr.get()), GetNearbySharingDecoder)
                  .WillRepeatedly(
                      testing::ReturnRef(mock_decoder_.shared_remote()));

              return mock_reference_ptr;
            });

    SetFakeFastInitiationScannerFactory();
    service_ = CreateService();
    SetFakeFastInitiationAdvertiserFactory(/*should_succeed_on_start=*/true);

    // Visibility timer starts on |service_| creation because default visibility
    // is 'Your Devices'.
    EXPECT_TRUE(IsVisibilityReminderTimerRunning());

    service_->set_free_disk_space_for_testing(kFreeDiskSpace);

    // From now on we don't allow any blocking tasks anymore.
    disallow_blocking_ = std::make_unique<base::ScopedDisallowBlocking>();
  }

  void TearDown() override {
    if (service_) {
      service_->Shutdown();
    }

    if (profile_) {
      DownloadCoreServiceFactory::GetForBrowserContext(profile_)
          ->SetDownloadManagerDelegateForTesting(nullptr);
      profile_ = nullptr;
    }

    profile_manager_.DeleteAllTestingProfiles();

    NearbyShareLocalDeviceDataManagerImpl::Factory::SetFactoryForTesting(
        nullptr);
    NearbyShareContactManagerImpl::Factory::SetFactoryForTesting(nullptr);
    NearbyShareCertificateManagerImpl::Factory::SetFactoryForTesting(nullptr);
    FastInitiationAdvertiser::Factory::SetFactoryForTesting(nullptr);
  }

  void SetManagedEnabled(bool is_enabled) {
    prefs_.SetManagedPref(prefs::kNearbySharingEnabledPrefName,
                          std::make_unique<base::Value>(is_enabled));
    ASSERT_TRUE(
        prefs_.IsManagedPreference(prefs::kNearbySharingEnabledPrefName));
  }

  std::unique_ptr<NearbySharingServiceImpl> CreateService() {
    NearbySharingServiceFactory::
        SetIsNearbyShareSupportedForBrowserContextForTesting(true);

    profile_ = profile_manager_.CreateTestingProfile(kProfileName);
    prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, true);

    fake_nearby_connections_manager_ = new FakeNearbyConnectionsManager();
    notification_tester_ =
        std::make_unique<NotificationDisplayServiceTester>(profile_);
    NotificationDisplayService* notification_display_service =
        NotificationDisplayServiceFactory::GetForProfile(profile_);
    auto power_client = std::make_unique<FakePowerClient>();
    power_client_ = power_client.get();
    auto wifi_network_handler =
        std::make_unique<FakeWifiNetworkConfigurationHandler>();
    wifi_network_handler_ = wifi_network_handler.get();
    auto service = std::make_unique<NearbySharingServiceImpl>(
        &prefs_, notification_display_service, profile_,
        base::WrapUnique(fake_nearby_connections_manager_.get()),
        &mock_nearby_process_manager_, std::move(power_client),
        std::move(wifi_network_handler));

    DownloadCoreServiceFactory::GetForBrowserContext(profile_)
        ->SetDownloadManagerDelegateForTesting(
            std::make_unique<ChromeDownloadManagerDelegate>(profile_));

    // Allow the posted tasks to fetch the BluetoothAdapter and set the default
    // device name to finish.
    task_environment_.RunUntilIdle();

    return service;
  }

  void SetVisibility(nearby_share::mojom::Visibility visibility) {
    NearbyShareSettings settings(&prefs_, local_device_data_manager());
    settings.SetVisibility(visibility);

    // This ensures that the change propagates through mojo and the observers
    // are called.
    base::RunLoop().RunUntilIdle();
  }

  nearby_share::mojom::Visibility GetVisibility() {
    NearbyShareSettings settings(&prefs_, local_device_data_manager());
    return settings.GetVisibility();
  }

  void SetIsEnabled(bool is_enabled) {
    NearbyShareSettings settings(&prefs_, local_device_data_manager());
    if (is_enabled) {
      settings.SetIsOnboardingComplete(is_enabled);
    }
    settings.SetEnabled(is_enabled);

    // This ensures that the change propagates through mojo and the observers
    // are called.
    base::RunLoop().RunUntilIdle();
  }

  void SetFastInitiationNotificationState(
      nearby_share::mojom::FastInitiationNotificationState state) {
    NearbyShareSettings settings(&prefs_, local_device_data_manager());
    settings.SetFastInitiationNotificationState(state);

    // This ensures that the change propagates through mojo and the observers
    // are called.
    base::RunLoop().RunUntilIdle();
  }

  void SetFakeFastInitiationAdvertiserFactory(bool should_succeed_on_start) {
    fast_initiation_advertiser_factory_ =
        std::make_unique<FakeFastInitiationAdvertiserFactory>(
            should_succeed_on_start);
    FastInitiationAdvertiser::Factory::SetFactoryForTesting(
        fast_initiation_advertiser_factory_.get());
  }

  void SetFakeFastInitiationScannerFactory() {
    fast_initiation_scanner_factory_ =
        std::make_unique<FakeFastInitiationScannerFactory>();
    FastInitiationScanner::Factory::SetFactoryForTesting(
        fast_initiation_scanner_factory_.get());
  }

  bool IsBluetoothPresent() { return is_bluetooth_present_; }
  bool IsBluetoothPowered() { return is_bluetooth_powered_; }

  void SetBluetoothIsPresent(bool present) {
    is_bluetooth_present_ = present;
    adapter_observer_->AdapterPresentChanged(mock_bluetooth_adapter_.get(),
                                             present);
  }

  void SetBluetoothIsPowered(bool powered) {
    is_bluetooth_powered_ = powered;
    adapter_observer_->AdapterPoweredChanged(mock_bluetooth_adapter_.get(),
                                             powered);
  }

  void SetHardwareSupportState(
      device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
          state) {
    hardware_support_state_ = state;
    adapter_observer_->LowEnergyScanSessionHardwareOffloadingStatusChanged(
        state);
  }

  void AddAdapterObserver(device::BluetoothAdapter::Observer* observer) {
    DCHECK(!adapter_observer_);
    adapter_observer_ = observer;
  }

  void OnSetAdvertisingInterval(int64_t min, int64_t max) {
    ++set_advertising_interval_call_count_;
    last_advertising_interval_min_ = min;
    last_advertising_interval_max_ = max;
  }

  std::unique_ptr<device::BluetoothLowEnergyScanSession>
  StartLowEnergyScanSession(
      std::unique_ptr<device::BluetoothLowEnergyScanFilter> filter,
      base::WeakPtr<device::BluetoothLowEnergyScanSession::Delegate> delegate) {
    auto mock_scan_session =
        std::make_unique<device::MockBluetoothLowEnergyScanSession>(
            base::BindOnce(
                &NearbySharingServiceImplTestBase::OnScanSessionDestroyed,
                weak_ptr_factory_.GetWeakPtr()));
    mock_scan_session_ = mock_scan_session.get();
    return mock_scan_session;
  }

  void OnScanSessionDestroyed() { mock_scan_session_ = nullptr; }

  void SetConnectionType(net::NetworkChangeNotifier::ConnectionType type) {
    network_notifier_->SetConnectionType(type);
    network_notifier_->NotifyObserversOfNetworkChangeForTests(
        network_notifier_->GetConnectionType());
  }

  NiceMock<ash::nearby::MockNearbyProcessManager>&
  mock_nearby_process_manager() {
    return mock_nearby_process_manager_;
  }

  void SetUpForegroundReceiveSurface(
      NiceMock<MockTransferUpdateCallback>& callback) {
    NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
        &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
    EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
    EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  }

  void ProcessLatestPublicCertificateDecryption(size_t expected_num_calls,
                                                bool success,
                                                bool for_self_share = false) {
    // Ensure that all pending mojo messages are processed and the certificate
    // manager state is as expected up to this point.
    base::RunLoop().RunUntilIdle();
    std::vector<
        FakeNearbyShareCertificateManager::GetDecryptedPublicCertificateCall>&
        calls = certificate_manager()->get_decrypted_public_certificate_calls();

    ASSERT_FALSE(calls.empty());
    EXPECT_EQ(expected_num_calls, calls.size());
    EXPECT_EQ(GetNearbyShareTestEncryptedMetadataKey().salt(),
              calls.back().encrypted_metadata_key.salt());
    EXPECT_EQ(GetNearbyShareTestEncryptedMetadataKey().encrypted_key(),
              calls.back().encrypted_metadata_key.encrypted_key());

    if (success) {
      nearby::sharing::proto::PublicCertificate cert =
          GetNearbyShareTestPublicCertificate(
              nearby_share::mojom::Visibility::kAllContacts);
      cert.set_for_self_share(for_self_share);
      std::move(calls.back().callback)
          .Run(NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
              cert, GetNearbyShareTestEncryptedMetadataKey()));
    } else {
      std::move(calls.back().callback).Run(std::nullopt);
    }
  }

  void SetUpKeyVerification(
      bool is_incoming,
      sharing::mojom::PairedKeyResultFrame::Status status) {
    SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
    local_device_data_manager()->SetDeviceName(kDeviceName);

    std::string encryption_frame = "test_encryption_frame";
    std::vector<uint8_t> encryption_bytes(encryption_frame.begin(),
                                          encryption_frame.end());
    EXPECT_CALL(mock_decoder_,
                DecodeFrame(testing::Eq(encryption_bytes), testing::_))
        .WillOnce(testing::Invoke(
            [is_incoming](
                const std::vector<uint8_t>& data,
                ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                    callback) {
              sharing::mojom::V1FramePtr mojo_v1frame =
                  sharing::mojom::V1Frame::NewPairedKeyEncryption(
                      sharing::mojom::PairedKeyEncryptionFrame::New(
                          is_incoming ? kIncomingConnectionSignedData
                                      : kOutgoingConnectionSignedData,
                          kPrivateCertificateHashAuthToken, std::nullopt));
              sharing::mojom::FramePtr mojo_frame =
                  sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));
              std::move(callback).Run(std::move(mojo_frame));
            }));
    connection_.AppendReadableData(encryption_bytes);

    std::string encryption_result = "test_encryption_result";
    std::vector<uint8_t> result_bytes(encryption_result.begin(),
                                      encryption_result.end());
    EXPECT_CALL(mock_decoder_,
                DecodeFrame(testing::Eq(result_bytes), testing::_))
        .WillOnce(testing::Invoke(
            [=](const std::vector<uint8_t>& data,
                ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                    callback) {
              sharing::mojom::V1FramePtr mojo_v1frame =
                  sharing::mojom::V1Frame::NewPairedKeyResult(
                      sharing::mojom::PairedKeyResultFrame::New(status));

              sharing::mojom::FramePtr mojo_frame =
                  sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));
              std::move(callback).Run(std::move(mojo_frame));
            }));
    connection_.AppendReadableData(result_bytes);
  }

  void SetUpAdvertisementDecoder(const std::vector<uint8_t>& endpoint_info,
                                 bool return_empty_advertisement,
                                 bool return_empty_device_name,
                                 size_t expected_number_of_calls) {
    EXPECT_CALL(mock_decoder_,
                DecodeAdvertisement(testing::Eq(endpoint_info), testing::_))
        .Times(expected_number_of_calls)
        .WillRepeatedly(
            testing::Invoke([=](const std::vector<uint8_t>& data,
                                ash::nearby::MockNearbySharingDecoder::
                                    DecodeAdvertisementCallback callback) {
              if (return_empty_advertisement) {
                std::move(callback).Run(nullptr);
                return;
              }

              std::optional<std::string> device_name;
              if (!return_empty_device_name) {
                device_name = kDeviceName;
              }

              sharing::mojom::AdvertisementPtr advertisement =
                  sharing::mojom::Advertisement::New(
                      GetNearbyShareTestEncryptedMetadataKey().salt(),
                      GetNearbyShareTestEncryptedMetadataKey().encrypted_key(),
                      kDeviceType, device_name);
              std::move(callback).Run(std::move(advertisement));
            }));
  }

  // By default, set up the decoder to return an introduction frame with every
  // attachment type.
  void SetUpIntroductionFrameDecoder(int text_metadata_count = 3,
                                     int file_metadata_count = 1,
                                     int wifi_credentials_metadata_count = 1) {
    std::string intro = "introduction_frame";
    std::vector<uint8_t> bytes(intro.begin(), intro.end());
    EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
        .WillOnce(testing::Invoke(
            [=](const std::vector<uint8_t>& data,
                ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                    callback) {
              std::move(callback).Run(
                  GetIntroductionFrame(text_metadata_count, file_metadata_count,
                                       wifi_credentials_metadata_count));
            }));
    connection_.AppendReadableData(bytes);
  }

  void SendConnectionResponse(
      sharing::mojom::ConnectionResponseFrame::Status status) {
    std::string intro = "connection_result_frame";
    std::vector<uint8_t> bytes(intro.begin(), intro.end());
    EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
        .WillOnce(testing::Invoke(
            [=](const std::vector<uint8_t>& data,
                ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                    callback) {
              std::move(callback).Run(GetConnectionResponseFrame(status));
            }));
    connection_.AppendReadableData(bytes);
  }

  void SendCancel() {
    std::string data = "cancel_frame";
    std::vector<uint8_t> bytes(data.begin(), data.end());
    EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
        .WillOnce(testing::Invoke(
            [=](const std::vector<uint8_t>& data,
                ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                    callback) { std::move(callback).Run(GetCancelFrame()); }));
    connection_.AppendReadableData(bytes);
  }

  ShareTarget SetUpIncomingConnection(
      NiceMock<MockTransferUpdateCallback>& callback,
      bool for_self_share = false,
      int wifi_credentials_metadata_count = 1) {
    fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                                kToken);
    SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                              /*return_empty_advertisement=*/false,
                              /*return_empty_device_name=*/false,
                              /*expected_number_of_calls=*/1u);
    SetUpIntroductionFrameDecoder(
        /*text_metadata_count=*/3,
        /*file_metadata_count=*/1,
        /*wifi_credentials_metadata_count=*/wifi_credentials_metadata_count);

    ShareTarget share_target;
    SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
    base::RunLoop run_loop;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke([&](const ShareTarget& incoming_share_target,
                                      TransferMetadata metadata) {
          EXPECT_FALSE(metadata.is_final_status());
          EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
                    metadata.status());
          share_target = incoming_share_target;
          run_loop.Quit();
        }));

    SetUpKeyVerification(/*is_incoming=*/true,
                         sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
    SetUpForegroundReceiveSurface(callback);
    service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                           &connection_);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                             /*success=*/true, for_self_share);
    run_loop.Run();

    EXPECT_TRUE(
        fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));
    EXPECT_EQ((uint8_t)wifi_credentials_metadata_count,
              share_target.wifi_credentials_attachments.size());

    return share_target;
  }

  ShareTarget SetUpOutgoingShareTarget(
      MockTransferUpdateCallback& transfer_callback,
      MockShareTargetDiscoveredCallback& discovery_callback) {
    SetUpKeyVerification(/*is_incoming=*/false,
                         sharing::mojom::PairedKeyResultFrame_Status::kSuccess);

    fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                                kToken);
    fake_nearby_connections_manager_->set_nearby_connection(&connection_);

    return DiscoverShareTarget(transfer_callback, discovery_callback);
  }

  ShareTarget DiscoverShareTarget(
      MockTransferUpdateCallback& transfer_callback,
      MockShareTargetDiscoveredCallback& discovery_callback) {
    SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

    // Ensure decoder parses a valid endpoint advertisement.
    SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                              /*return_empty_advertisement=*/false,
                              /*return_empty_device_name=*/false,
                              /*expected_number_of_calls=*/1u);

    // Start discovering, to ensure a discovery listener is registered.
    base::RunLoop run_loop;
    EXPECT_EQ(
        NearbySharingService::StatusCodes::kOk,
        service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                      SendSurfaceState::kForeground));
    EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

    ShareTarget discovered_target;
    // Discover a new endpoint, with fields set up a valid certificate.
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
        .WillOnce([&run_loop, &discovered_target](ShareTarget share_target) {
          discovered_target = share_target;
          run_loop.Quit();
        });
    fake_nearby_connections_manager_->OnEndpointFound(
        kEndpointId, nearby::connections::mojom::DiscoveredEndpointInfo::New(
                         kValidV1EndpointInfo, kServiceId));
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                             /*success=*/true);
    run_loop.Run();
    return discovered_target;
  }

  std::optional<ShareTarget> CreateShareTarget(
      const sharing::mojom::AdvertisementPtr& advertisement,
      std::optional<NearbyShareDecryptedPublicCertificate> certificate) {
    return service_->CreateShareTarget(kEndpointId, advertisement, certificate,
                                       /*is_incoming=*/true);
  }

  nearby::sharing::service::proto::Frame GetWrittenFrame() {
    std::vector<uint8_t> data = connection_.GetWrittenData();
    nearby::sharing::service::proto::Frame frame;
    frame.ParseFromArray(data.data(), data.size());
    return frame;
  }

  void ExpectPairedKeyEncryptionFrame() {
    nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
    ASSERT_TRUE(frame.has_v1());
    ASSERT_TRUE(frame.v1().has_paired_key_encryption());
  }

  void ExpectPairedKeyResultFrame() {
    nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
    ASSERT_TRUE(frame.has_v1());
    ASSERT_TRUE(frame.v1().has_paired_key_result());
  }

  void ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::Status status) {
    nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
    ASSERT_TRUE(frame.has_v1());
    ASSERT_TRUE(frame.v1().has_connection_response());
    EXPECT_EQ(status, frame.v1().connection_response().status());
  }

  nearby::sharing::service::proto::IntroductionFrame ExpectIntroductionFrame() {
    nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
    EXPECT_TRUE(frame.has_v1());
    EXPECT_TRUE(frame.v1().has_introduction());
    return frame.v1().introduction();
  }

  void ExpectCancelFrame() {
    nearby::sharing::service::proto::Frame frame = GetWrittenFrame();
    ASSERT_TRUE(frame.has_v1());
    EXPECT_EQ(nearby::sharing::service::proto::V1Frame::CANCEL,
              frame.v1().type());
  }

  // Optionally, |new_share_target| is updated with the ShareTargets sent to
  // OnTransferUpdate() calls.
  void ExpectTransferUpdates(
      MockTransferUpdateCallback& transfer_callback,
      const ShareTarget& target,
      const std::vector<TransferMetadata::Status>& updates,
      base::OnceClosure callback,
      ShareTarget* new_share_target = nullptr) {
    auto barrier = base::BarrierClosure(updates.size(), std::move(callback));
    auto& expectation =
        EXPECT_CALL(transfer_callback, OnTransferUpdate).Times(updates.size());

    for (TransferMetadata::Status status : updates) {
      expectation.WillOnce(testing::Invoke([=, this](
                                               const ShareTarget& share_target,
                                               TransferMetadata metadata) {
        EXPECT_EQ(target.id, share_target.id);
        EXPECT_EQ(status, metadata.status());
        if (new_share_target) {
          *new_share_target = share_target;
        }

        // Though this is indirect, verify that the highest level
        // success/failure metric was logged. We expect transfer updates to
        // be a few indeterminate status then only one success or failure
        // status.
        TransferMetadata::Result result = TransferMetadata::ToResult(status);
        histogram_tester_.ExpectBucketCount(
            "ChromeOS.FeatureUsage.NearbyShare",
            ash::feature_usage::FeatureUsageMetrics::Event::kUsedWithSuccess,
            result == TransferMetadata::Result::kSuccess ? 1 : 0);
        histogram_tester_.ExpectBucketCount(
            "ChromeOS.FeatureUsage.NearbyShare",
            ash::feature_usage::FeatureUsageMetrics::Event::kUsedWithFailure,
            result == TransferMetadata::Result::kFailure ? 1 : 0);

        barrier.Run();
      }));
    }
  }

  // Returns the modified ShareTarget received from a TransferUpdate.
  ShareTarget SetUpOutgoingConnectionUntilAccept(
      MockTransferUpdateCallback& transfer_callback,
      const ShareTarget& target) {
    ShareTarget new_share_target;
    base::RunLoop introduction_run_loop;
    ExpectTransferUpdates(transfer_callback, target,
                          {TransferMetadata::Status::kConnecting,
                           TransferMetadata::Status::kAwaitingLocalConfirmation,
                           TransferMetadata::Status::kAwaitingRemoteAcceptance},
                          introduction_run_loop.QuitClosure(),
                          &new_share_target);

    EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
              service_->SendAttachments(target,
                                        CreateTextAttachments({kTextPayload})));
    introduction_run_loop.Run();

    // Verify data sent to the remote device so far.
    ExpectPairedKeyEncryptionFrame();
    ExpectPairedKeyResultFrame();
    ExpectIntroductionFrame();

    return new_share_target;
  }

  struct PayloadInfo {
    int64_t payload_id;
    base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener;
  };

  PayloadInfo AcceptAndSendPayload(
      MockTransferUpdateCallback& transfer_callback,
      const ShareTarget& target) {
    PayloadInfo info = {};
    base::RunLoop payload_run_loop;
    fake_nearby_connections_manager_->set_send_payload_callback(
        base::BindLambdaForTesting(
            [&](NearbyConnectionsManager::PayloadPtr payload,
                base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener>
                    listener) {
              ASSERT_TRUE(payload->content->is_bytes());
              std::vector<uint8_t> bytes = payload->content->get_bytes()->bytes;
              EXPECT_EQ(kTextPayload, std::string(bytes.begin(), bytes.end()));
              info.payload_id = payload->id;
              info.listener = listener;
              payload_run_loop.Quit();
            }));

    // We're now waiting for the remote device to respond with the accept
    // result.
    base::RunLoop accept_run_loop;
    ExpectTransferUpdates(transfer_callback, target,
                          {TransferMetadata::Status::kInProgress},
                          accept_run_loop.QuitClosure());

    // Kick off send process by accepting the transfer from the remote device.
    SendConnectionResponse(
        sharing::mojom::ConnectionResponseFrame::Status::kAccept);

    accept_run_loop.Run();
    payload_run_loop.Run();
    return info;
  }

  void FinishOutgoingTransfer(MockTransferUpdateCallback& transfer_callback,
                              const ShareTarget& target,
                              const PayloadInfo& info) {
    // Simulate a successful transfer via Nearby Connections.
    base::RunLoop success_run_loop;
    ExpectTransferUpdates(transfer_callback, target,
                          {TransferMetadata::Status::kComplete},
                          success_run_loop.QuitClosure());
    ASSERT_TRUE(info.listener);
    info.listener->OnStatusUpdate(
        nearby::connections::mojom::PayloadTransferUpdate::New(
            info.payload_id,
            nearby::connections::mojom::PayloadStatus::kSuccess,
            /*total_bytes=*/strlen(kTextPayload),
            /*bytes_transferred=*/strlen(kTextPayload)),
        /*upgraded_medium=*/std::nullopt);
    success_run_loop.Run();
  }

  std::unique_ptr<sharing::Advertisement> GetCurrentAdvertisement() {
    auto endpoint_info =
        fake_nearby_connections_manager_->advertising_endpoint_info();
    if (!endpoint_info) {
      return nullptr;
    }

    return sharing::AdvertisementDecoder::FromEndpointInfo(base::make_span(
        *fake_nearby_connections_manager_->advertising_endpoint_info()));
  }

  void FindEndpoint(const std::string& endpoint_id) {
    fake_nearby_connections_manager_->OnEndpointFound(
        endpoint_id, nearby::connections::mojom::DiscoveredEndpointInfo::New(
                         kValidV1EndpointInfo, kServiceId));
  }

  void LoseEndpoint(const std::string& endpoint_id) {
    fake_nearby_connections_manager_->OnEndpointLost(endpoint_id);
  }

  // This method sets up an incoming connection and performs the steps required
  // to simulate a successful incoming transfer.
  void SuccessfullyReceiveTransfer() {
    for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
      fake_nearby_connections_manager_->SetPayloadPathStatus(
          payload_id, nearby::connections::mojom::Status::kSuccess);
    }

    NiceMock<MockTransferUpdateCallback> callback;
    ShareTarget share_target = SetUpIncomingConnection(callback);

    base::RunLoop run_loop_accept;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke(
            [](const ShareTarget& share_target, TransferMetadata metadata) {
              EXPECT_FALSE(metadata.is_final_status());
              EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                        metadata.status());
            }));

    service_->Accept(
        share_target,
        base::BindLambdaForTesting(
            [&](NearbySharingServiceImpl::StatusCodes status_code) {
              EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                        status_code);
              run_loop_accept.Quit();
            }));

    run_loop_accept.Run();

    fake_nearby_connections_manager_->SetIncomingPayload(
        kFilePayloadId, GetFilePayloadPtr(kFilePayloadId));

    for (int64_t id : kValidIntroductionFramePayloadIds) {
      // Update file payload at the end.
      if (id == kFilePayloadId) {
        continue;
      }

      if (id != kWifiCredentialsPayloadId) {
        fake_nearby_connections_manager_->SetIncomingPayload(
            id, GetTextPayloadPtr(id, kTextPayload));
      }

      if (id == kWifiCredentialsPayloadId) {
        fake_nearby_connections_manager_->SetIncomingPayload(
            id, GetWifiPayloadPtr(id, kWifiPassword));
      }

      base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
          fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
              id);
      ASSERT_TRUE(listener);

      base::RunLoop run_loop_progress;
      EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
          .WillOnce(testing::Invoke([&](const ShareTarget& share_target,
                                        TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kInProgress, metadata.status());
            run_loop_progress.Quit();
          }));

      nearby::connections::mojom::PayloadTransferUpdatePtr payload =
          nearby::connections::mojom::PayloadTransferUpdate::New(
              id, nearby::connections::mojom::PayloadStatus::kSuccess,
              /*total_bytes=*/kPayloadSize,
              /*bytes_transferred=*/kPayloadSize);
      listener->OnStatusUpdate(std::move(payload),
                               /*upgraded_medium=*/std::nullopt);
      run_loop_progress.Run();

      task_environment_.FastForwardBy(kMinProgressUpdateFrequency);
    }

    base::FilePath file_path;
    base::RunLoop run_loop_success;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke([&](const ShareTarget& share_target,
                                      TransferMetadata metadata) {
          EXPECT_TRUE(metadata.is_final_status());
          EXPECT_EQ(metadata.progress(), 100);
          EXPECT_EQ(TransferMetadata::Status::kComplete, metadata.status());

          ASSERT_TRUE(share_target.has_attachments());
          EXPECT_EQ(1u, share_target.file_attachments.size());
          for (const FileAttachment& file : share_target.file_attachments) {
            EXPECT_TRUE(file.file_path());
            file_path = *file.file_path();
          }

          EXPECT_EQ(3u, share_target.text_attachments.size());
          for (const TextAttachment& text : share_target.text_attachments) {
            EXPECT_EQ(kTextPayload, text.text_body());
          }

          EXPECT_EQ(1u, share_target.wifi_credentials_attachments.size());
          for (const WifiCredentialsAttachment& wifi_credentials :
               share_target.wifi_credentials_attachments) {
            EXPECT_EQ(kSsid, wifi_credentials.ssid());
            EXPECT_EQ(kWifiPassword, wifi_credentials.wifi_password());
            EXPECT_EQ(kWifiSecurityType, wifi_credentials.security_type());
          }

          EXPECT_EQ(1u, wifi_network_handler_->num_configure_network_calls());
          EXPECT_EQ(kSsid, wifi_network_handler_->last_attachment().ssid());
          EXPECT_EQ(kWifiPassword,
                    wifi_network_handler_->last_attachment().wifi_password());
          EXPECT_EQ(kWifiSecurityType,
                    wifi_network_handler_->last_attachment().security_type());

          run_loop_success.Quit();
        }));

    base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
        fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
            kFilePayloadId);
    ASSERT_TRUE(listener);

    nearby::connections::mojom::PayloadTransferUpdatePtr payload =
        nearby::connections::mojom::PayloadTransferUpdate::New(
            kFilePayloadId, nearby::connections::mojom::PayloadStatus::kSuccess,
            /*total_bytes=*/kPayloadSize,
            /*bytes_transferred=*/kPayloadSize);
    listener->OnStatusUpdate(std::move(payload),
                             /*upgraded_medium=*/std::nullopt);
    run_loop_success.Run();

    EXPECT_FALSE(fake_nearby_connections_manager_->connection_endpoint_info(
        kEndpointId));
    EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads());

    // TODO(crbug.com/40716484): This check is flaky, should be investigated.
    // EXPECT_TRUE(FileExists(file_path));

    // To avoid UAF in OnIncomingTransferUpdate().
    service_->UnregisterReceiveSurface(&callback);

    // Remove test file.
    {
      base::ScopedAllowBlockingForTesting allow_blocking;
      base::DeleteFile(file_path);
    }
  }

  void ReceiveBadWifiPayload(
      nearby::connections::mojom::PayloadPtr wifi_payload) {
    for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
      fake_nearby_connections_manager_->SetPayloadPathStatus(
          payload_id, nearby::connections::mojom::Status::kSuccess);
    }

    NiceMock<MockTransferUpdateCallback> callback;
    ShareTarget share_target = SetUpIncomingConnection(callback);

    base::RunLoop run_loop_accept;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke(
            [](const ShareTarget& share_target, TransferMetadata metadata) {
              EXPECT_FALSE(metadata.is_final_status());
              EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                        metadata.status());
            }));

    service_->Accept(
        share_target,
        base::BindLambdaForTesting(
            [&](NearbySharingServiceImpl::StatusCodes status_code) {
              EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                        status_code);
              run_loop_accept.Quit();
            }));

    run_loop_accept.Run();

    fake_nearby_connections_manager_->SetIncomingPayload(
        kFilePayloadId, GetFilePayloadPtr(kFilePayloadId));

    for (int64_t id : kValidIntroductionFramePayloadIds) {
      if (id == kFilePayloadId) {
        continue;
      }

      if (id != kWifiCredentialsPayloadId) {
        fake_nearby_connections_manager_->SetIncomingPayload(
            id, GetTextPayloadPtr(id, kTextPayload));
      } else {
        fake_nearby_connections_manager_->SetIncomingPayload(
            id, std::move(wifi_payload));
      }

      base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
          fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
              id);
      ASSERT_TRUE(listener);

      base::RunLoop run_loop_progress;
      EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
          .WillOnce(testing::Invoke([&](const ShareTarget& share_target,
                                        TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kInProgress, metadata.status());
            run_loop_progress.Quit();
          }));

      nearby::connections::mojom::PayloadTransferUpdatePtr payload =
          nearby::connections::mojom::PayloadTransferUpdate::New(
              id, nearby::connections::mojom::PayloadStatus::kSuccess,
              /*total_bytes=*/kPayloadSize,
              /*bytes_transferred=*/kPayloadSize);
      listener->OnStatusUpdate(std::move(payload),
                               /*upgraded_medium=*/std::nullopt);
      run_loop_progress.Run();

      task_environment_.FastForwardBy(kMinProgressUpdateFrequency);
    }

    base::RunLoop run_loop_success;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke([&](const ShareTarget& share_target,
                                      TransferMetadata metadata) {
          EXPECT_TRUE(metadata.is_final_status());
          EXPECT_LT(metadata.progress(), 100);
          EXPECT_EQ(TransferMetadata::Status::kIncompletePayloads,
                    metadata.status());

          ASSERT_TRUE(share_target.has_attachments());
          EXPECT_EQ(0u, wifi_network_handler_->num_configure_network_calls());

          run_loop_success.Quit();
        }));

    base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
        fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
            kFilePayloadId);
    ASSERT_TRUE(listener);

    nearby::connections::mojom::PayloadTransferUpdatePtr payload =
        nearby::connections::mojom::PayloadTransferUpdate::New(
            kFilePayloadId, nearby::connections::mojom::PayloadStatus::kSuccess,
            /*total_bytes=*/kPayloadSize,
            /*bytes_transferred=*/kPayloadSize);
    listener->OnStatusUpdate(std::move(payload),
                             /*upgraded_medium=*/std::nullopt);

    run_loop_success.Run();

    EXPECT_FALSE(fake_nearby_connections_manager_->connection_endpoint_info(
        kEndpointId));
    EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads());

    // To avoid UAF in OnIncomingTransferUpdate().
    service_->UnregisterReceiveSurface(&callback);
  }

 protected:
  FakeNearbyShareLocalDeviceDataManager* local_device_data_manager() {
    EXPECT_EQ(1u, local_device_data_manager_factory_.instances().size());
    return local_device_data_manager_factory_.instances().back();
  }

  FakeNearbyShareContactManager* contact_manager() {
    EXPECT_EQ(1u, contact_manager_factory_.instances().size());
    return contact_manager_factory_.instances().back();
  }

  FakeNearbyShareCertificateManager* certificate_manager() {
    EXPECT_EQ(1u, certificate_manager_factory_.instances().size());
    return certificate_manager_factory_.instances().back();
  }

  base::FilePath CreateTestFile(const std::string& name,
                                const std::vector<uint8_t>& content) {
    base::ScopedAllowBlockingForTesting allow_blocking;
    base::FilePath path = temp_dir_.GetPath().Append(name);
    base::File file(path, base::File::Flags::FLAG_CREATE_ALWAYS |
                              base::File::Flags::FLAG_READ |
                              base::File::Flags::FLAG_WRITE);
    EXPECT_TRUE(file.WriteAndCheck(
        /*offset=*/0, base::make_span(content)));
    EXPECT_TRUE(file.Flush());
    file.Close();
    return path;
  }

  void CreateFeatureList(size_t feature_mask) {
    std::vector<base::test::FeatureRef> enabled_features;
    std::vector<base::test::FeatureRef> disabled_features;

    // Use |feature_mask| as a bitmask to decide which features in
    // |kTestFeatures| to enable or disable.
    for (size_t i = 0; i < kTestFeatures.size(); i++) {
      if (feature_mask & 1 << i) {
        enabled_features.push_back(kTestFeatures[i]);
      } else {
        disabled_features.push_back(kTestFeatures[i]);
      }
    }

    scoped_feature_list_.InitWithFeatures(enabled_features, disabled_features);
  }

  size_t set_advertising_interval_call_count() {
    return set_advertising_interval_call_count_;
  }

  int64_t last_advertising_interval_min() {
    return last_advertising_interval_min_;
  }

  int64_t last_advertising_interval_max() {
    return last_advertising_interval_max_;
  }

  bool IsProcessShutdownTimerRunning() {
    return service_->process_shutdown_pending_timer_.IsRunning();
  }

  void FireProcessShutdownIfRunning() {
    if (IsProcessShutdownTimerRunning()) {
      service_->process_shutdown_pending_timer_.FireNow();
    }
  }

  bool IsBoundToProcess() { return service_->process_reference_ != nullptr; }

  void SetRecentNearbyProcessShutdownCount(int count) {
    service_->recent_nearby_process_unexpected_shutdown_count_ = count;
  }

  bool IsVisibilityReminderTimerRunning() {
    return service_->visibility_reminder_timer_.IsRunning();
  }

  void ShutdownNearbyProcess(NearbyProcessShutdownReason shutdown_reason) {
    fake_nearby_connections_manager_->CleanupForProcessStopped();
    std::move(process_stopped_callback_).Run(shutdown_reason);
  }

  base::test::ScopedFeatureList scoped_feature_list_;
  base::ScopedTempDir temp_dir_;
  // We need to ensure that |network_notifier_| is created and destroyed after
  // |task_environment_| to avoid UAF issues when using
  // ChromeDownloadManagerDelegate.
  std::unique_ptr<net::test::MockNetworkChangeNotifier> network_notifier_;
  content::BrowserTaskEnvironment task_environment_;
  TestingProfileManager profile_manager_{TestingBrowserProcess::GetGlobal()};
  raw_ptr<Profile> profile_ = nullptr;
  sync_preferences::TestingPrefServiceSyncable prefs_;
  raw_ptr<FakeNearbyConnectionsManager, DanglingUntriaged>
      fake_nearby_connections_manager_ = nullptr;
  raw_ptr<FakePowerClient, DanglingUntriaged> power_client_ = nullptr;
  raw_ptr<FakeWifiNetworkConfigurationHandler, DanglingUntriaged>
      wifi_network_handler_ = nullptr;
  FakeNearbyShareLocalDeviceDataManager::Factory
      local_device_data_manager_factory_;
  FakeNearbyShareContactManager::Factory contact_manager_factory_;
  FakeNearbyShareCertificateManager::Factory certificate_manager_factory_;
  std::unique_ptr<NotificationDisplayServiceTester> notification_tester_;
  NiceMock<ash::nearby::MockNearbyProcessManager> mock_nearby_process_manager_;
  std::unique_ptr<TestSessionController> session_controller_;
  std::unique_ptr<NearbySharingServiceImpl> service_;
  std::unique_ptr<base::ScopedDisallowBlocking> disallow_blocking_;
  std::unique_ptr<FakeFastInitiationAdvertiserFactory>
      fast_initiation_advertiser_factory_;
  std::unique_ptr<FakeFastInitiationScannerFactory>
      fast_initiation_scanner_factory_;
  bool is_bluetooth_present_ = true;
  bool is_bluetooth_powered_ = true;
  device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus
      hardware_support_state_;
  raw_ptr<device::BluetoothAdapter::Observer, DanglingUntriaged>
      adapter_observer_ = nullptr;
  scoped_refptr<NiceMock<MockBluetoothAdapterWithIntervals>>
      mock_bluetooth_adapter_;
  raw_ptr<device::MockBluetoothLowEnergyScanSession> mock_scan_session_ =
      nullptr;
  NiceMock<ash::nearby::MockNearbySharingDecoder> mock_decoder_;
  FakeNearbyConnection connection_;
  size_t set_advertising_interval_call_count_ = 0u;
  int64_t last_advertising_interval_min_ = 0;
  int64_t last_advertising_interval_max_ = 0;
  ash::nearby::NearbyProcessManager::NearbyProcessStoppedCallback
      process_stopped_callback_;
  base::HistogramTester histogram_tester_;

  base::WeakPtrFactory<NearbySharingServiceImplTestBase> weak_ptr_factory_{
      this};
};

// We parameterize these tests to run them with upcoming features enabled and
// disabled.
class NearbySharingServiceImplTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<size_t> {
 public:
  NearbySharingServiceImplTest()
      : NearbySharingServiceImplTestBase(/*feature_mask=*/GetParam()) {}
};

struct ValidSendSurfaceTestDataInternal {
  bool bluetooth_enabled;
  net::NetworkChangeNotifier::ConnectionType connection_type;
} kValidSendSurfaceTestData[] = {
    // No network connection, only bluetooth available
    {true, net::NetworkChangeNotifier::CONNECTION_NONE},
    // Wifi available
    {true, net::NetworkChangeNotifier::CONNECTION_WIFI},
    // Ethernet available
    {true, net::NetworkChangeNotifier::CONNECTION_ETHERNET},
    // 3G available
    {true, net::NetworkChangeNotifier::CONNECTION_3G}};

// size_t parameter is |feature_mask|.
using ValidSendSurfaceTestData =
    std::tuple<ValidSendSurfaceTestDataInternal, size_t>;

class NearbySharingServiceImplValidSendTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<ValidSendSurfaceTestData> {
 public:
  NearbySharingServiceImplValidSendTest()
      : NearbySharingServiceImplTestBase(
            /*feature_mask=*/std::get<1>(GetParam())) {}
};

struct InvalidSendSurfaceTestDataInternal {
  bool screen_locked;
  bool bluetooth_enabled;
  net::NetworkChangeNotifier::ConnectionType connection_type;
} kInvalidSendSurfaceTestData[] = {
    // Screen locked
    {/*screen_locked=*/true, true, net::NetworkChangeNotifier::CONNECTION_WIFI},
    // No network connection and no bluetooth
    {/*screen_locked=*/false, false,
     net::NetworkChangeNotifier::CONNECTION_NONE},
    // 3G available and no bluetooth
    {/*screen_locked=*/false, false, net::NetworkChangeNotifier::CONNECTION_3G},
    // Wifi available and no bluetooth (invalid until WiFi LAN is supported)
    {/*screen_locked=*/false, false,
     net::NetworkChangeNotifier::CONNECTION_WIFI},
    // Ethernet available and no bluetooth (invalid until WiFi LAN is supported)
    {/*screen_locked=*/false, false,
     net::NetworkChangeNotifier::CONNECTION_ETHERNET}};

// size_t parameter is |feature_mask|.
using InvalidSendSurfaceTestData =
    std::tuple<InvalidSendSurfaceTestDataInternal, size_t>;

class NearbySharingServiceImplInvalidSendTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<InvalidSendSurfaceTestData> {
 public:
  NearbySharingServiceImplInvalidSendTest()
      : NearbySharingServiceImplTestBase(
            /*feature_mask==*/std::get<1>(GetParam())) {}
};

using ResponseFrameStatus = sharing::mojom::ConnectionResponseFrame::Status;

struct SendFailureTestDataInternal {
  ResponseFrameStatus response_status;
  TransferMetadata::Status expected_status;
} kSendFailureTestData[] = {
    {ResponseFrameStatus::kReject, TransferMetadata::Status::kRejected},
    {ResponseFrameStatus::kNotEnoughSpace,
     TransferMetadata::Status::kNotEnoughSpace},
    {ResponseFrameStatus::kUnsupportedAttachmentType,
     TransferMetadata::Status::kUnsupportedAttachmentType},
    {ResponseFrameStatus::kTimedOut, TransferMetadata::Status::kTimedOut},
    {ResponseFrameStatus::kUnknown, TransferMetadata::Status::kFailed},
};

// size_t parameter is |feature_mask|.
using SendFailureTestData = std::tuple<SendFailureTestDataInternal, size_t>;

class NearbySharingServiceImplSendFailureTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<SendFailureTestData> {
 public:
  NearbySharingServiceImplSendFailureTest()
      : NearbySharingServiceImplTestBase(
            /*feature_mask=*/std::get<1>(GetParam())) {}
};

class TestObserver : public NearbySharingService::Observer {
 public:
  explicit TestObserver(NearbySharingService* service) : service_(service) {
    service_->AddObserver(this);
  }

  void OnHighVisibilityChanged(bool in_high_visibility) override {
    in_high_visibility_ = in_high_visibility;
  }

  void OnNearbyProcessStopped() override { process_stopped_called_ = true; }

  void OnStartAdvertisingFailure() override {
    on_start_advertising_failure_called_ = true;
  }

  void OnFastInitiationDevicesDetected() override {
    devices_detected_called_ = true;
  }
  void OnFastInitiationDevicesNotDetected() override {
    devices_not_detected_called_ = true;
  }
  void OnFastInitiationScanningStopped() override {
    scanning_stopped_called_ = true;
  }

  void OnShutdown() override {
    shutdown_called_ = true;
    service_->RemoveObserver(this);
  }

  bool in_high_visibility_ = false;
  bool shutdown_called_ = false;
  bool process_stopped_called_ = false;
  bool on_start_advertising_failure_called_ = false;
  bool devices_detected_called_ = false;
  bool devices_not_detected_called_ = false;
  bool scanning_stopped_called_ = false;
  raw_ptr<NearbySharingService, DanglingUntriaged> service_;
};

TEST_P(NearbySharingServiceImplTest, DisableNearbyShutdownConnections) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
  service_->FlushMojoForTesting();
  EXPECT_TRUE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest, StartFastInitiationAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_EQ(1u, fast_initiation_advertiser_factory_->StartAdvertisingCount());

  // Call RegisterSendSurface a second time and make sure StartAdvertising is
  // not called again.
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kError,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_EQ(1u, fast_initiation_advertiser_factory_->StartAdvertisingCount());
}

TEST_P(NearbySharingServiceImplTest, StartFastInitiationAdvertisingError) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetFakeFastInitiationAdvertiserFactory(/*should_succeed_on_start=*/false);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundStartFastInitiationAdvertisingError) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kBackground));
  EXPECT_EQ(0u, fast_initiation_advertiser_factory_->StartAdvertisingCount());
}

TEST_P(NearbySharingServiceImplTest,
       StartFastInitiationAdvertising_BluetoothNotPresent) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetBluetoothIsPresent(false);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kNoAvailableConnectionMedium,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
}

TEST_P(NearbySharingServiceImplTest,
       StartFastInitiationAdvertising_BluetoothNotPowered) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  is_bluetooth_powered_ = false;
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kNoAvailableConnectionMedium,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
}

TEST_P(NearbySharingServiceImplTest, StopFastInitiationAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_EQ(1u, fast_initiation_advertiser_factory_->StartAdvertisingCount());
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->UnregisterSendSurface(&transfer_callback, &discovery_callback));
  EXPECT_TRUE(fast_initiation_advertiser_factory_
                  ->StopAdvertisingCalledAndAdvertiserDestroyed());
}

TEST_P(NearbySharingServiceImplTest,
       StopFastInitiationAdvertising_BluetoothBecomesNotPresent) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  SetBluetoothIsPresent(false);
  EXPECT_TRUE(fast_initiation_advertiser_factory_
                  ->StopAdvertisingCalledAndAdvertiserDestroyed());
}

TEST_P(NearbySharingServiceImplTest,
       StopFastInitiationAdvertising_BluetoothBecomesNotPowered) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  SetBluetoothIsPowered(false);
  EXPECT_TRUE(fast_initiation_advertiser_factory_
                  ->StopAdvertisingCalledAndAdvertiserDestroyed());
}

TEST_P(NearbySharingServiceImplTest, RegisterSendSurface_BluetoothNotPresent) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE);
  SetBluetoothIsPresent(false);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kNoAvailableConnectionMedium,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest, RegisterSendSurface_BluetoothNotPowered) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_NONE);
  is_bluetooth_powered_ = false;
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kNoAvailableConnectionMedium,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundRegisterSendSurfaceStartsDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundRegisterSendSurfaceTwiceKeepsDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kError,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       BluetoothBecomesNotPresentStopDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  SetBluetoothIsPresent(false);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  SetBluetoothIsPresent(true);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       BluetoothBecomesNotPoweredStopDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  SetBluetoothIsPowered(false);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  SetBluetoothIsPowered(true);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       RegisterSendSurfaceAlreadyReceivingNotDiscovering) {
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);
  EXPECT_FALSE(connection_.IsClosed());

  MockTransferUpdateCallback send_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(NearbySharingService::StatusCodes::kTransferAlreadyInProgress,
            service_->RegisterSendSurface(&send_callback, &discovery_callback,
                                          SendSurfaceState::kForeground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundRegisterSendSurfaceNotDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kBackground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       DifferentSurfaceRegisterSendSurfaceTwiceKeepsDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kError,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kBackground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       RegisterSendSurfaceEndpointFoundDiscoveryCallbackNotified) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  // Ensure decoder parses a valid endpoint advertisement.
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  // Start discovering, to ensure a discovery listener is registered.
  base::RunLoop run_loop;
  MockTransferUpdateCallback transfer_callback;
  NiceMock<MockShareTargetDiscoveredCallback> discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  // Discover a new endpoint, with fields set up a valid certificate.
  EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
      .WillOnce([&run_loop](ShareTarget share_target) {
        EXPECT_FALSE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_FALSE(share_target.has_attachments());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);
        run_loop.Quit();
      });
  fake_nearby_connections_manager_->OnEndpointFound(
      kEndpointId, nearby::connections::mojom::DiscoveredEndpointInfo::New(
                       kValidV1EndpointInfo, kServiceId));
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  // Register another send surface, which will automatically catch up discovered
  // endpoints.
  base::RunLoop run_loop2;
  MockTransferUpdateCallback transfer_callback2;
  NiceMock<MockShareTargetDiscoveredCallback> discovery_callback2;
  EXPECT_CALL(discovery_callback2, OnShareTargetDiscovered)
      .WillOnce([&run_loop2](ShareTarget share_target) {
        EXPECT_EQ(kDeviceName, share_target.device_name);
        run_loop2.Quit();
      });

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback2, &discovery_callback2,
                                    SendSurfaceState::kForeground));
  run_loop2.Run();

  // Shut down the service while the discovery callbacks are still in scope.
  // OnShareTargetLost() will be invoked during shutdown.
  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest, RegisterSendSurfaceEmptyCertificate) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  // Ensure decoder parses a valid endpoint advertisement.
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  // Start discovering, to ensure a discovery listener is registered.
  base::RunLoop run_loop;
  MockTransferUpdateCallback transfer_callback;
  NiceMock<MockShareTargetDiscoveredCallback> discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  // Discover a new endpoint, with fields set up a valid certificate.
  EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
      .WillOnce([&run_loop](ShareTarget share_target) {
        EXPECT_FALSE(share_target.is_incoming);
        EXPECT_FALSE(share_target.is_known);
        EXPECT_FALSE(share_target.has_attachments());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_FALSE(share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_EQ(kEndpointId, share_target.device_id);
        EXPECT_FALSE(share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);
        run_loop.Quit();
      });
  fake_nearby_connections_manager_->OnEndpointFound(
      kEndpointId, nearby::connections::mojom::DiscoveredEndpointInfo::New(
                       kValidV1EndpointInfo, kServiceId));
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/false);
  run_loop.Run();

  // Register another send surface, which will automatically catch up discovered
  // endpoints.
  base::RunLoop run_loop2;
  MockTransferUpdateCallback transfer_callback2;
  NiceMock<MockShareTargetDiscoveredCallback> discovery_callback2;
  EXPECT_CALL(discovery_callback2, OnShareTargetDiscovered)
      .WillOnce([&run_loop2](ShareTarget share_target) {
        EXPECT_EQ(kDeviceName, share_target.device_name);
        run_loop2.Quit();
      });

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback2, &discovery_callback2,
                                    SendSurfaceState::kForeground));
  run_loop2.Run();

  // Shut down the service while the discovery callbacks are still in scope.
  // OnShareTargetLost() will be invoked during shutdown.
  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplValidSendTest,
       RegisterSendSurfaceIsDiscovering) {
  is_bluetooth_present_ = std::get<0>(GetParam()).bluetooth_enabled;
  SetConnectionType(std::get<0>(GetParam()).connection_type);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

INSTANTIATE_TEST_SUITE_P(
    NearbySharingServiceImplTest,
    NearbySharingServiceImplValidSendTest,
    testing::Combine(testing::ValuesIn(kValidSendSurfaceTestData),
                     testing::Range<size_t>(0, 1 << kTestFeatures.size())));

TEST_P(NearbySharingServiceImplInvalidSendTest,
       RegisterSendSurfaceNotDiscovering) {
  session_controller_->SetScreenLocked(std::get<0>(GetParam()).screen_locked);
  is_bluetooth_present_ = std::get<0>(GetParam()).bluetooth_enabled;
  SetConnectionType(std::get<0>(GetParam()).connection_type);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());

  NearbySharingService::StatusCodes expected_status =
      is_bluetooth_present_
          ? NearbySharingService::StatusCodes::kOk
          : NearbySharingService::StatusCodes::kNoAvailableConnectionMedium;

  EXPECT_EQ(expected_status, service_->RegisterSendSurface(
                                 &transfer_callback, &discovery_callback,
                                 SendSurfaceState::kForeground));

  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

INSTANTIATE_TEST_SUITE_P(
    NearbySharingServiceImplTest,
    NearbySharingServiceImplInvalidSendTest,
    testing::Combine(testing::ValuesIn(kInvalidSendSurfaceTestData),
                     testing::Range<size_t>(0, 1 << kTestFeatures.size())));

TEST_P(NearbySharingServiceImplTest, DisableFeatureSendSurfaceNotDiscovering) {
  prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
  service_->FlushMojoForTesting();
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_TRUE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       DisableFeatureSendSurfaceStopsDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
  service_->FlushMojoForTesting();
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_TRUE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest, UnregisterSendSurfaceStopsDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->UnregisterSendSurface(&transfer_callback, &discovery_callback));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       UnregisterSendSurfaceDifferentCallbackKeepDiscovering) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  MockTransferUpdateCallback transfer_callback2;
  MockShareTargetDiscoveredCallback discovery_callback2;
  EXPECT_EQ(NearbySharingService::StatusCodes::kError,
            service_->UnregisterSendSurface(&transfer_callback2,
                                            &discovery_callback2));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest, UnregisterSendSurfaceNeverRegistered) {
  // Set visibility to Hidden to stop advertising.
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kError,
      service_->UnregisterSendSurface(&transfer_callback, &discovery_callback));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_TRUE(IsBoundToProcess());
  FireProcessShutdownIfRunning();
  EXPECT_FALSE(IsBoundToProcess());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundRegisterReceiveSurfaceIsAdvertisingAllContacts) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  local_device_data_manager()->SetDeviceName(kDeviceName);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_EQ(NearbyConnectionsManager::PowerLevel::kHighPower,
            fake_nearby_connections_manager_->advertising_power_level());
  ASSERT_TRUE(fake_nearby_connections_manager_->advertising_endpoint_info());
  auto advertisement = GetCurrentAdvertisement();
  ASSERT_TRUE(advertisement);
  EXPECT_EQ(kDeviceName, advertisement->device_name());
  EXPECT_EQ(nearby_share::mojom::ShareTargetType::kLaptop,
            advertisement->device_type());
  auto& test_metadata_key = GetNearbyShareTestEncryptedMetadataKey();
  EXPECT_EQ(test_metadata_key.salt(), advertisement->salt());
  EXPECT_EQ(test_metadata_key.encrypted_key(),
            advertisement->encrypted_metadata_key());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundRegisterReceiveSurfaceIsAdvertisingNoOne) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  local_device_data_manager()->SetDeviceName(kDeviceName);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_EQ(NearbyConnectionsManager::PowerLevel::kHighPower,
            fake_nearby_connections_manager_->advertising_power_level());
  ASSERT_TRUE(fake_nearby_connections_manager_->advertising_endpoint_info());
  auto advertisement = GetCurrentAdvertisement();
  ASSERT_TRUE(advertisement);
  EXPECT_EQ(kDeviceName, advertisement->device_name());
  EXPECT_EQ(nearby_share::mojom::ShareTargetType::kLaptop,
            advertisement->device_type());
  // Expecting random metadata key.
  EXPECT_EQ(static_cast<size_t>(sharing::Advertisement::kSaltSize),
            advertisement->salt().size());
  EXPECT_EQ(static_cast<size_t>(
                sharing::Advertisement::kMetadataEncryptionKeyHashByteSize),
            advertisement->encrypted_metadata_key().size());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundRegisterReceiveSurfaceIsAdvertisingSelectedContacts) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetVisibility(nearby_share::mojom::Visibility::kSelectedContacts);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_EQ(NearbyConnectionsManager::PowerLevel::kLowPower,
            fake_nearby_connections_manager_->advertising_power_level());
  ASSERT_TRUE(fake_nearby_connections_manager_->advertising_endpoint_info());
  auto advertisement = GetCurrentAdvertisement();
  ASSERT_TRUE(advertisement);
  EXPECT_FALSE(advertisement->device_name());
  EXPECT_EQ(nearby_share::mojom::ShareTargetType::kLaptop,
            advertisement->device_type());
  auto& test_metadata_key = GetNearbyShareTestEncryptedMetadataKey();
  EXPECT_EQ(test_metadata_key.salt(), advertisement->salt());
  EXPECT_EQ(test_metadata_key.encrypted_key(),
            advertisement->encrypted_metadata_key());
}

TEST_P(NearbySharingServiceImplTest,
       RegisterReceiveSurfaceTwiceSameCallbackKeepAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  NearbySharingService::StatusCodes result2 = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result2, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       RegisterReceiveSurfaceTwiceKeepAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  MockTransferUpdateCallback callback2;
  NearbySharingService::StatusCodes result2 = service_->RegisterReceiveSurface(
      &callback2, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result2, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       ScreenLockedRegisterReceiveSurfaceNotAdvertising) {
  session_controller_->SetScreenLocked(true);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest, ScreenLocksDuringAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());

  session_controller_->SetScreenLocked(true);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());

  session_controller_->SetScreenLocked(false);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest, ScreenLocksDuringDiscovery) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  session_controller_->SetScreenLocked(true);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  session_controller_->SetScreenLocked(false);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       SuspendedRegisterReceiveSurfaceNotAdvertising) {
  power_client_->SetSuspended(true);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest, SuspendDuringAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  power_client_->SetSuspended(true);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  power_client_->SetSuspended(false);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
}

TEST_P(NearbySharingServiceImplTest,
       DataUsageChangedRegisterReceiveSurfaceRestartsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  prefs_.SetInteger(prefs::kNearbySharingDataUsageName,
                    static_cast<int>(nearby_share::mojom::DataUsage::kOffline));
  service_->FlushMojoForTesting();
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_EQ(nearby_share::mojom::DataUsage::kOffline,
            fake_nearby_connections_manager_->advertising_data_usage());

  prefs_.SetInteger(prefs::kNearbySharingDataUsageName,
                    static_cast<int>(nearby_share::mojom::DataUsage::kOnline));
  service_->FlushMojoForTesting();
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_EQ(nearby_share::mojom::DataUsage::kOnline,
            fake_nearby_connections_manager_->advertising_data_usage());
}

TEST_P(
    NearbySharingServiceImplTest,
    UnregisterForegroundReceiveSurfaceVisibilityAllContactsRestartAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kAllContacts));
  service_->FlushMojoForTesting();

  // Register both foreground and background receive surfaces
  MockTransferUpdateCallback background_transfer_callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &background_transfer_callback,
      NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  MockTransferUpdateCallback foreground_transfer_callback;
  result = service_->RegisterReceiveSurface(
      &foreground_transfer_callback,
      NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  // Unregister the foreground surface. Advertising is stopped and restarted
  // with low power. The service reports InHighVisibility until the
  // StopAdvertising callback is called.
  FakeNearbyConnectionsManager::ConnectionsCallback stop_advertising_callback =
      fake_nearby_connections_manager_->GetStopAdvertisingCallback();
  FakeNearbyConnectionsManager::ConnectionsCallback start_advertising_callback =
      fake_nearby_connections_manager_->GetStartAdvertisingCallback();
  result = service_->UnregisterReceiveSurface(&foreground_transfer_callback);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_TRUE(service_->IsInHighVisibility());

  std::move(stop_advertising_callback)
      .Run(NearbyConnectionsManager::ConnectionsStatus::kSuccess);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(service_->IsInHighVisibility());

  std::move(start_advertising_callback)
      .Run(NearbyConnectionsManager::ConnectionsStatus::kSuccess);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(service_->IsInHighVisibility());
}

TEST_P(NearbySharingServiceImplTest,
       NoNetworkRegisterReceiveSurfaceIsAdvertising) {
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  // Succeeds since bluetooth is present.
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       NoBluetoothNoNetworkRegisterForegroundReceiveSurfaceNotAdvertising) {
  SetBluetoothIsPresent(false);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kNoAvailableConnectionMedium);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       NoBluetoothNoNetworkRegisterBackgroundReceiveSurfaceWorks) {
  SetBluetoothIsPresent(false);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest, WifiRegisterReceiveSurfaceIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       EthernetRegisterReceiveSurfaceIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       ThreeGRegisterReceiveSurfaceIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  // Since bluetooth is on, connection still succeeds.
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       NoBluetoothWifiReceiveSurfaceIsAdvertising) {
  SetBluetoothIsPresent(false);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);

  // TODO(crbug.com/1129069): When WiFi LAN is supported we will expect this to
  // be true.
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kNoAvailableConnectionMedium);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       NoBluetoothEthernetReceiveSurfaceIsAdvertising) {
  SetBluetoothIsPresent(false);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_ETHERNET);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);

  // TODO(crbug.com/1129069): When WiFi LAN is supported we will expect this to
  // be true.
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kNoAvailableConnectionMedium);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       NoBluetoothThreeGReceiveSurfaceNotAdvertising) {
  SetBluetoothIsPresent(false);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_3G);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kNoAvailableConnectionMedium);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       DisableFeatureReceiveSurfaceNotAdvertising) {
  prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
  service_->FlushMojoForTesting();
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_TRUE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       DisableFeatureReceiveSurfaceStopsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  prefs_.SetBoolean(prefs::kNearbySharingEnabledPrefName, false);
  service_->FlushMojoForTesting();
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_TRUE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundReceiveSurfaceNoOneVisibilityIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                    static_cast<int>(nearby_share::mojom::Visibility::kNoOne));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundReceiveSurfaceNoOneVisibilityNotAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                    static_cast<int>(nearby_share::mojom::Visibility::kNoOne));
  service_->FlushMojoForTesting();
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundReceiveSurfaceVisibilityToNoOneStopsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
  service_->FlushMojoForTesting();
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                    static_cast<int>(nearby_share::mojom::Visibility::kNoOne));
  service_->FlushMojoForTesting();
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundReceiveSurfaceVisibilityToSelectedStartsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(prefs::kNearbySharingBackgroundVisibilityName,
                    static_cast<int>(nearby_share::mojom::Visibility::kNoOne));
  service_->FlushMojoForTesting();
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());

  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
  service_->FlushMojoForTesting();
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundReceiveSurfaceSelectedContactsVisibilityIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundReceiveSurfaceSelectedContactsVisibilityIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kSelectedContacts));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       ForegroundReceiveSurfaceAllContactsVisibilityIsAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kAllContacts));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest,
       BackgroundReceiveSurfaceAllContactsVisibilityNotAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  prefs_.SetInteger(
      prefs::kNearbySharingBackgroundVisibilityName,
      static_cast<int>(nearby_share::mojom::Visibility::kAllContacts));
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest, UnregisterReceiveSurfaceStopsAdvertising) {
  // Set visibility to Hidden to stop advertising.
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  NearbySharingService::StatusCodes result2 =
      service_->UnregisterReceiveSurface(&callback);
  EXPECT_EQ(result2, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());
  EXPECT_TRUE(IsBoundToProcess());
  FireProcessShutdownIfRunning();
  EXPECT_FALSE(IsBoundToProcess());
}

TEST_P(NearbySharingServiceImplTest,
       UnregisterReceiveSurfaceDifferentCallbackKeepAdvertising) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  MockTransferUpdateCallback callback2;
  NearbySharingService::StatusCodes result2 =
      service_->UnregisterReceiveSurface(&callback2);
  EXPECT_EQ(result2, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
}

TEST_P(NearbySharingServiceImplTest, UnregisterReceiveSurfaceNeverRegistered) {
  // Set visibility to Hidden to stop advertising.
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  MockTransferUpdateCallback callback;
  NearbySharingService::StatusCodes result =
      service_->UnregisterReceiveSurface(&callback);
  // This is no longer considered an error condition.
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_FALSE(fake_nearby_connections_manager_->IsAdvertising());
  EXPECT_TRUE(IsBoundToProcess());
  FireProcessShutdownIfRunning();
  EXPECT_FALSE(IsBoundToProcess());
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_ClosedReadingIntroduction) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_)).Times(0);

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);

  // Ensure that the messages sent by ProcessLatestPublicCertificateDecryption
  // are processed prior to closing connection.
  base::RunLoop().RunUntilIdle();

  connection_.Close();

  // Introduction is ignored without any side effect.

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_EmptyIntroductionFrame) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);
  SetUpIntroductionFrameDecoder(
      /*text_metadata_count=*/0,
      /*file_metadata_count=*/0,
      /*wifi_credentials_metadata_count=*/0);

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_TRUE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kUnsupportedAttachmentType,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_FALSE(share_target.has_attachments());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::
          UNSUPPORTED_ATTACHMENT_TYPE);

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_ValidIntroductionFrame_InvalidCertificate) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);
  SetUpIntroductionFrameDecoder();

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_FALSE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_FALSE(share_target.is_known);
        EXPECT_TRUE(share_target.has_attachments());
        EXPECT_EQ(3u, share_target.text_attachments.size());
        EXPECT_EQ(1u, share_target.file_attachments.size());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_FALSE(share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_EQ(kEndpointId, share_target.device_id);
        EXPECT_FALSE(share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/false);
  run_loop.Run();

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, IncomingConnection_TimedOut) {
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);
  EXPECT_FALSE(connection_.IsClosed());

  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_TRUE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kTimedOut, metadata.status());
          }));

  task_environment_.FastForwardBy(kReadResponseFrameTimeout +
                                  kIncomingRejectionDelay + kDelta);
  EXPECT_TRUE(connection_.IsClosed());
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_ClosedWaitingLocalConfirmation) {
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_2;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop_2](const ShareTarget& share_target,
                                              TransferMetadata metadata) {
        EXPECT_TRUE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kUnexpectedDisconnection,
                  metadata.status());
        run_loop_2.Quit();
      }));

  connection_.Close();
  run_loop_2.Run();

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, IncomingConnection_OutOfStorage) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  // Set a huge file size in introduction frame to go out of storage.
  std::string intro = "introduction_frame";
  std::vector<uint8_t> bytes(intro.begin(), intro.end());
  EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
      .WillOnce(testing::Invoke([](const std::vector<uint8_t>& data,
                                   ash::nearby::MockNearbySharingDecoder::
                                       DecodeFrameCallback callback) {
        std::vector<sharing::mojom::FileMetadataPtr> mojo_file_metadatas;
        mojo_file_metadatas.push_back(sharing::mojom::FileMetadata::New(
            "name", sharing::mojom::FileMetadata::Type::kAudio,
            /*payload_id=*/1, kFreeDiskSpace + 1, "mime_type",
            /*id=*/123));

        sharing::mojom::V1FramePtr mojo_v1frame =
            sharing::mojom::V1Frame::NewIntroduction(
                sharing::mojom::IntroductionFrame::New(
                    std::move(mojo_file_metadatas),
                    std::vector<sharing::mojom::TextMetadataPtr>(),
                    /*required_package=*/std::nullopt,
                    std::vector<sharing::mojom::WifiCredentialsMetadataPtr>()));

        sharing::mojom::FramePtr mojo_frame =
            sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));

        std::move(callback).Run(std::move(mojo_frame));
      }));
  connection_.AppendReadableData(std::move(bytes));

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_TRUE(share_target.has_attachments());
        EXPECT_EQ(0u, share_target.text_attachments.size());
        EXPECT_EQ(1u, share_target.file_attachments.size());
        EXPECT_EQ(0u, share_target.wifi_credentials_attachments.size());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);

        EXPECT_EQ(TransferMetadata::Status::kNotEnoughSpace, metadata.status());
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, IncomingConnection_FileSizeOverflow) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  // Set file size sum huge to check for overflow.
  std::string intro = "introduction_frame";
  std::vector<uint8_t> bytes(intro.begin(), intro.end());
  EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
      .WillOnce(testing::Invoke([](const std::vector<uint8_t>& data,
                                   ash::nearby::MockNearbySharingDecoder::
                                       DecodeFrameCallback callback) {
        std::vector<sharing::mojom::FileMetadataPtr> mojo_file_metadatas;
        mojo_file_metadatas.push_back(sharing::mojom::FileMetadata::New(
            "name_1", sharing::mojom::FileMetadata::Type::kAudio,
            /*payload_id=*/1, /*size=*/std::numeric_limits<int64_t>::max(),
            "mime_type",
            /*id=*/123));
        mojo_file_metadatas.push_back(sharing::mojom::FileMetadata::New(
            "name_2", sharing::mojom::FileMetadata::Type::kVideo,
            /*payload_id=*/2, /*size=*/100, "mime_type",
            /*id=*/124));

        sharing::mojom::V1FramePtr mojo_v1frame =
            sharing::mojom::V1Frame::NewIntroduction(
                sharing::mojom::IntroductionFrame::New(
                    std::move(mojo_file_metadatas),
                    std::vector<sharing::mojom::TextMetadataPtr>(),
                    /*required_package=*/std::nullopt,
                    std::vector<sharing::mojom::WifiCredentialsMetadataPtr>()));

        sharing::mojom::FramePtr mojo_frame =
            sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));

        std::move(callback).Run(std::move(mojo_frame));
      }));
  connection_.AppendReadableData(std::move(bytes));

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);

        EXPECT_EQ(TransferMetadata::Status::kNotEnoughSpace, metadata.status());
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_ValidIntroductionFrame_ValidCertificate) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);
  SetUpIntroductionFrameDecoder();

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_FALSE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_TRUE(share_target.has_attachments());
        EXPECT_EQ(3u, share_target.text_attachments.size());
        EXPECT_EQ(1u, share_target.file_attachments.size());
        EXPECT_EQ(1u, share_target.wifi_credentials_attachments.size());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);

        EXPECT_FALSE(metadata.token().has_value());
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_InvalidWifiIntroductionPayload) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  std::string intro = "introduction_frame";
  std::vector<uint8_t> bytes(intro.begin(), intro.end());
  EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
      .WillOnce(testing::Invoke(
          [](const std::vector<uint8_t>& data,
             ash::nearby::MockNearbySharingDecoder::DecodeFrameCallback
                 callback) {
            std::vector<sharing::mojom::WifiCredentialsMetadataPtr>
                mojo_wifi_credentials_metadatas;
            mojo_wifi_credentials_metadatas.push_back(
                sharing::mojom::WifiCredentialsMetadata::New(
                    /*ssid=*/"", kWifiSecurityType, kWifiCredentialsPayloadId,
                    kWifiCredentialsId));

            sharing::mojom::V1FramePtr mojo_v1frame =
                sharing::mojom::V1Frame::NewIntroduction(
                    sharing::mojom::IntroductionFrame::New(
                        std::vector<sharing::mojom::FileMetadataPtr>(),
                        std::vector<sharing::mojom::TextMetadataPtr>(),
                        /*required_package=*/std::nullopt,
                        std::move(mojo_wifi_credentials_metadatas)));

            sharing::mojom::FramePtr mojo_frame =
                sharing::mojom::Frame::NewV1(std::move(mojo_v1frame));

            std::move(callback).Run(std::move(mojo_frame));
          }));
  connection_.AppendReadableData(std::move(bytes));

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;

  // This call works when the Receive Wi-Fi flag is both enabled and disabled
  // by checking for two different fail cases:
  // 1. When the flag is enabled, but the introduction frame has an empty SSID
  //    we expect kUnsupportedAttachmentType.
  // 2. When the flag is disabled, we should not accept Wi-Fi credentials yet
  //    so we also expect kUnsupportedAttachmentType.
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_TRUE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kUnsupportedAttachmentType,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(0u, share_target.wifi_credentials_attachments.size());
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kSuccess);
  SetUpForegroundReceiveSurface(callback);
  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  // To avoid UAF in OncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, AcceptInvalidShareTarget) {
  ShareTarget share_target;
  base::RunLoop run_loop;
  service_->Accept(
      share_target,
      base::BindLambdaForTesting(
          [&](NearbySharingServiceImpl::StatusCodes status_code) {
            EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOutOfOrderApiCall,
                      status_code);
            run_loop.Quit();
          }));

  run_loop.Run();
}

TEST_P(NearbySharingServiceImplTest,
       AcceptValidShareTarget_RegisterPayloadError) {
  fake_nearby_connections_manager_->SetPayloadPathStatus(
      kFilePayloadId, nearby::connections::mojom::Status::kError);
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_accept;
  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(
                             NearbySharingServiceImpl::StatusCodes::kError,
                             status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  EXPECT_TRUE(
      fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();

  EXPECT_FALSE(connection_.IsClosed());

  // TODO(crbug.com/40146639) - Remove cleanups after bugfix
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    std::optional<base::FilePath> path =
        fake_nearby_connections_manager_->GetRegisteredPayloadPath(
            kFilePayloadId);
    EXPECT_TRUE(path);
    base::DeleteFile(*path);
  }

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_accept;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
          }));

  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  EXPECT_TRUE(
      fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::ACCEPT);

  EXPECT_FALSE(connection_.IsClosed());

  // TODO(crbug.com/40146639) - Remove cleanups after bugfix
  {
    base::ScopedAllowBlockingForTesting allow_blocking;
    std::optional<base::FilePath> path =
        fake_nearby_connections_manager_->GetRegisteredPayloadPath(
            kFilePayloadId);
    EXPECT_TRUE(path);
    base::DeleteFile(*path);
  }

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget_PayloadSuccessful) {
  SuccessfullyReceiveTransfer();
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget_NoWifiPayload) {
  ReceiveBadWifiPayload(nearby::connections::mojom::Payload::New());
}

TEST_P(NearbySharingServiceImplTest,
       AcceptValidShareTarget_WifiPayloadBytesEmpty) {
  ReceiveBadWifiPayload(nearby::connections::mojom::Payload::New(
      kWifiCredentialsPayloadId,
      nearby::connections::mojom::PayloadContent::NewBytes(
          nearby::connections::mojom::BytesPayload::New(
              std::vector<uint8_t>()))));
}

TEST_P(NearbySharingServiceImplTest,
       AcceptValidShareTarget_NoCredentialsInProto) {
  const std::string& proto_string = "Random string that's not a proto";

  ReceiveBadWifiPayload(nearby::connections::mojom::Payload::New(
      kWifiCredentialsPayloadId,
      nearby::connections::mojom::PayloadContent::NewBytes(
          nearby::connections::mojom::BytesPayload::New(std::vector<uint8_t>(
              proto_string.begin(), proto_string.end())))));
}

TEST_P(NearbySharingServiceImplTest,
       AcceptValidShareTarget_WifiProtoPasswordEmpty) {
  nearby::sharing::service::proto::WifiCredentials credentials_proto;
  credentials_proto.set_password("");
  credentials_proto.set_hidden_ssid(false);
  const std::string& proto_string = credentials_proto.SerializeAsString();

  ReceiveBadWifiPayload(nearby::connections::mojom::Payload::New(
      kWifiCredentialsPayloadId,
      nearby::connections::mojom::PayloadContent::NewBytes(
          nearby::connections::mojom::BytesPayload::New(std::vector<uint8_t>(
              proto_string.begin(), proto_string.end())))));
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget_HiddenNetwork) {
  nearby::sharing::service::proto::WifiCredentials credentials_proto;
  credentials_proto.set_password(kWifiPassword);
  credentials_proto.set_hidden_ssid(true);
  const std::string& proto_string = credentials_proto.SerializeAsString();

  ReceiveBadWifiPayload(nearby::connections::mojom::Payload::New(
      kWifiCredentialsPayloadId,
      nearby::connections::mojom::PayloadContent::NewBytes(
          nearby::connections::mojom::BytesPayload::New(std::vector<uint8_t>(
              proto_string.begin(), proto_string.end())))));
}

TEST_P(NearbySharingServiceImplTest,
       AcceptValidShareTarget_PayloadSuccessful_IncomingPayloadNotFound) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_accept;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
          }));

  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  fake_nearby_connections_manager_->SetIncomingPayload(
      kFilePayloadId, GetFilePayloadPtr(kFilePayloadId));

  for (int64_t id : kValidIntroductionFramePayloadIds) {
    // Update file payload at the end.
    if (id == kFilePayloadId) {
      continue;
    }

    // Deliberately not calling SetIncomingPayload() for text payloads to check
    // for failure condition.

    if (id == kWifiCredentialsPayloadId) {
      fake_nearby_connections_manager_->SetIncomingPayload(
          id, GetWifiPayloadPtr(kWifiCredentialsPayloadId, kWifiPassword));
    }

    base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
        fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
            id);
    ASSERT_TRUE(listener);

    base::RunLoop run_loop_progress;
    EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
        .WillOnce(testing::Invoke([&](const ShareTarget& share_target,
                                      TransferMetadata metadata) {
          EXPECT_FALSE(metadata.is_final_status());
          EXPECT_EQ(TransferMetadata::Status::kInProgress, metadata.status());
          run_loop_progress.Quit();
        }));

    nearby::connections::mojom::PayloadTransferUpdatePtr payload =
        nearby::connections::mojom::PayloadTransferUpdate::New(
            id, nearby::connections::mojom::PayloadStatus::kSuccess,
            /*total_bytes=*/kPayloadSize,
            /*bytes_transferred=*/kPayloadSize);
    listener->OnStatusUpdate(std::move(payload),
                             /*upgraded_medium=*/std::nullopt);
    run_loop_progress.Run();

    task_environment_.FastForwardBy(kMinProgressUpdateFrequency);
  }

  base::RunLoop run_loop_success;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_TRUE(metadata.is_final_status());
            EXPECT_LT(metadata.progress(), 100);
            EXPECT_EQ(TransferMetadata::Status::kIncompletePayloads,
                      metadata.status());

            ASSERT_TRUE(share_target.has_attachments());
            EXPECT_EQ(1u, share_target.file_attachments.size());
            const FileAttachment& file = share_target.file_attachments[0];
            EXPECT_FALSE(file.file_path());
            EXPECT_EQ(0u, wifi_network_handler_->num_configure_network_calls());
            run_loop_success.Quit();
          }));

  base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
      fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
          kFilePayloadId);
  ASSERT_TRUE(listener);

  nearby::connections::mojom::PayloadTransferUpdatePtr payload =
      nearby::connections::mojom::PayloadTransferUpdate::New(
          kFilePayloadId, nearby::connections::mojom::PayloadStatus::kSuccess,
          /*total_bytes=*/kPayloadSize,
          /*bytes_transferred=*/kPayloadSize);
  listener->OnStatusUpdate(std::move(payload),
                           /*upgraded_medium=*/std::nullopt);
  run_loop_success.Run();

  EXPECT_FALSE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));
  EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads());

  // File deletion runs in a ThreadPool.
  task_environment_.RunUntilIdle();

  std::optional<base::FilePath> file_path =
      fake_nearby_connections_manager_->GetRegisteredPayloadPath(
          kFilePayloadId);
  ASSERT_TRUE(file_path);
  EXPECT_FALSE(FileExists(*file_path));

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget_PayloadFailed) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_accept;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
          }));

  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
      fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
          kFilePayloadId);
  ASSERT_TRUE(listener);

  base::RunLoop run_loop_failure;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_TRUE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kFailed, metadata.status());

            ASSERT_TRUE(share_target.has_attachments());
            EXPECT_EQ(1u, share_target.file_attachments.size());
            const FileAttachment& file = share_target.file_attachments[0];
            EXPECT_FALSE(file.file_path());
            run_loop_failure.Quit();
          }));

  nearby::connections::mojom::PayloadTransferUpdatePtr payload =
      nearby::connections::mojom::PayloadTransferUpdate::New(
          kFilePayloadId, nearby::connections::mojom::PayloadStatus::kFailure,
          /*total_bytes=*/kPayloadSize,
          /*bytes_transferred=*/kPayloadSize);
  listener->OnStatusUpdate(std::move(payload),
                           /*upgraded_medium=*/std::nullopt);
  run_loop_failure.Run();

  EXPECT_FALSE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));
  EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads());

  // File deletion runs in a ThreadPool.
  task_environment_.RunUntilIdle();

  std::optional<base::FilePath> file_path =
      fake_nearby_connections_manager_->GetRegisteredPayloadPath(
          kFilePayloadId);
  ASSERT_TRUE(file_path);
  EXPECT_FALSE(FileExists(*file_path));

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, AcceptValidShareTarget_PayloadCancelled) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_accept;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
          }));

  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener> listener =
      fake_nearby_connections_manager_->GetRegisteredPayloadStatusListener(
          kFilePayloadId);
  ASSERT_TRUE(listener);

  base::RunLoop run_loop_failure;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_TRUE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status());

            ASSERT_TRUE(share_target.has_attachments());
            EXPECT_EQ(1u, share_target.file_attachments.size());
            const FileAttachment& file = share_target.file_attachments[0];
            EXPECT_FALSE(file.file_path());
            run_loop_failure.Quit();
          }));

  nearby::connections::mojom::PayloadTransferUpdatePtr payload =
      nearby::connections::mojom::PayloadTransferUpdate::New(
          kFilePayloadId, nearby::connections::mojom::PayloadStatus::kCanceled,
          /*total_bytes=*/kPayloadSize,
          /*bytes_transferred=*/kPayloadSize);
  listener->OnStatusUpdate(std::move(payload),
                           /*upgraded_medium=*/std::nullopt);
  run_loop_failure.Run();

  EXPECT_FALSE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));
  EXPECT_FALSE(fake_nearby_connections_manager_->has_incoming_payloads());

  // File deletion runs in a ThreadPool.
  task_environment_.RunUntilIdle();

  std::optional<base::FilePath> file_path =
      fake_nearby_connections_manager_->GetRegisteredPayloadPath(
          kFilePayloadId);
  ASSERT_TRUE(file_path);
  EXPECT_FALSE(FileExists(*file_path));

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, RejectInvalidShareTarget) {
  ShareTarget share_target;
  base::RunLoop run_loop;
  service_->Reject(
      share_target,
      base::BindLambdaForTesting(
          [&](NearbySharingServiceImpl::StatusCodes status_code) {
            EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOutOfOrderApiCall,
                      status_code);
            run_loop.Quit();
          }));

  run_loop.Run();
}

TEST_P(NearbySharingServiceImplTest, RejectValidShareTarget) {
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);

  base::RunLoop run_loop_reject;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_TRUE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kRejected, metadata.status());
          }));

  service_->Reject(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_reject.Quit();
                       }));

  run_loop_reject.Run();

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::REJECT);

  task_environment_.FastForwardBy(kIncomingRejectionDelay + kDelta);
  EXPECT_TRUE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_KeyVerificationRunnerStatusUnable) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);
  SetUpIntroductionFrameDecoder();

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_FALSE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_TRUE(share_target.has_attachments());
        EXPECT_EQ(3u, share_target.text_attachments.size());
        EXPECT_EQ(1u, share_target.file_attachments.size());
        EXPECT_EQ(1u, share_target.wifi_credentials_attachments.size());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);

        EXPECT_EQ(kFourDigitToken, metadata.token());
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kUnable);
  SetUpForegroundReceiveSurface(callback);

  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  EXPECT_TRUE(
      fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_KeyVerificationRunnerStatusUnable_LowPower) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);
  SetUpIntroductionFrameDecoder();

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;
  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke([&run_loop](const ShareTarget& share_target,
                                            TransferMetadata metadata) {
        EXPECT_FALSE(metadata.is_final_status());
        EXPECT_EQ(TransferMetadata::Status::kAwaitingLocalConfirmation,
                  metadata.status());
        EXPECT_TRUE(share_target.is_incoming);
        EXPECT_TRUE(share_target.is_known);
        EXPECT_TRUE(share_target.has_attachments());
        EXPECT_EQ(3u, share_target.text_attachments.size());
        EXPECT_EQ(1u, share_target.file_attachments.size());
        EXPECT_EQ(1u, share_target.wifi_credentials_attachments.size());
        EXPECT_EQ(kDeviceName, share_target.device_name);
        EXPECT_EQ(GURL(kTestMetadataIconUrl), share_target.image_url);
        EXPECT_EQ(kDeviceType, share_target.type);
        EXPECT_TRUE(share_target.device_id);
        EXPECT_NE(kEndpointId, share_target.device_id);
        EXPECT_EQ(kTestMetadataFullName, share_target.full_name);
        EXPECT_FALSE(share_target.for_self_share);

        EXPECT_EQ(kFourDigitToken, metadata.token());
        run_loop.Quit();
      }));

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kUnable);

  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());

  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);
  run_loop.Run();

  EXPECT_FALSE(
      fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_KeyVerificationRunnerStatusFail) {
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;

  SetUpKeyVerification(/*is_incoming=*/true,
                       sharing::mojom::PairedKeyResultFrame_Status::kFail);
  SetUpForegroundReceiveSurface(callback);

  // Ensures that introduction is never received for failed key verification.
  std::string intro = "introduction_frame";
  std::vector<uint8_t> bytes(intro.begin(), intro.end());
  EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
      .Times(0);
  connection_.AppendReadableData(bytes);

  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);

  // Ensure that the messages sent by ProcessLatestPublicCertificateDecryption
  // are processed prior to checking if connection is closed.
  base::RunLoop().RunUntilIdle();
  EXPECT_TRUE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       IncomingConnection_EmptyAuthToken_KeyVerificationRunnerStatusFail) {
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/1u);

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  NiceMock<MockTransferUpdateCallback> callback;

  SetUpForegroundReceiveSurface(callback);

  // Ensures that introduction is never received for empty auth token.
  std::string intro = "introduction_frame";
  std::vector<uint8_t> bytes(intro.begin(), intro.end());
  EXPECT_CALL(mock_decoder_, DecodeFrame(testing::Eq(bytes), testing::_))
      .Times(0);
  connection_.AppendReadableData(bytes);

  service_->OnIncomingConnectionAccepted(kEndpointId, kValidV1EndpointInfo,
                                         &connection_);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/true);

  EXPECT_TRUE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, RegisterReceiveSurfaceWhileSending) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  base::RunLoop run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        run_loop.QuitClosure());
  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  run_loop.Run();

  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &transfer_callback,
      NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kTransferAlreadyInProgress);

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, RegisterReceiveSurfaceAlreadyReceiving) {
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(callback);
  EXPECT_FALSE(connection_.IsClosed());

  EXPECT_EQ(
      NearbySharingService::StatusCodes::kTransferAlreadyInProgress,
      service_->RegisterReceiveSurface(
          &callback, NearbySharingService::ReceiveSurfaceState::kForeground));
  EXPECT_FALSE(fake_nearby_connections_manager_->IsDiscovering());
  EXPECT_FALSE(fake_nearby_connections_manager_->is_shutdown());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, RegisterReceiveSurfaceWhileDiscovering) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &transfer_callback,
      NearbySharingService::ReceiveSurfaceState::kForeground);
  EXPECT_EQ(result,
            NearbySharingService::StatusCodes::kTransferAlreadyInProgress);
}

TEST_P(NearbySharingServiceImplTest, SendAttachments_WithoutAttachments) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      DiscoverShareTarget(transfer_callback, discovery_callback);

  EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kError,
            service_->SendAttachments(target, /*attachments=*/{}));
  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_AlreadySending) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  base::RunLoop run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        run_loop.QuitClosure());
  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  run_loop.Run();

  // We're now in the sending state, try to send again should fail
  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kError,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_WithoutScanning) {
  ShareTarget target;
  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kError,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
}

TEST_P(NearbySharingServiceImplTest, SendText_UnknownTarget) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  DiscoverShareTarget(transfer_callback, discovery_callback);

  ShareTarget target;
  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kError,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_FailedCreateEndpointInfo) {
  // Set name with too many characters.
  local_device_data_manager()->SetDeviceName(std::string(300, 'a'));

  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      DiscoverShareTarget(transfer_callback, discovery_callback);

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kError,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_FailedToConnect) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  // Call DiscoverShareTarget() instead of SetUpOutgoingShareTarget() as we want
  // to fail before key verification is done.
  ShareTarget target =
      DiscoverShareTarget(transfer_callback, discovery_callback);

  base::RunLoop run_loop;
  ExpectTransferUpdates(
      transfer_callback, target,
      {TransferMetadata::Status::kConnecting,
       TransferMetadata::Status::kFailedToInitiateOutgoingConnection},
      run_loop.QuitClosure());

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  run_loop.Run();

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_FailedKeyVerification) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      DiscoverShareTarget(transfer_callback, discovery_callback);

  base::RunLoop run_loop;
  ExpectTransferUpdates(
      transfer_callback, target,
      {TransferMetadata::Status::kConnecting,
       TransferMetadata::Status::kPairedKeyVerificationFailed},
      run_loop.QuitClosure());

  SetUpKeyVerification(/*is_incoming=*/false,
                       sharing::mojom::PairedKeyResultFrame_Status::kFail);
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  fake_nearby_connections_manager_->set_nearby_connection(&connection_);

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  run_loop.Run();

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_UnableToVerifyKey) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      DiscoverShareTarget(transfer_callback, discovery_callback);

  base::RunLoop run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        run_loop.QuitClosure());

  SetUpKeyVerification(/*is_incoming=*/false,
                       sharing::mojom::PairedKeyResultFrame_Status::kUnable);
  fake_nearby_connections_manager_->SetRawAuthenticationToken(kEndpointId,
                                                              kToken);
  fake_nearby_connections_manager_->set_nearby_connection(&connection_);

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  run_loop.Run();

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplSendFailureTest, SendText_RemoteFailure) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  base::RunLoop introduction_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        introduction_run_loop.QuitClosure());

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  introduction_run_loop.Run();

  // Verify data sent to the remote device so far.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectIntroductionFrame();

  // We're now waiting for the remote device to respond with the accept result.
  base::RunLoop reject_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {std::get<0>(GetParam()).expected_status},
                        reject_run_loop.QuitClosure());

  // Cancel the transfer by rejecting it.
  SendConnectionResponse(std::get<0>(GetParam()).response_status);
  reject_run_loop.Run();

  EXPECT_TRUE(connection_.IsClosed());

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplSendFailureTest, SendFiles_RemoteFailure) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  std::vector<uint8_t> test_data = {'T', 'e', 's', 't'};
  base::FilePath path = CreateTestFile("text.txt", test_data);

  base::RunLoop introduction_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        introduction_run_loop.QuitClosure());

  EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
            service_->SendAttachments(target, CreateFileAttachments({path})));
  introduction_run_loop.Run();

  // Verify data sent to the remote device so far.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectIntroductionFrame();

  // We're now waiting for the remote device to respond with the accept result.
  base::RunLoop reject_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {std::get<0>(GetParam()).expected_status},
                        reject_run_loop.QuitClosure());

  // Cancel the transfer by rejecting it.
  SendConnectionResponse(std::get<0>(GetParam()).response_status);
  reject_run_loop.Run();

  EXPECT_TRUE(connection_.IsClosed());

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

INSTANTIATE_TEST_SUITE_P(
    NearbySharingServiceImplSendFailureTest,
    NearbySharingServiceImplSendFailureTest,
    testing::Combine(testing::ValuesIn(kSendFailureTestData),
                     testing::Range<size_t>(0, 1 << kTestFeatures.size())));

TEST_P(NearbySharingServiceImplTest, SendText_Success) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  base::RunLoop introduction_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        introduction_run_loop.QuitClosure());

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  introduction_run_loop.Run();

  // Verify data sent to the remote device so far.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  auto intro = ExpectIntroductionFrame();

  ASSERT_EQ(1, intro.text_metadata_size());
  auto meta = intro.text_metadata(0);

  EXPECT_EQ(kTextPayload, meta.text_title());
  EXPECT_EQ(strlen(kTextPayload), static_cast<size_t>(meta.size()));
  EXPECT_EQ(nearby::sharing::service::proto::TextMetadata_Type_TEXT,
            meta.type());

  ASSERT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));
  auto advertisement =
      sharing::AdvertisementDecoder::FromEndpointInfo(base::make_span(
          *fake_nearby_connections_manager_->connection_endpoint_info(
              kEndpointId)));
  ASSERT_TRUE(advertisement);
  EXPECT_EQ(kDeviceName, advertisement->device_name());
  EXPECT_EQ(nearby_share::mojom::ShareTargetType::kLaptop,
            advertisement->device_type());
  auto& test_metadata_key = GetNearbyShareTestEncryptedMetadataKey();
  EXPECT_EQ(test_metadata_key.salt(), advertisement->salt());
  EXPECT_EQ(test_metadata_key.encrypted_key(),
            advertisement->encrypted_metadata_key());

  PayloadInfo info = AcceptAndSendPayload(transfer_callback, target);
  FinishOutgoingTransfer(transfer_callback, target, info);

  // We should not have called disconnect yet as we want to wait for 1 minute to
  // make sure all outgoing packets have been sent properly.
  EXPECT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  // Forward time until we send the disconnect request to Nearby Connections.
  task_environment_.FastForwardBy(kOutgoingDisconnectionDelay);

  // Expect to be disconnected now.
  EXPECT_FALSE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendText_SuccessClosedConnection) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);
  SetUpOutgoingConnectionUntilAccept(transfer_callback, target);
  PayloadInfo info = AcceptAndSendPayload(transfer_callback, target);
  FinishOutgoingTransfer(transfer_callback, target, info);

  // We should not have called disconnect yet as we want to wait for 1 minute to
  // make sure all outgoing packets have been sent properly.
  EXPECT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  // Call disconnect on the connection early before the timeout has passed.
  connection_.Close();

  // Expect that we haven't called disconnect again as the endpoint is already
  // disconnected.
  EXPECT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  // Make sure the scheduled disconnect callback does nothing.
  task_environment_.FastForwardBy(kOutgoingDisconnectionDelay);

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, SendFiles_Success) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  std::vector<uint8_t> test_data = {'T', 'e', 's', 't'};
  std::string file_name = "test.txt";
  base::FilePath path = CreateTestFile(file_name, test_data);

  base::RunLoop introduction_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        introduction_run_loop.QuitClosure());

  EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
            service_->SendAttachments(target, CreateFileAttachments({path})));
  introduction_run_loop.Run();

  // Verify data sent to the remote device so far.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  auto intro = ExpectIntroductionFrame();

  ASSERT_EQ(1, intro.file_metadata_size());
  auto meta = intro.file_metadata(0);

  EXPECT_EQ(file_name, meta.name());
  EXPECT_EQ("text/plain", meta.mime_type());
  EXPECT_EQ(test_data.size(), static_cast<size_t>(meta.size()));
  EXPECT_EQ(nearby::sharing::service::proto::FileMetadata_Type_UNKNOWN,
            meta.type());

  // Expect the file payload to be sent in the end.
  base::RunLoop payload_run_loop;
  fake_nearby_connections_manager_->set_send_payload_callback(
      base::BindLambdaForTesting(
          [&](NearbyConnectionsManager::PayloadPtr payload,
              base::WeakPtr<NearbyConnectionsManager::PayloadStatusListener>
                  listener) {
            base::ScopedAllowBlockingForTesting allow_blocking;

            ASSERT_TRUE(payload->content->is_file());
            base::File file = std::move(payload->content->get_file()->file);
            ASSERT_TRUE(file.IsValid());

            std::vector<uint8_t> payload_bytes(test_data.size());
            EXPECT_TRUE(file.ReadAndCheck(/*offset=*/0,
                                          base::make_span(payload_bytes)));
            EXPECT_EQ(test_data, payload_bytes);
            file.Close();

            payload_run_loop.Quit();
          }));

  // We're now waiting for the remote device to respond with the accept result.
  base::RunLoop accept_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kInProgress},
                        accept_run_loop.QuitClosure());

  // Kick off send process by accepting the transfer from the remote device.
  SendConnectionResponse(
      sharing::mojom::ConnectionResponseFrame::Status::kAccept);

  accept_run_loop.Run();
  payload_run_loop.Run();

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, Cancel_Sender_Initiator) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);
  target = SetUpOutgoingConnectionUntilAccept(transfer_callback, target);
  PayloadInfo info = AcceptAndSendPayload(transfer_callback, target);

  // After we stop scanning, we check back in after kInvalidateDelay
  // milliseconds to make sure that we stopped in order to send a file and not
  // because the user left the page. We have to fast forward here, otherwise, we
  // will hit this callback when trying to fastfoward by kInitiatorCancelDelay
  // below.
  task_environment_.FastForwardBy(kInvalidateDelay);

  base::RunLoop run_loop;
  EXPECT_CALL(transfer_callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_EQ(target.id, share_target.id);
            EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status());
          }));
  EXPECT_FALSE(
      fake_nearby_connections_manager_->WasPayloadCanceled(info.payload_id));
  // The initiator of the cancellation explicitly calls Cancel().
  service_->Cancel(target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop.Quit();
                       }));
  run_loop.Run();
  EXPECT_TRUE(
      fake_nearby_connections_manager_->WasPayloadCanceled(info.payload_id));

  // After the TransferMetadata::Status::kCancelled update, we expect other
  // classes to unregister the send surface.
  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);

  // The initiator of the cancel should send a cancel frame to the other device,
  // then wait a few seconds before disconnecting to allow for processing on the
  // other device.
  ExpectCancelFrame();
  EXPECT_FALSE(connection_.IsClosed());
  task_environment_.FastForwardBy(kInitiatorCancelDelay);
  EXPECT_TRUE(connection_.IsClosed());
}

TEST_P(NearbySharingServiceImplTest, Cancel_Sender_Noninitiator) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);
  target = SetUpOutgoingConnectionUntilAccept(transfer_callback, target);
  PayloadInfo info = AcceptAndSendPayload(transfer_callback, target);

  base::RunLoop run_loop;
  EXPECT_CALL(transfer_callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_EQ(target.id, share_target.id);
            EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status());
            run_loop.Quit();
          }));
  EXPECT_FALSE(
      fake_nearby_connections_manager_->WasPayloadCanceled(info.payload_id));
  // The non-initiator of the cancellation processes a cancellation frame from
  // the initiator.
  SendCancel();
  run_loop.Run();
  EXPECT_TRUE(
      fake_nearby_connections_manager_->WasPayloadCanceled(info.payload_id));

  // The non-initiator should close the connection immediately
  EXPECT_TRUE(connection_.IsClosed());
}

TEST_P(NearbySharingServiceImplTest, Cancel_Receiver_Initiator) {
  NiceMock<MockTransferUpdateCallback> transfer_callback;
  ShareTarget target = SetUpIncomingConnection(transfer_callback);
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();

  base::RunLoop run_loop;
  EXPECT_CALL(transfer_callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_EQ(target.id, share_target.id);
            EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status());
          }));
  EXPECT_FALSE(
      fake_nearby_connections_manager_->WasPayloadCanceled(kFilePayloadId));
  // The initiator of the cancellation explicitly calls Cancel().
  service_->Cancel(target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop.Quit();
                       }));
  run_loop.Run();
  EXPECT_TRUE(
      fake_nearby_connections_manager_->WasPayloadCanceled(kFilePayloadId));

  // After the TransferMetadata::Status::kCancelled update, we expect other
  // classes to unregister the receive surface.
  service_->UnregisterReceiveSurface(&transfer_callback);

  // The initiator of the cancel should send a cancel frame to the other device,
  // then wait a few seconds before disconnecting to allow for processing on the
  // other device.
  ExpectCancelFrame();
  EXPECT_FALSE(connection_.IsClosed());
  task_environment_.FastForwardBy(kInitiatorCancelDelay);
  EXPECT_TRUE(connection_.IsClosed());
}

TEST_P(NearbySharingServiceImplTest, Cancel_Receiver_Noninitiator) {
  NiceMock<MockTransferUpdateCallback> transfer_callback;
  ShareTarget target = SetUpIncomingConnection(transfer_callback);
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();

  base::RunLoop run_loop;
  EXPECT_CALL(transfer_callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_EQ(target.id, share_target.id);
            EXPECT_EQ(TransferMetadata::Status::kCancelled, metadata.status());
            run_loop.Quit();
          }));
  EXPECT_FALSE(
      fake_nearby_connections_manager_->WasPayloadCanceled(kFilePayloadId));
  // The non-initiator of the cancellation processes a cancellation frame from
  // the initiator.
  SendCancel();
  run_loop.Run();
  EXPECT_TRUE(
      fake_nearby_connections_manager_->WasPayloadCanceled(kFilePayloadId));

  // The non-initiator should close the connection immediately
  EXPECT_TRUE(connection_.IsClosed());
}

TEST_P(NearbySharingServiceImplTest,
       RegisterForegroundReceiveSurfaceEntersHighVisibility) {
  TestObserver observer(service_.get());
  NiceMock<MockTransferUpdateCallback> callback;

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  local_device_data_manager()->SetDeviceName(kDeviceName);

  // To start, we should not be in high visibility state.
  EXPECT_FALSE(service_->IsInHighVisibility());
  EXPECT_FALSE(observer.on_start_advertising_failure_called_);

  // If we register a foreground surface we should end up in high visibility
  // state.
  SetUpForegroundReceiveSurface(callback);

  // At this point we should have a new high visibility state and the observer
  // should have been called as well.
  EXPECT_TRUE(service_->IsInHighVisibility());
  EXPECT_TRUE(observer.in_high_visibility_);
  EXPECT_FALSE(observer.on_start_advertising_failure_called_);

  // If we unregister the foreground receive surface we should no longer be in
  // high visibility and the observer should be notified.
  EXPECT_EQ(NearbySharingService::StatusCodes::kOk,
            service_->UnregisterReceiveSurface(&callback));
  EXPECT_FALSE(service_->IsInHighVisibility());
  EXPECT_FALSE(observer.in_high_visibility_);

  // Remove the observer before it goes out of scope.
  service_->RemoveObserver(&observer);
}

TEST_P(NearbySharingServiceImplTest, ProcessStoppedCallsObservers) {
  TestObserver observer(service_.get());
  NiceMock<MockTransferUpdateCallback> callback;

  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  local_device_data_manager()->SetDeviceName(kDeviceName);

  // If we register a foreground surface we should end up in high visibility
  // state.
  SetUpForegroundReceiveSurface(callback);
  EXPECT_TRUE(service_->IsInHighVisibility());

  // Signal a process crash, check that the observer is called and high
  // visibility is stopped.
  fake_nearby_connections_manager_->CleanupForProcessStopped();
  std::move(process_stopped_callback_)
      .Run(ash::nearby::NearbyProcessManager::NearbyProcessShutdownReason::
               kCrash);
  EXPECT_TRUE(observer.process_stopped_called_);
  EXPECT_FALSE(service_->IsInHighVisibility());

  // Remove the observer before it goes out of scope.
  service_->RemoveObserver(&observer);
}

TEST_P(NearbySharingServiceImplTest, ShutdownCallsObservers) {
  TestObserver observer(service_.get());

  EXPECT_FALSE(observer.shutdown_called_);

  service_->Shutdown();

  EXPECT_TRUE(observer.shutdown_called_);

  // Prevent a double shutdown.
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest, SendPayloadWithArcCallback) {
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  FakeArcNearbyShareSession arc_session;

  service_->SetArcTransferCleanupCallback(
      base::BindOnce(&FakeArcNearbyShareSession::OnCleanupCallbackStub,
                     base::Unretained(&arc_session)));
  EXPECT_FALSE(arc_session.CleanupCallbackCalled());

  ShareTarget target =
      SetUpOutgoingShareTarget(transfer_callback, discovery_callback);

  base::RunLoop introduction_run_loop;
  ExpectTransferUpdates(transfer_callback, target,
                        {TransferMetadata::Status::kConnecting,
                         TransferMetadata::Status::kAwaitingLocalConfirmation,
                         TransferMetadata::Status::kAwaitingRemoteAcceptance},
                        introduction_run_loop.QuitClosure());

  EXPECT_EQ(
      NearbySharingServiceImpl::StatusCodes::kOk,
      service_->SendAttachments(target, CreateTextAttachments({kTextPayload})));
  introduction_run_loop.Run();

  // Verify data sent to the remote device so far.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  auto intro = ExpectIntroductionFrame();

  ASSERT_EQ(1, intro.text_metadata_size());
  auto meta = intro.text_metadata(0);

  EXPECT_EQ(kTextPayload, meta.text_title());
  EXPECT_EQ(strlen(kTextPayload), static_cast<size_t>(meta.size()));
  EXPECT_EQ(nearby::sharing::service::proto::TextMetadata_Type_TEXT,
            meta.type());

  ASSERT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));
  auto advertisement =
      sharing::AdvertisementDecoder::FromEndpointInfo(base::make_span(
          *fake_nearby_connections_manager_->connection_endpoint_info(
              kEndpointId)));
  ASSERT_TRUE(advertisement);
  EXPECT_EQ(kDeviceName, advertisement->device_name());
  EXPECT_EQ(nearby_share::mojom::ShareTargetType::kLaptop,
            advertisement->device_type());
  auto& test_metadata_key = GetNearbyShareTestEncryptedMetadataKey();
  EXPECT_EQ(test_metadata_key.salt(), advertisement->salt());
  EXPECT_EQ(test_metadata_key.encrypted_key(),
            advertisement->encrypted_metadata_key());

  PayloadInfo info = AcceptAndSendPayload(transfer_callback, target);
  FinishOutgoingTransfer(transfer_callback, target, info);

  // We should not have called disconnect yet as we want to wait for 1 minute to
  // make sure all outgoing packets have been sent properly.
  EXPECT_TRUE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  // Forward time until we send the disconnect request to Nearby Connections.
  task_environment_.FastForwardBy(kOutgoingDisconnectionDelay);

  // ARC cleanup runs in a ThreadPool.
  task_environment_.RunUntilIdle();
  EXPECT_TRUE(arc_session.CleanupCallbackCalled());

  // Expect to be disconnected now.
  EXPECT_FALSE(
      fake_nearby_connections_manager_->connection_endpoint_info(kEndpointId));

  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
}

TEST_P(NearbySharingServiceImplTest, ShutdownCallsObserversWithArcCallback) {
  TestObserver observer(service_.get());
  FakeArcNearbyShareSession arc_session;

  service_->SetArcTransferCleanupCallback(
      base::BindOnce(&FakeArcNearbyShareSession::OnCleanupCallbackStub,
                     base::Unretained(&arc_session)));

  EXPECT_FALSE(arc_session.CleanupCallbackCalled());
  EXPECT_FALSE(observer.shutdown_called_);

  service_->Shutdown();

  // ARC cleanup runs in a ThreadPool.
  task_environment_.RunUntilIdle();

  EXPECT_TRUE(observer.shutdown_called_);
  EXPECT_TRUE(arc_session.CleanupCallbackCalled());

  // Prevent a double shutdown.
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest, RotateBackgroundAdvertisement_Periodic) {
  certificate_manager()->set_next_salt({0x00, 0x01});
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  NiceMock<MockTransferUpdateCallback> callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  auto endpoint_info_initial =
      fake_nearby_connections_manager_->advertising_endpoint_info();

  certificate_manager()->set_next_salt({0x00, 0x02});
  task_environment_.FastForwardBy(base::Seconds(870));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  auto endpoint_info_rotated =
      fake_nearby_connections_manager_->advertising_endpoint_info();
  EXPECT_NE(endpoint_info_initial, endpoint_info_rotated);
}

TEST_P(NearbySharingServiceImplTest,
       RotateBackgroundAdvertisement_PrivateCertificatesChange) {
  certificate_manager()->set_next_salt({0x00, 0x01});
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  NiceMock<MockTransferUpdateCallback> callback;
  NearbySharingService::StatusCodes result = service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(result, NearbySharingService::StatusCodes::kOk);
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  auto endpoint_info_initial =
      fake_nearby_connections_manager_->advertising_endpoint_info();

  certificate_manager()->set_next_salt({0x00, 0x02});
  certificate_manager()->NotifyPrivateCertificatesChanged();
  EXPECT_TRUE(fake_nearby_connections_manager_->IsAdvertising());
  auto endpoint_info_rotated =
      fake_nearby_connections_manager_->advertising_endpoint_info();
  EXPECT_NE(endpoint_info_initial, endpoint_info_rotated);
}

TEST_P(NearbySharingServiceImplTest, OrderedEndpointDiscoveryEvents) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);

  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;

  // Start discovering, to ensure a discovery listener is registered.
  EXPECT_EQ(
      NearbySharingService::StatusCodes::kOk,
      service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                    SendSurfaceState::kForeground));
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());

  // Ensure that the endpoint discovered and lost event are process
  // sequentially. This is particularly importatnt due to the asynchronous
  // operations needed to handle endpoint discovery.
  //
  // Order of events:
  //   - Nearby Connections discovers endpoint 1
  //   - Nearby Connections loses endpoint 1
  //   - Nearby Share processes these two events in order.
  //   - Nearby Connections discovers endpoint 2
  //   - Nearby Connections discovers endpoint 3
  //   - Nearby Connections loses endpoint 3
  //   - Nearby Connections loses endpoint 2
  //   - Nearby Share processes these four events in order.

  // Expect the advertisement decoder  to be invoked once for each discovery.
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/false,
                            /*expected_number_of_calls=*/3u);
  {
    base::RunLoop run_loop;
    FindEndpoint(/*endpoint_id=*/"1");
    LoseEndpoint(/*endpoint_id=*/"1");
    ::testing::InSequence s;
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered);
    EXPECT_CALL(discovery_callback, OnShareTargetLost)
        .WillOnce([&run_loop](ShareTarget share_target) { run_loop.Quit(); });

    // Needed for discovery processing.
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                             /*success=*/true);
    run_loop.Run();
  }
  {
    base::RunLoop run_loop;
    FindEndpoint(/*endpoint_id=*/"2");
    FindEndpoint(/*endpoint_id=*/"3");
    LoseEndpoint(/*endpoint_id=*/"3");
    LoseEndpoint(/*endpoint_id=*/"2");
    ::testing::InSequence s;
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
        .WillOnce([](ShareTarget share_target) {
          EXPECT_EQ("2", share_target.device_id);
        });
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
        .WillOnce([](ShareTarget share_target) {
          EXPECT_EQ("3", share_target.device_id);
        });
    EXPECT_CALL(discovery_callback, OnShareTargetLost)
        .WillOnce([](ShareTarget share_target) {
          EXPECT_EQ("3", share_target.device_id);
        });
    EXPECT_CALL(discovery_callback, OnShareTargetLost)
        .WillOnce([&run_loop](ShareTarget share_target) {
          EXPECT_EQ("2", share_target.device_id);
          run_loop.Quit();
        });
    // Needed for discovery processing. Fail, then the ShareTarget device ID is
    // set to the endpoint ID, which we use above to verify the correct endpoint
    // ID processing order.
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/2,
                                             /*success=*/false);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/3,
                                             /*success=*/false);

    run_loop.Run();
  }
}

TEST_P(NearbySharingServiceImplTest,
       RetryDiscoveredEndpoints_NoDownloadIfDecryption) {
  // Start discovery.
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/true,
                            /*expected_number_of_calls=*/1u);
  // Order of events:
  // - Discover endpoint 1 --> decrypts public certificate
  // - Fire certificate download timer --> no download because no cached
  //                                       advertisements
  {
    base::RunLoop run_loop;
    FindEndpoint(/*endpoint_id=*/"1");
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
        .WillOnce([&run_loop](ShareTarget share_target) { run_loop.Quit(); });
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                             /*success=*/true);
    run_loop.Run();
  }
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());

  EXPECT_CALL(discovery_callback, OnShareTargetLost);
  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest,
       RetryDiscoveredEndpoints_DownloadCertsAndRetryDecryption) {
  // Start discovery.
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/true,
                            /*expected_number_of_calls=*/6u);
  // Order of events:
  // - Discover endpoint 1 --> decrypts public certificate
  // - Discover endpoint 2 --> cannot decrypt public certificate
  // - Discover endpoint 3 --> decrypts public certificate
  // - Discover endpoint 4 --> cannot decrypt public certificate
  // - Lose endpoint 3
  // - Fire certificate download timer --> certificates downloaded
  // - (Re)discover endpoints 2 and 4
  {
    base::RunLoop run_loop;
    FindEndpoint(/*endpoint_id=*/"1");
    FindEndpoint(/*endpoint_id=*/"2");
    FindEndpoint(/*endpoint_id=*/"3");
    FindEndpoint(/*endpoint_id=*/"4");
    LoseEndpoint(/*endpoint_id=*/"3");
    ::testing::InSequence s;
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered).Times(2);
    EXPECT_CALL(discovery_callback, OnShareTargetLost)
        .WillOnce([&run_loop](ShareTarget share_target) { run_loop.Quit(); });
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                             /*success=*/true);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/2,
                                             /*success=*/false);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/3,
                                             /*success=*/true);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/4,
                                             /*success=*/false);
    run_loop.Run();
  }
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(2u,
            certificate_manager()->num_download_public_certificates_calls());
  certificate_manager()->NotifyPublicCertificatesDownloaded();
  {
    base::RunLoop run_loop;
    ::testing::InSequence s;
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered);
    EXPECT_CALL(discovery_callback, OnShareTargetDiscovered)
        .WillOnce([&run_loop](ShareTarget share_target) { run_loop.Quit(); });
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/5,
                                             /*success=*/true);
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/6,
                                             /*success=*/true);
    run_loop.Run();
  }
  EXPECT_CALL(discovery_callback, OnShareTargetLost).Times(3);
  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest,
       RetryDiscoveredEndpoints_DiscoveryRestartClearsCache) {
  // Start discovery.
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/true,
                            /*expected_number_of_calls=*/1u);
  // Order of events:
  // - Discover endpoint 1 --> cannot decrypt public certificate
  // - Stop discovery
  // - Certificate download timer not running; not discovering
  // - Start discovery
  // - Fire certificate download timer --> certificates not downloaded; cached
  //                                       advertisement map has been cleared
  FindEndpoint(/*endpoint_id=*/"1");
  ::testing::InSequence s;
  EXPECT_CALL(discovery_callback, OnShareTargetDiscovered).Times(0);
  EXPECT_CALL(discovery_callback, OnShareTargetLost).Times(0);
  ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/1,
                                           /*success=*/false);
  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  // Note: Certificate downloads are also requested in RegisterSendSurface; this
  // is not related to the retry timer.
  EXPECT_EQ(2u,
            certificate_manager()->num_download_public_certificates_calls());
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(2u,
            certificate_manager()->num_download_public_certificates_calls());
  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest, RetryDiscoveredEndpoints_DownloadLimit) {
  // Start discovery.
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_WIFI);
  MockTransferUpdateCallback transfer_callback;
  MockShareTargetDiscoveredCallback discovery_callback;
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  EXPECT_EQ(1u,
            certificate_manager()->num_download_public_certificates_calls());
  EXPECT_TRUE(fake_nearby_connections_manager_->IsDiscovering());
  SetUpAdvertisementDecoder(kValidV1EndpointInfo,
                            /*return_empty_advertisement=*/false,
                            /*return_empty_device_name=*/true,
                            /*expected_number_of_calls=*/2u +
                                kMaxCertificateDownloadsDuringDiscovery);
  // Order of events:
  // - x3:
  //   - (Re)discover endpoint 1 --> cannot decrypt public certificate
  //   - Fire certificate download timer --> certificates downloaded
  // - Rediscover endpoint 1 --> cannot decrypt public certificate
  // - Fire certificate download timer --> no download; limit reached
  // - Restart discovery which resets limit counter
  FindEndpoint(/*endpoint_id=*/"1");
  for (size_t i = 1; i <= kMaxCertificateDownloadsDuringDiscovery; ++i) {
    ProcessLatestPublicCertificateDecryption(/*expected_num_calls=*/i,
                                             /*success=*/false);
    task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
    EXPECT_EQ(1u + i,
              certificate_manager()->num_download_public_certificates_calls());
    certificate_manager()->NotifyPublicCertificatesDownloaded();
  }
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(1u + kMaxCertificateDownloadsDuringDiscovery,
            certificate_manager()->num_download_public_certificates_calls());
  service_->UnregisterSendSurface(&transfer_callback, &discovery_callback);
  service_->RegisterSendSurface(&transfer_callback, &discovery_callback,
                                SendSurfaceState::kForeground);
  // Note: Certificate downloads are also requested in RegisterSendSurface; this
  // is not related to the retry timer.
  EXPECT_EQ(2u + kMaxCertificateDownloadsDuringDiscovery,
            certificate_manager()->num_download_public_certificates_calls());
  FindEndpoint(/*endpoint_id=*/"1");
  ProcessLatestPublicCertificateDecryption(
      /*expected_num_calls=*/1u + kMaxCertificateDownloadsDuringDiscovery,
      /*success=*/false);
  task_environment_.FastForwardBy(kCertificateDownloadDuringDiscoveryPeriod);
  EXPECT_EQ(3u + kMaxCertificateDownloadsDuringDiscovery,
            certificate_manager()->num_download_public_certificates_calls());

  service_->Shutdown();
  service_.reset();
}

TEST_P(NearbySharingServiceImplTest, NotBoundToProcessIfDisabled) {
  SetIsEnabled(false);
  EXPECT_FALSE(IsBoundToProcess());
}

TEST_P(NearbySharingServiceImplTest, UnbindsFromProcessWhenDisabled) {
  SetIsEnabled(true);
  EXPECT_TRUE(IsBoundToProcess());
  SetIsEnabled(false);
  EXPECT_FALSE(IsBoundToProcess());
}

TEST_P(NearbySharingServiceImplTest, BindsProcessWhenReenabled) {
  SetIsEnabled(true);
  EXPECT_TRUE(IsBoundToProcess());
  SetIsEnabled(false);
  EXPECT_FALSE(IsBoundToProcess());
  SetIsEnabled(true);
  EXPECT_TRUE(IsBoundToProcess());
}

// Parameters are |is_enabled|, |shutdown_reason|, |recent_shutdown_count|, and
// |feature_mask|.
using ServiceRestartTestParams =
    std::tuple<bool, NearbyProcessShutdownReason, int, size_t>;

class NearbySharingServiceImplRestartTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<ServiceRestartTestParams> {
 public:
  NearbySharingServiceImplRestartTest()
      : NearbySharingServiceImplTestBase(
            /*feature_mask=*/std::get<3>(GetParam())) {}
};

TEST_P(NearbySharingServiceImplRestartTest, RestartsServiceWhenAppropriate) {
  bool is_enabled = std::get<0>(GetParam());
  NearbyProcessShutdownReason shutdown_reason = std::get<1>(GetParam());
  int recent_shutdown_count = std::get<2>(GetParam());

  SetIsEnabled(is_enabled);
  SetRecentNearbyProcessShutdownCount(recent_shutdown_count);

  bool expected_to_restart;

  // Important:  Remember to update testing::Values used in
  // INSTANTIATE_TEST_SUITE_P when adding cases to this switch statement.
  switch (shutdown_reason) {
    case NearbyProcessShutdownReason::kNormal:
      expected_to_restart = false;
      break;

    case NearbyProcessShutdownReason::kCrash:
    case NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection:
    case NearbyProcessShutdownReason::kPresenceMojoPipeDisconnection:
    case NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection:
      expected_to_restart =
          is_enabled && recent_shutdown_count <=
                            NearbySharingServiceImpl::
                                kMaxRecentNearbyProcessUnexpectedShutdownCount;
      break;
  }

  EXPECT_CALL(mock_nearby_process_manager(), GetNearbyProcessReference)
      .Times(expected_to_restart ? 1 : 0);

  // If the feature is disabled, the saved process_stopped_callback_ is invalid
  // and shouldn't be called.
  if (is_enabled) {
    ShutdownNearbyProcess(shutdown_reason);
  }
}

INSTANTIATE_TEST_SUITE_P(
    NearbySharingServiceImplTest,
    NearbySharingServiceImplRestartTest,
    testing::Combine(
        testing::Bool(),
        testing::Values(
            NearbyProcessShutdownReason::kNormal,
            NearbyProcessShutdownReason::kCrash,
            NearbyProcessShutdownReason::kConnectionsMojoPipeDisconnection,
            NearbyProcessShutdownReason::kPresenceMojoPipeDisconnection,
            NearbyProcessShutdownReason::kDecoderMojoPipeDisconnection),
        testing::Values(0,
                        NearbySharingServiceImpl::
                                kMaxRecentNearbyProcessUnexpectedShutdownCount -
                            1,
                        NearbySharingServiceImpl::
                            kMaxRecentNearbyProcessUnexpectedShutdownCount),
        testing::Range<size_t>(0, 1 << kTestFeatures.size())));

TEST_P(NearbySharingServiceImplTest, ProcessShutdownTimerDoesNotRestart) {
  // Set visibility to Hidden to deactivate advertising to start the process
  // shutdown timer.
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);

  EXPECT_TRUE(IsBoundToProcess());
  EXPECT_TRUE(IsProcessShutdownTimerRunning());

  // Registering a receive surface should cancel the timer.
  NiceMock<MockTransferUpdateCallback> callback;
  SetUpForegroundReceiveSurface(callback);
  EXPECT_TRUE(IsBoundToProcess());
  EXPECT_FALSE(IsProcessShutdownTimerRunning());

  // Unregistering the receive surface should start the timer.
  service_->UnregisterReceiveSurface(&callback);
  EXPECT_TRUE(IsBoundToProcess());
  EXPECT_TRUE(IsProcessShutdownTimerRunning());

  // Run the timer down a bit.
  task_environment_.FastForwardBy(base::Seconds(10));

  // Unregister a receive surface again and make sure the timer did not restart.
  service_->UnregisterReceiveSurface(&callback);
  EXPECT_TRUE(IsBoundToProcess());
  EXPECT_TRUE(IsProcessShutdownTimerRunning());
  task_environment_.FastForwardBy(base::Seconds(10));
  EXPECT_FALSE(IsBoundToProcess());
  EXPECT_FALSE(IsProcessShutdownTimerRunning());
}

TEST_P(NearbySharingServiceImplTest, NoShutdownTimerWithoutProcessRef) {
  FireProcessShutdownIfRunning();
  // Reset process reference.
  SetIsEnabled(false);
  EXPECT_FALSE(IsBoundToProcess());
  EXPECT_FALSE(IsProcessShutdownTimerRunning());

  // Unregister a receive surface and make sure the timer does not start.
  NiceMock<MockTransferUpdateCallback> callback;
  service_->UnregisterReceiveSurface(&callback);
  EXPECT_FALSE(IsBoundToProcess());
  EXPECT_FALSE(IsProcessShutdownTimerRunning());
}

TEST_P(NearbySharingServiceImplTest, FastInitiationScanning_StartAndStop) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  // Trigger a call to StopFastInitiationScanning().
  SetBluetoothIsPowered(false);
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  // Trigger a call to StartFastInitiationScanning().
  SetBluetoothIsPowered(true);
  EXPECT_EQ(2u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest,
       FastInitiationScanning_DisallowedByPolicy) {
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  SetManagedEnabled(false);
  base::RunLoop().RunUntilIdle();
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);

  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest,
       FastInitiationScanning_OnFastInitiationNotificationStateChanged) {
  // Fast init notifications are enabled by default so a scanner is created on
  // initialization of the service.
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  // The existing scanner is destroyed when fast init notifications are turned
  // off.
  SetFastInitiationNotificationState(
      nearby_share::mojom::FastInitiationNotificationState::kDisabledByUser);
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  SetFastInitiationNotificationState(
      nearby_share::mojom::FastInitiationNotificationState::kEnabled);
  EXPECT_EQ(2u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest,
       FastInitiationScanning_MultipleReceiveSurfaces) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  // Registering a background receive surface should not create a scanner since
  // we're already scanning.
  MockTransferUpdateCallback callback;
  service_->RegisterReceiveSurface(
      &callback, NearbySharingService::ReceiveSurfaceState::kBackground);
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest, FastInitiationScanning_NotifyObservers) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);
  TestObserver observer(service_.get());
  ASSERT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());

  FakeFastInitiationScanner* scanner =
      fast_initiation_scanner_factory_->last_fake_fast_initiation_scanner();
  scanner->DevicesDetected();
  EXPECT_TRUE(observer.devices_detected_called_);
  scanner->DevicesNotDetected();
  EXPECT_TRUE(observer.devices_not_detected_called_);
  scanner->ScannerInvalidated();
  EXPECT_TRUE(observer.scanning_stopped_called_);

  // Remove the observer before it goes out of scope.
  service_->RemoveObserver(&observer);
}

TEST_P(NearbySharingServiceImplTest, FastInitiationScanning_NoHardwareSupport) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);

  // Hardware support is enabled by default in these tests, so we expect that a
  // scanner has been created.
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  fast_initiation_scanner_factory_->SetHardwareSupportAvailable(false);
  // Changes in HardwareSupportState should trigger InvalidateSurfaceState
  SetHardwareSupportState(
      device::BluetoothAdapter::LowEnergyScanSessionHardwareOffloadingStatus::
          kNotSupported);

  // Make sure we stopped scanning and didn't restart.
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest,
       FastInitiationScanning_PostTransferCooldown) {
  SetConnectionType(net::NetworkChangeNotifier::CONNECTION_BLUETOOTH);

  // Make sure we started scanning once
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(0u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  SuccessfullyReceiveTransfer();

  // Make sure we stopped scanning and didn't restart... yet.
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());

  // Fast forward 10s to pass through the cooldown period.
  task_environment_.FastForwardBy(base::Seconds(10));

  // Make sure we restarted Fast Initiation scanning.
  EXPECT_EQ(2u, fast_initiation_scanner_factory_->scanner_created_count());
  EXPECT_EQ(1u, fast_initiation_scanner_factory_->scanner_destroyed_count());
}

TEST_P(NearbySharingServiceImplTest, VisibilityReminderTimerIsTriggered) {
  constexpr base::TimeDelta kTestDelay = base::Minutes(3);
  service_->set_visibility_reminder_timer_delay_for_testing(kTestDelay);
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  task_environment_.FastForwardBy(kTestDelay);
  std::vector<message_center::Notification> notifications =
      notification_tester_->GetDisplayedNotificationsForType(
          NotificationHandler::Type::NEARBY_SHARE);

  // Visibility reminder notification should display after 3 Minutes.
  EXPECT_EQ(1u, notifications.size());
}

TEST_P(NearbySharingServiceImplTest, CreateShareTarget) {
  sharing::mojom::AdvertisementPtr advertisement =
      sharing::mojom::Advertisement::New(
          GetNearbyShareTestEncryptedMetadataKey().salt(),
          GetNearbyShareTestEncryptedMetadataKey().encrypted_key(), kDeviceType,
          kDeviceName);

  // Flip |for_self_share| to true to ensure the resulting ShareTarget picks
  // this up.
  nearby::sharing::proto::PublicCertificate certificate_proto =
      GetNearbyShareTestPublicCertificate(
          nearby_share::mojom::Visibility::kAllContacts);
  certificate_proto.set_for_self_share(true);

  std::optional<NearbyShareDecryptedPublicCertificate> certificate =
      NearbyShareDecryptedPublicCertificate::DecryptPublicCertificate(
          certificate_proto, GetNearbyShareTestEncryptedMetadataKey());
  ASSERT_TRUE(certificate.has_value());
  ASSERT_EQ(certificate_proto.for_self_share(), certificate->for_self_share());

  std::optional<ShareTarget> share_target =
      CreateShareTarget(advertisement, certificate);
  ASSERT_TRUE(share_target.has_value());
  EXPECT_EQ(kDeviceName, share_target->device_name);
  EXPECT_EQ(kDeviceType, share_target->type);
  EXPECT_EQ(certificate_proto.for_self_share(), share_target->for_self_share);

  // Test when |certificate| is null.
  share_target = CreateShareTarget(advertisement, /*certificate=*/std::nullopt);
  ASSERT_TRUE(share_target.has_value());
  EXPECT_EQ(kDeviceName, share_target->device_name);
  EXPECT_EQ(kDeviceType, share_target->type);
  EXPECT_FALSE(share_target->for_self_share);
}

TEST_P(NearbySharingServiceImplTest, SelfShareAutoAccept) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  // We create an incoming connection corresponding to a certificate where the
  // |for_self_share| field is set to 'true'. This value will be propagated to
  // the ShareTarget, which will be used as a signal for the service to
  // automatically accept the transfer when Self Share is enabled. This is
  // similar to other tests (see "AcceptValidShareTarget") but without the
  // explicit call to service_->Accept(). Wi-Fi credentials is an edge case for
  // Self Share that we test separately, so |no_wifi_credentials| is true.
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(
      callback, /*for_self_share=*/true, /*wifi_credentials_metadata_count=*/0);

  base::RunLoop run_loop;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [&](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
            run_loop.Quit();
          }));
  run_loop.Run();

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::ACCEPT);

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest, SelfShareAutoAccept_WiFiCredentials) {
  for (int64_t payload_id : kValidIntroductionFramePayloadIds) {
    fake_nearby_connections_manager_->SetPayloadPathStatus(
        payload_id, nearby::connections::mojom::Status::kSuccess);
  }

  // We create an incoming connection corresponding to a certificate where the
  // |for_self_share| field is set to 'true'. This value will be propagated to
  // the ShareTarget, which will be used as a signal for the service to
  // automatically accept the transfer when Self Share is enabled. However, we
  // don't auto-accept Wi-Fi credentials and thus we
  NiceMock<MockTransferUpdateCallback> callback;
  ShareTarget share_target = SetUpIncomingConnection(
      callback, /*for_self_share=*/true, /*wifi_credentials_metadata_count=*/1);

  base::RunLoop run_loop_accept;
  EXPECT_CALL(callback, OnTransferUpdate(testing::_, testing::_))
      .WillOnce(testing::Invoke(
          [](const ShareTarget& share_target, TransferMetadata metadata) {
            EXPECT_FALSE(metadata.is_final_status());
            EXPECT_EQ(TransferMetadata::Status::kAwaitingRemoteAcceptance,
                      metadata.status());
          }));

  service_->Accept(share_target,
                   base::BindLambdaForTesting(
                       [&](NearbySharingServiceImpl::StatusCodes status_code) {
                         EXPECT_EQ(NearbySharingServiceImpl::StatusCodes::kOk,
                                   status_code);
                         run_loop_accept.Quit();
                       }));

  run_loop_accept.Run();

  EXPECT_TRUE(
      fake_nearby_connections_manager_->DidUpgradeBandwidth(kEndpointId));

  // Check data written to connection_.
  ExpectPairedKeyEncryptionFrame();
  ExpectPairedKeyResultFrame();
  ExpectConnectionResponseFrame(
      nearby::sharing::service::proto::ConnectionResponseFrame::ACCEPT);

  EXPECT_FALSE(connection_.IsClosed());

  // To avoid UAF in OnIncomingTransferUpdate().
  service_->UnregisterReceiveSurface(&callback);
}

TEST_P(NearbySharingServiceImplTest,
       SelfShareEnabled_YourDevicesVisibilityOnScreenLock) {
  const std::set<std::string> contacts = {"1", "2"};
  SetVisibility(nearby_share::mojom::Visibility::kAllContacts);
  contact_manager()->SetAllowedContacts(contacts);

  // Lock screen, expect Your Devices visibility.
  session_controller_->SetScreenLocked(true);
  EXPECT_EQ(nearby_share::mojom::Visibility::kYourDevices, GetVisibility());
  EXPECT_EQ(std::set<std::string>(), contact_manager()->GetAllowedContacts());

  // Unlock screen, expect visibility to return to All Contacts.
  session_controller_->SetScreenLocked(false);
  EXPECT_EQ(nearby_share::mojom::Visibility::kAllContacts, GetVisibility());
  EXPECT_EQ(contacts, contact_manager()->GetAllowedContacts());
}

TEST_P(
    NearbySharingServiceImplTest,
    SelfShareEnabled_YourDevicesVisibilityOnScreenLock_DefaultSelectedContactsVisibility) {
  const std::set<std::string> contacts = {"1", "2"};
  SetVisibility(nearby_share::mojom::Visibility::kSelectedContacts);
  contact_manager()->SetAllowedContacts(contacts);

  // Lock screen, expect Your Devices visibility.
  session_controller_->SetScreenLocked(true);
  EXPECT_EQ(nearby_share::mojom::Visibility::kYourDevices, GetVisibility());
  EXPECT_EQ(std::set<std::string>(), contact_manager()->GetAllowedContacts());

  // Unlock screen, expect visibility to return to All Contacts.
  session_controller_->SetScreenLocked(false);
  EXPECT_EQ(nearby_share::mojom::Visibility::kSelectedContacts,
            GetVisibility());
  EXPECT_EQ(contacts, contact_manager()->GetAllowedContacts());
}

TEST_P(NearbySharingServiceImplTest,
       UserSelectsHiddenVisibility_HiddenVisibilityOnScreenLock) {
  SetVisibility(nearby_share::mojom::Visibility::kNoOne);
  std::set<std::string> empty_set;
  contact_manager()->SetAllowedContacts(empty_set);

  // Lock screen, expect Hidden visibility.
  session_controller_->SetScreenLocked(true);
  EXPECT_EQ(nearby_share::mojom::Visibility::kNoOne, GetVisibility());
  EXPECT_EQ(empty_set, contact_manager()->GetAllowedContacts());

  // Unlock screen, expect visibility to still be Hidden.
  session_controller_->SetScreenLocked(false);
  EXPECT_EQ(nearby_share::mojom::Visibility::kNoOne, GetVisibility());
  EXPECT_EQ(empty_set, contact_manager()->GetAllowedContacts());
}

INSTANTIATE_TEST_SUITE_P(NearbySharingServiceImplTest,
                         NearbySharingServiceImplTest,
                         testing::Range<size_t>(0, 1 << kTestFeatures.size()));

// The boolean variable represents is service enabled. Visibility represents
// different visibility selections.
using VisibilityReminderTestParams =
    std::tuple<bool, nearby_share::mojom::Visibility, size_t>;

class NearbySharingServiceVisibilityReminderTest
    : public NearbySharingServiceImplTestBase,
      public testing::WithParamInterface<VisibilityReminderTestParams> {
 public:
  NearbySharingServiceVisibilityReminderTest()
      : NearbySharingServiceImplTestBase(std::get<2>(GetParam())) {}
};

TEST_P(NearbySharingServiceVisibilityReminderTest,
       StartOrStopVisibilityReminderTimerAppropriatelyWhenEnableOrDisable) {
  bool is_enabled = std::get<0>(GetParam());
  nearby_share::mojom::Visibility visibility = std::get<1>(GetParam());

  SetVisibility(visibility);
  SetIsEnabled(is_enabled);
  if (is_enabled) {
    switch (visibility) {
      case nearby_share::mojom::Visibility::kAllContacts:
      case nearby_share::mojom::Visibility::kSelectedContacts:
        EXPECT_TRUE(IsVisibilityReminderTimerRunning());
        break;
      case nearby_share::mojom::Visibility::kNoOne:
        EXPECT_FALSE(IsVisibilityReminderTimerRunning());
        break;
      default:
        break;
    }
  } else {
    EXPECT_FALSE(IsVisibilityReminderTimerRunning());
  }
}

TEST_P(NearbySharingServiceVisibilityReminderTest,
       StartOrStopVisibilityReminderTimerAppropriatelyWhenChangeVisibility) {
  bool is_enabled = std::get<0>(GetParam());
  nearby_share::mojom::Visibility visibility = std::get<1>(GetParam());

  SetIsEnabled(is_enabled);
  if (is_enabled) {
    SetVisibility(visibility);
    switch (visibility) {
      case nearby_share::mojom::Visibility::kAllContacts:
      case nearby_share::mojom::Visibility::kSelectedContacts:
        EXPECT_TRUE(IsVisibilityReminderTimerRunning());
        break;
      case nearby_share::mojom::Visibility::kNoOne:
        EXPECT_FALSE(IsVisibilityReminderTimerRunning());
        break;
      default:
        break;
    }
  } else {
    EXPECT_FALSE(IsVisibilityReminderTimerRunning());
  }
}

INSTANTIATE_TEST_SUITE_P(
    NearbySharingServiceImplTest,
    NearbySharingServiceVisibilityReminderTest,
    testing::Combine(
        testing::Bool(),
        testing::Values(nearby_share::mojom::Visibility::kAllContacts,
                        nearby_share::mojom::Visibility::kSelectedContacts,
                        nearby_share::mojom::Visibility::kNoOne),
        testing::Range<size_t>(0, 1 << kTestFeatures.size())));

}  // namespace NearbySharingServiceUnitTests