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

#include "components/arc/arc_bridge_host_impl.h"

#include <algorithm>
#include <utility>

#include "ash/public/interfaces/ash_message_center_controller.mojom.h"
#include "ash/public/interfaces/constants.mojom.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "components/arc/arc_bridge_service.h"
#include "components/arc/mojo_channel.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"

namespace arc {

ArcBridgeHostImpl::ArcBridgeHostImpl(ArcBridgeService* arc_bridge_service,
                                     mojom::ArcBridgeInstancePtr instance)
    : arc_bridge_service_(arc_bridge_service),
      binding_(this),
      instance_(std::move(instance)) {
  DCHECK(arc_bridge_service_);
  DCHECK(instance_.is_bound());
  instance_.set_connection_error_handler(
      base::BindOnce(&ArcBridgeHostImpl::OnClosed, base::Unretained(this)));
  mojom::ArcBridgeHostPtr host_proxy;
  binding_.Bind(mojo::MakeRequest(&host_proxy));
  instance_->Init(std::move(host_proxy));
}

ArcBridgeHostImpl::~ArcBridgeHostImpl() {
  OnClosed();
}

void ArcBridgeHostImpl::OnAccessibilityHelperInstanceReady(
    mojom::AccessibilityHelperInstancePtr accessibility_helper_ptr) {
  OnInstanceReady(arc_bridge_service_->accessibility_helper(),
                  std::move(accessibility_helper_ptr));
}

void ArcBridgeHostImpl::OnAppInstanceReady(mojom::AppInstancePtr app_ptr) {
  OnInstanceReady(arc_bridge_service_->app(), std::move(app_ptr));
}

void ArcBridgeHostImpl::OnAppfuseInstanceReady(
    mojom::AppfuseInstancePtr appfuse_ptr) {
  OnInstanceReady(arc_bridge_service_->appfuse(), std::move(appfuse_ptr));
}

void ArcBridgeHostImpl::OnAudioInstanceReady(
    mojom::AudioInstancePtr audio_ptr) {
  OnInstanceReady(arc_bridge_service_->audio(), std::move(audio_ptr));
}

void ArcBridgeHostImpl::OnAuthInstanceReady(mojom::AuthInstancePtr auth_ptr) {
  OnInstanceReady(arc_bridge_service_->auth(), std::move(auth_ptr));
}

void ArcBridgeHostImpl::OnBackupSettingsInstanceReady(
    mojom::BackupSettingsInstancePtr backup_settings_ptr) {
  OnInstanceReady(arc_bridge_service_->backup_settings(),
                  std::move(backup_settings_ptr));
}

void ArcBridgeHostImpl::OnBluetoothInstanceReady(
    mojom::BluetoothInstancePtr bluetooth_ptr) {
  OnInstanceReady(arc_bridge_service_->bluetooth(), std::move(bluetooth_ptr));
}

void ArcBridgeHostImpl::OnBootPhaseMonitorInstanceReady(
    mojom::BootPhaseMonitorInstancePtr boot_phase_monitor_ptr) {
  OnInstanceReady(arc_bridge_service_->boot_phase_monitor(),
                  std::move(boot_phase_monitor_ptr));
}

void ArcBridgeHostImpl::OnCastReceiverInstanceReady(
    mojom::CastReceiverInstancePtr cast_receiver_ptr) {
  OnInstanceReady(arc_bridge_service_->cast_receiver(),
                  std::move(cast_receiver_ptr));
}

void ArcBridgeHostImpl::OnCertStoreInstanceReady(
    mojom::CertStoreInstancePtr instance_ptr) {
  OnInstanceReady(arc_bridge_service_->cert_store(), std::move(instance_ptr));
}

void ArcBridgeHostImpl::OnClipboardInstanceReady(
    mojom::ClipboardInstancePtr clipboard_ptr) {
  OnInstanceReady(arc_bridge_service_->clipboard(), std::move(clipboard_ptr));
}

void ArcBridgeHostImpl::OnCrashCollectorInstanceReady(
    mojom::CrashCollectorInstancePtr crash_collector_ptr) {
  OnInstanceReady(arc_bridge_service_->crash_collector(),
                  std::move(crash_collector_ptr));
}

void ArcBridgeHostImpl::OnDiskQuotaInstanceReady(
    mojom::DiskQuotaInstancePtr disk_quota_ptr) {
  OnInstanceReady(arc_bridge_service_->disk_quota(), std::move(disk_quota_ptr));
}

void ArcBridgeHostImpl::OnEnterpriseReportingInstanceReady(
    mojom::EnterpriseReportingInstancePtr enterprise_reporting_ptr) {
  OnInstanceReady(arc_bridge_service_->enterprise_reporting(),
                  std::move(enterprise_reporting_ptr));
}

void ArcBridgeHostImpl::OnFileSystemInstanceReady(
    mojom::FileSystemInstancePtr file_system_ptr) {
  OnInstanceReady(arc_bridge_service_->file_system(),
                  std::move(file_system_ptr));
}

void ArcBridgeHostImpl::OnImeInstanceReady(mojom::ImeInstancePtr ime_ptr) {
  OnInstanceReady(arc_bridge_service_->ime(), std::move(ime_ptr));
}

void ArcBridgeHostImpl::OnInputMethodManagerInstanceReady(
    mojom::InputMethodManagerInstancePtr input_method_manager_ptr) {
  OnInstanceReady(arc_bridge_service_->input_method_manager(),
                  std::move(input_method_manager_ptr));
}

void ArcBridgeHostImpl::OnIntentHelperInstanceReady(
    mojom::IntentHelperInstancePtr intent_helper_ptr) {
  OnInstanceReady(arc_bridge_service_->intent_helper(),
                  std::move(intent_helper_ptr));
}

void ArcBridgeHostImpl::OnKioskInstanceReady(
    mojom::KioskInstancePtr kiosk_ptr) {
  OnInstanceReady(arc_bridge_service_->kiosk(), std::move(kiosk_ptr));
}

void ArcBridgeHostImpl::OnLockScreenInstanceReady(
    mojom::LockScreenInstancePtr lock_screen_ptr) {
  OnInstanceReady(arc_bridge_service_->lock_screen(),
                  std::move(lock_screen_ptr));
}

void ArcBridgeHostImpl::OnMetricsInstanceReady(
    mojom::MetricsInstancePtr metrics_ptr) {
  OnInstanceReady(arc_bridge_service_->metrics(), std::move(metrics_ptr));
}

void ArcBridgeHostImpl::OnMidisInstanceReady(
    mojom::MidisInstancePtr midis_ptr) {
  OnInstanceReady(arc_bridge_service_->midis(), std::move(midis_ptr));
}

void ArcBridgeHostImpl::OnNetInstanceReady(mojom::NetInstancePtr net_ptr) {
  OnInstanceReady(arc_bridge_service_->net(), std::move(net_ptr));
}

void ArcBridgeHostImpl::OnNotificationsInstanceReady(
    mojom::NotificationsInstancePtr notifications_ptr) {
  // Forward notification instance to ash.
  ash::mojom::AshMessageCenterControllerPtr ash_message_center_controller;
  content::ServiceManagerConnection::GetForProcess()
      ->GetConnector()
      ->BindInterface(ash::mojom::kServiceName, &ash_message_center_controller);
  ash_message_center_controller->SetArcNotificationsInstance(
      std::move(notifications_ptr));
}

void ArcBridgeHostImpl::OnObbMounterInstanceReady(
    mojom::ObbMounterInstancePtr obb_mounter_ptr) {
  OnInstanceReady(arc_bridge_service_->obb_mounter(),
                  std::move(obb_mounter_ptr));
}

void ArcBridgeHostImpl::OnOemCryptoInstanceReady(
    mojom::OemCryptoInstancePtr oemcrypto_ptr) {
  OnInstanceReady(arc_bridge_service_->oemcrypto(), std::move(oemcrypto_ptr));
}

void ArcBridgeHostImpl::OnPolicyInstanceReady(
    mojom::PolicyInstancePtr policy_ptr) {
  OnInstanceReady(arc_bridge_service_->policy(), std::move(policy_ptr));
}

void ArcBridgeHostImpl::OnPowerInstanceReady(
    mojom::PowerInstancePtr power_ptr) {
  OnInstanceReady(arc_bridge_service_->power(), std::move(power_ptr));
}

void ArcBridgeHostImpl::OnPrintInstanceReady(
    mojom::PrintInstancePtr print_ptr) {
  OnInstanceReady(arc_bridge_service_->print(), std::move(print_ptr));
}

void ArcBridgeHostImpl::OnProcessInstanceReady(
    mojom::ProcessInstancePtr process_ptr) {
  OnInstanceReady(arc_bridge_service_->process(), std::move(process_ptr));
}

void ArcBridgeHostImpl::OnRotationLockInstanceReady(
    mojom::RotationLockInstancePtr rotation_lock_ptr) {
  OnInstanceReady(arc_bridge_service_->rotation_lock(),
                  std::move(rotation_lock_ptr));
}

void ArcBridgeHostImpl::OnScreenCaptureInstanceReady(
    mojom::ScreenCaptureInstancePtr screen_capture_ptr) {
  OnInstanceReady(arc_bridge_service_->screen_capture(),
                  std::move(screen_capture_ptr));
}

void ArcBridgeHostImpl::OnStorageManagerInstanceReady(
    mojom::StorageManagerInstancePtr storage_manager_ptr) {
  OnInstanceReady(arc_bridge_service_->storage_manager(),
                  std::move(storage_manager_ptr));
}

void ArcBridgeHostImpl::OnTimerInstanceReady(
    mojom::TimerInstancePtr timer_ptr) {
  OnInstanceReady(arc_bridge_service_->timer(), std::move(timer_ptr));
}

void ArcBridgeHostImpl::OnTracingInstanceReady(
    mojom::TracingInstancePtr tracing_ptr) {
  OnInstanceReady(arc_bridge_service_->tracing(), std::move(tracing_ptr));
}

void ArcBridgeHostImpl::OnTtsInstanceReady(mojom::TtsInstancePtr tts_ptr) {
  OnInstanceReady(arc_bridge_service_->tts(), std::move(tts_ptr));
}

void ArcBridgeHostImpl::OnUsbHostInstanceReady(
    mojom::UsbHostInstancePtr usb_host_ptr) {
  OnInstanceReady(arc_bridge_service_->usb_host(), std::move(usb_host_ptr));
}

void ArcBridgeHostImpl::OnVideoInstanceReady(
    mojom::VideoInstancePtr video_ptr) {
  OnInstanceReady(arc_bridge_service_->video(), std::move(video_ptr));
}

void ArcBridgeHostImpl::OnVoiceInteractionArcHomeInstanceReady(
    mojom::VoiceInteractionArcHomeInstancePtr home_ptr) {
  OnInstanceReady(arc_bridge_service_->voice_interaction_arc_home(),
                  std::move(home_ptr));
}

void ArcBridgeHostImpl::OnVoiceInteractionFrameworkInstanceReady(
    mojom::VoiceInteractionFrameworkInstancePtr framework_ptr) {
  OnInstanceReady(arc_bridge_service_->voice_interaction_framework(),
                  std::move(framework_ptr));
}

void ArcBridgeHostImpl::OnVolumeMounterInstanceReady(
    mojom::VolumeMounterInstancePtr volume_mounter_ptr) {
  OnInstanceReady(arc_bridge_service_->volume_mounter(),
                  std::move(volume_mounter_ptr));
}

void ArcBridgeHostImpl::OnWakeLockInstanceReady(
    mojom::WakeLockInstancePtr wakelock_ptr) {
  OnInstanceReady(arc_bridge_service_->wake_lock(), std::move(wakelock_ptr));
}

void ArcBridgeHostImpl::OnWallpaperInstanceReady(
    mojom::WallpaperInstancePtr wallpaper_ptr) {
  OnInstanceReady(arc_bridge_service_->wallpaper(), std::move(wallpaper_ptr));
}

void ArcBridgeHostImpl::OnClosed() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  VLOG(1) << "Mojo connection lost";

