chromium/chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.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 "chromeos/components/cdm_factory_daemon/cdm_factory_daemon_proxy_lacros.h"

#include "base/logging.h"
#include "base/no_destructor.h"
#include "chromeos/lacros/lacros_service.h"
#include "mojo/public/cpp/bindings/generic_pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"

namespace chromeos {

CdmFactoryDaemonProxyLacros::CdmFactoryDaemonProxyLacros()
    : CdmFactoryDaemonProxy() {}

CdmFactoryDaemonProxyLacros::~CdmFactoryDaemonProxyLacros() = default;

void CdmFactoryDaemonProxyLacros::Create(
    mojo::PendingReceiver<BrowserCdmFactory> receiver) {
  // We do not want to use a SelfOwnedReceiver for the main implementation here
  // because if the GPU process goes down, there's no reason to drop our
  // connection to ash-chrome that we may have established already. However, the
  // connection between lacros-GPU and lacros-browser uses a ReceiverSet, which
  // is self-destructing on disconnect.
  GetInstance().BindReceiver(std::move(receiver));
}

void CdmFactoryDaemonProxyLacros::CreateFactory(
    const std::string& key_system,
    CreateFactoryCallback callback) {
  DCHECK(mojo_task_runner_->RunsTasksInCurrentSequence());
  DVLOG(1) << "CdmFactoryDaemonProxyLacros::CreateFactory called";
  if (ash_remote_) {
    DVLOG(1) << "BrowserCdmFactory mojo connection already exists, re-use it";
    GetFactoryInterface(key_system, std::move(callback));
    return;
  }

  EstablishAshConnection(
      base::BindOnce(&CdmFactoryDaemonProxyLacros::GetFactoryInterface,
                     base::Unretained(this), key_system, std::move(callback)));
}

void CdmFactoryDaemonProxyLacros::GetHwConfigData(
    GetHwConfigDataCallback callback) {
  DCHECK(mojo_task_runner_->RunsTasksInCurrentSequence());
  DVLOG(1) << "CdmFactoryDaemonProxyLacros::GetHwConfigData called";
  if (ash_remote_) {
    DVLOG(1) << "BrowserCdmFactory mojo connection already exists, re-use it";
    ProxyGetHwConfigData(std::move(callback));
    return;
  }

  EstablishAshConnection(
      base::BindOnce(&CdmFactoryDaemonProxyLacros::ProxyGetHwConfigData,
                     base::Unretained(this), std::move(callback)));
}

void CdmFactoryDaemonProxyLacros::GetOutputProtection(
    mojo::PendingReceiver<cdm::mojom::OutputProtection> output_protection) {
  if (ash_remote_) {
    // This should always be bound unless it became disconnected in the middle
    // of setting things up.
    ash_remote_->GetOutputProtection(std::move(output_protection));
  }
}

void CdmFactoryDaemonProxyLacros::GetScreenResolutions(
    GetScreenResolutionsCallback callback) {
  if (ash_remote_) {
    // This should always be bound unless it became disconnected in the middle
    // of setting things up.
    ash_remote_->GetScreenResolutions(std::move(callback));
  } else {
    std::move(callback).Run(std::vector<gfx::Size>());
  }
}

void CdmFactoryDaemonProxyLacros::GetAndroidHwKeyData(
    const std::vector<uint8_t>& key_id,
    const std::vector<uint8_t>& hw_identifier,
    GetAndroidHwKeyDataCallback callback) {
  // This should only go through ash-chrome.
  NOTREACHED_IN_MIGRATION();
}

void CdmFactoryDaemonProxyLacros::AllocateSecureBuffer(
    uint32_t size,
    AllocateSecureBufferCallback callback) {
  if (ash_remote_) {
    // This should always be bound unless it became disconnected in the middle
    // of setting things up.
    ash_remote_->AllocateSecureBuffer(size, std::move(callback));
  } else {
    std::move(callback).Run(mojo::PlatformHandle());
  }
}

void CdmFactoryDaemonProxyLacros::ParseEncryptedSliceHeader(
    uint64_t secure_handle,
    uint32_t offset,
    const std::vector<uint8_t>& stream_data,
    ParseEncryptedSliceHeaderCallback callback) {
  if (ash_remote_) {
    // This should always be bound unless it became disconnected in the middle
    // of setting things up.
    ash_remote_->ParseEncryptedSliceHeader(secure_handle, offset, stream_data,
                                           std::move(callback));
  } else {
    std::move(callback).Run(false, {});
  }
}

void CdmFactoryDaemonProxyLacros::EstablishAshConnection(
    base::OnceClosure callback) {
  // This may have happened already.
  if (ash_remote_) {
    std::move(callback).Run();
    return;
  }

  auto* service = LacrosService::Get();
  if (!service || !service->IsSupported<cdm::mojom::BrowserCdmFactory>()) {
    std::move(callback).Run();
    return;
  }
  // For Lacros, we connect to the ash-chrome browser process which will proxy
  // the connection to the daemon.
  service->BindBrowserCdmFactory(
      mojo::GenericPendingReceiver(ash_remote_.BindNewPipeAndPassReceiver()));
  std::move(callback).Run();
  return;
}

void CdmFactoryDaemonProxyLacros::GetFactoryInterface(
    const std::string& key_system,
    CreateFactoryCallback callback) {
  if (!ash_remote_) {
    LOG(ERROR) << "ash_remote_ interface is not connected";
    std::move(callback).Run(mojo::PendingRemote<cdm::mojom::CdmFactory>());
    return;
  }
  ash_remote_->CreateFactory(key_system, std::move(callback));
}

void CdmFactoryDaemonProxyLacros::ProxyGetHwConfigData(
    GetHwConfigDataCallback callback) {
  if (!ash_remote_) {
    LOG(ERROR) << "ash_remote_ interface is not connected";
    std::move(callback).Run(false, std::vector<uint8_t>());
    return;
  }
  ash_remote_->GetHwConfigData(std::move(callback));
}

// static
CdmFactoryDaemonProxyLacros& CdmFactoryDaemonProxyLacros::GetInstance() {
  static base::NoDestructor<CdmFactoryDaemonProxyLacros> instance;
  return *instance;
}

}  // namespace chromeos