chromium/chromeos/ash/components/tether/tether_component_impl.cc

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

#include "chromeos/ash/components/tether/tether_component_impl.h"

#include <memory>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/components/tether/active_host.h"
#include "chromeos/ash/components/tether/asynchronous_shutdown_object_container_impl.h"
#include "chromeos/ash/components/tether/crash_recovery_manager_impl.h"
#include "chromeos/ash/components/tether/host_scan_scheduler.h"
#include "chromeos/ash/components/tether/persistent_host_scan_cache_impl.h"
#include "chromeos/ash/components/tether/synchronous_shutdown_object_container_impl.h"
#include "chromeos/ash/components/tether/tether_disconnector.h"
#include "chromeos/ash/components/tether/tether_host_response_recorder.h"
#include "chromeos/ash/components/tether/tether_session_completion_logger.h"
#include "chromeos/ash/components/tether/wifi_hotspot_disconnector_impl.h"
#include "chromeos/ash/services/device_sync/public/cpp/device_sync_client.h"
#include "chromeos/ash/services/secure_channel/public/cpp/client/secure_channel_client.h"
#include "components/pref_registry/pref_registry_syncable.h"

namespace ash {

namespace tether {

namespace {

void OnDisconnectErrorDuringShutdown(const std::string& error_name) {
  PA_LOG(WARNING) << "Error disconnecting from Tether network during shutdown; "
                  << "Error name: " << error_name;
}

TetherSessionCompletionLogger::SessionCompletionReason
GetSessionCompletionReasonFromShutdownReason(
    TetherComponent::ShutdownReason shutdown_reason) {
  switch (shutdown_reason) {
    case TetherComponent::ShutdownReason::OTHER:
      return TetherSessionCompletionLogger::SessionCompletionReason::OTHER;
    case TetherComponent::ShutdownReason::USER_LOGGED_OUT:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          USER_LOGGED_OUT;
    case TetherComponent::ShutdownReason::USER_CLOSED_LID:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          USER_CLOSED_LID;
    case TetherComponent::ShutdownReason::PREF_DISABLED:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          PREF_DISABLED;
    case TetherComponent::ShutdownReason::BLUETOOTH_DISABLED:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          BLUETOOTH_DISABLED;
    case TetherComponent::ShutdownReason::CELLULAR_DISABLED:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          CELLULAR_DISABLED;
    case TetherComponent::ShutdownReason::MULTIDEVICE_HOST_UNVERIFIED:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          MULTIDEVICE_HOST_UNVERIFIED;
    case TetherComponent::ShutdownReason::BETTER_TOGETHER_SUITE_DISABLED:
      return TetherSessionCompletionLogger::SessionCompletionReason::
          BETTER_TOGETHER_SUITE_DISABLED;
    default:
      break;
  }

  return TetherSessionCompletionLogger::SessionCompletionReason::OTHER;
}

}  // namespace

// static
TetherComponentImpl::Factory* TetherComponentImpl::Factory::factory_instance_ =
    nullptr;

// static
std::unique_ptr<TetherComponent> TetherComponentImpl::Factory::Create(
    device_sync::DeviceSyncClient* device_sync_client,
    secure_channel::SecureChannelClient* secure_channel_client,
    TetherHostFetcher* tether_host_fetcher,
    NotificationPresenter* notification_presenter,
    GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker,
    PrefService* pref_service,
    NetworkHandler* network_handler,
    NetworkConnect* network_connect,
    scoped_refptr<device::BluetoothAdapter> adapter,
    session_manager::SessionManager* session_manager) {
  if (factory_instance_) {
    return factory_instance_->CreateInstance(
        device_sync_client, secure_channel_client, tether_host_fetcher,
        notification_presenter, gms_core_notifications_state_tracker,
        pref_service, network_handler, network_connect, adapter,
        session_manager);
  }

  return base::WrapUnique(new TetherComponentImpl(
      device_sync_client, secure_channel_client, tether_host_fetcher,
      notification_presenter, gms_core_notifications_state_tracker,
      pref_service, network_handler, network_connect, adapter,
      session_manager));
}

// static
void TetherComponentImpl::Factory::SetFactoryForTesting(Factory* factory) {
  factory_instance_ = factory;
}

// static
void TetherComponentImpl::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  ActiveHost::RegisterPrefs(registry);
  PersistentHostScanCacheImpl::RegisterPrefs(registry);
  TetherHostResponseRecorder::RegisterPrefs(registry);
  WifiHotspotDisconnectorImpl::RegisterPrefs(registry);
}

TetherComponentImpl::TetherComponentImpl(
    device_sync::DeviceSyncClient* device_sync_client,
    secure_channel::SecureChannelClient* secure_channel_client,
    TetherHostFetcher* tether_host_fetcher,
    NotificationPresenter* notification_presenter,
    GmsCoreNotificationsStateTrackerImpl* gms_core_notifications_state_tracker,
    PrefService* pref_service,
    NetworkHandler* network_handler,
    NetworkConnect* network_connect,
    scoped_refptr<device::BluetoothAdapter> adapter,
    session_manager::SessionManager* session_manager)
    : asynchronous_shutdown_object_container_(
          AsynchronousShutdownObjectContainerImpl::Factory::Create(
              device_sync_client,
              secure_channel_client,
              tether_host_fetcher,
              network_handler->network_state_handler(),
              network_handler->managed_network_configuration_handler(),
              network_handler->network_connection_handler(),
              pref_service)),
      synchronous_shutdown_object_container_(
          SynchronousShutdownObjectContainerImpl::Factory::Create(
              asynchronous_shutdown_object_container_.get(),
              notification_presenter,
              gms_core_notifications_state_tracker,
              pref_service,
              network_handler,
              network_connect,
              session_manager,
              device_sync_client,
              secure_channel_client)),
      crash_recovery_manager_(CrashRecoveryManagerImpl::Factory::Create(
          network_handler->network_state_handler(),
          synchronous_shutdown_object_container_->active_host(),
          synchronous_shutdown_object_container_->host_scan_cache())) {
  crash_recovery_manager_->RestorePreCrashStateIfNecessary(
      base::BindOnce(&TetherComponentImpl::OnPreCrashStateRestored,
                     weak_ptr_factory_.GetWeakPtr()));
}

TetherComponentImpl::~TetherComponentImpl() = default;

void TetherComponentImpl::RequestShutdown(
    const ShutdownReason& shutdown_reason) {
  has_shutdown_been_requested_ = true;

  // If shutdown has already happened, there is nothing else to do.
  if (status() != TetherComponent::Status::ACTIVE) {
    return;
  }

  shutdown_reason_ = shutdown_reason;

  // If crash recovery has not yet completed, wait for it to complete before
  // continuing.
  if (crash_recovery_manager_) {
    return;
  }

  InitiateShutdown();
}

void TetherComponentImpl::OnPreCrashStateRestored() {
  // |crash_recovery_manager_| is no longer needed since it has completed.
  crash_recovery_manager_.reset();

  if (has_shutdown_been_requested_) {
    InitiateShutdown();
    return;
  }

  // Start a scan now that the Tether module has started up.
  synchronous_shutdown_object_container_->host_scan_scheduler()
      ->AttemptScanIfOffline();
}

void TetherComponentImpl::InitiateShutdown() {
  DCHECK(has_shutdown_been_requested_);
  DCHECK(!crash_recovery_manager_);
  DCHECK(status() == TetherComponent::Status::ACTIVE);

  ActiveHost* active_host =
      synchronous_shutdown_object_container_->active_host();
  TetherDisconnector* tether_disconnector =
      synchronous_shutdown_object_container_->tether_disconnector();

  // If there is an active connection, it needs to be disconnected before the
  // Tether component is shut down.
  if (active_host->GetActiveHostStatus() !=
      ActiveHost::ActiveHostStatus::DISCONNECTED) {
    PA_LOG(VERBOSE) << "There was an active connection during Tether shutdown. "
                    << "Initiating disconnection from device ID \""
                    << multidevice::RemoteDeviceRef::TruncateDeviceIdForLogs(
                           active_host->GetActiveHostDeviceId())
                    << "\".";
    tether_disconnector->DisconnectFromNetwork(
        active_host->GetTetherNetworkGuid(), base::DoNothing(),
        base::BindOnce(&OnDisconnectErrorDuringShutdown),
        GetSessionCompletionReasonFromShutdownReason(shutdown_reason_));
  }

  TransitionToStatus(TetherComponent::Status::SHUTTING_DOWN);

  // Delete objects which can shutdown synchronously immediately.
  synchronous_shutdown_object_container_.reset();

  // Start the shutdown process for objects which shutdown asynchronously.
  asynchronous_shutdown_object_container_->Shutdown(
      base::BindOnce(&TetherComponentImpl::OnShutdownComplete,
                     weak_ptr_factory_.GetWeakPtr()));
}

void TetherComponentImpl::OnShutdownComplete() {
  DCHECK(status() == TetherComponent::Status::SHUTTING_DOWN);

  // Shutdown has completed. The asynchronous objects can now be deleted as
  // well.
  asynchronous_shutdown_object_container_.reset();

  TransitionToStatus(TetherComponent::Status::SHUT_DOWN);
}

}  // namespace tether

}  // namespace ash