  // Close all mojo channels.
  mojo_channels_.clear();
  instance_.reset();
  if (binding_.is_bound())
    binding_.Close();
}

template <typename InstanceType, typename HostType>
void ArcBridgeHostImpl::OnInstanceReady(
    ConnectionHolder<InstanceType, HostType>* holder,
    mojo::InterfacePtr<InstanceType> ptr) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  DCHECK(binding_.is_bound());
  DCHECK(ptr.is_bound());

  // Track |channel|'s lifetime via |mojo_channels_| so that it will be
  // closed on ArcBridgeHost/Instance closing or the ArcBridgeHostImpl's
  // destruction.
  auto* channel =
      new MojoChannel<InstanceType, HostType>(holder, std::move(ptr));
  mojo_channels_.emplace_back(channel);

  // Since |channel| is managed by |mojo_channels_|, its lifetime is shorter
  // than |this|. Thus, the connection error handler will be invoked only
  // when |this| is alive and base::Unretained is safe here.
  channel->set_connection_error_handler(base::BindOnce(
      &ArcBridgeHostImpl::OnChannelClosed, base::Unretained(this), channel));

  // Call QueryVersion so that the version info is properly stored in the
  // InterfacePtr<T>.
  channel->QueryVersion();
}

void ArcBridgeHostImpl::OnChannelClosed(MojoChannelBase* channel) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  mojo_channels_.erase(
      std::find_if(mojo_channels_.begin(), mojo_channels_.end(),
                   [channel](std::unique_ptr<MojoChannelBase>& ptr) {
                     return ptr.get() == channel;
                   }));
}

}  // namespace arc
