chromium/ash/components/arc/camera/arc_camera_bridge.cc

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

#include "ash/components/arc/camera/arc_camera_bridge.h"

#include <utility>

#include "ash/components/arc/arc_browser_context_keyed_service_factory_base.h"
#include "ash/components/arc/session/arc_bridge_service.h"
#include "base/files/scoped_file.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/singleton.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/ash/components/dbus/arc/arc_camera_client.h"
#include "crypto/random.h"
#include "media/capture/video/chromeos/camera_hal_dispatcher_impl.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/platform/platform_channel.h"
#include "mojo/public/cpp/system/invitation.h"
#include "mojo/public/cpp/system/platform_handle.h"

namespace arc {

namespace {

// Singleton factory for ArcCameraBridge.
class ArcCameraBridgeFactory
    : public internal::ArcBrowserContextKeyedServiceFactoryBase<
          ArcCameraBridge,
          ArcCameraBridgeFactory> {
 public:
  // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
  static constexpr const char* kName = "ArcCameraBridgeFactory";

  static ArcCameraBridgeFactory* GetInstance() {
    return base::Singleton<ArcCameraBridgeFactory>::get();
  }

 private:
  friend base::DefaultSingletonTraits<ArcCameraBridgeFactory>;
  ArcCameraBridgeFactory() = default;
  ~ArcCameraBridgeFactory() override = default;
};

}  // namespace

// Runs the callback after verifying the connection to the service.
class ArcCameraBridge::PendingStartCameraServiceResult {
 public:
  PendingStartCameraServiceResult(
      ArcCameraBridge* owner,
      mojo::ScopedMessagePipeHandle pipe,
      ArcCameraBridge::StartCameraServiceCallback callback)
      : owner_(owner),
        service_(
            mojo::PendingRemote<mojom::CameraService>(std::move(pipe), 0u)),
        callback_(std::move(callback)) {
    service_.set_disconnect_handler(
        base::BindOnce(&PendingStartCameraServiceResult::OnError,
                       weak_ptr_factory_.GetWeakPtr()));
    service_.QueryVersion(
        base::BindOnce(&PendingStartCameraServiceResult::OnVersionReady,
                       weak_ptr_factory_.GetWeakPtr()));
  }

  PendingStartCameraServiceResult(const PendingStartCameraServiceResult&) =
      delete;
  PendingStartCameraServiceResult& operator=(
      const PendingStartCameraServiceResult&) = delete;

  ~PendingStartCameraServiceResult() = default;

 private:
  void OnVersionReady(uint32_t version) { Finish(); }

  void OnError() {
    LOG(ERROR) << "Failed to query the camera service version.";
    // Run the callback anyways. The same error will be delivered to the Android
    // side error handler.
    Finish();
  }

  // Runs the callback and removes this object from the owner.
  void Finish() {
    DCHECK(callback_);
    std::move(callback_).Run(service_.Unbind());
    // Destructs |this|.
    owner_->pending_start_camera_service_results_.erase(this);
  }

  const raw_ptr<ArcCameraBridge> owner_;
  mojo::Remote<mojom::CameraService> service_;
  ArcCameraBridge::StartCameraServiceCallback callback_;
  base::WeakPtrFactory<PendingStartCameraServiceResult> weak_ptr_factory_{this};
};

// static
ArcCameraBridge* ArcCameraBridge::GetForBrowserContext(
    content::BrowserContext* context) {
  return ArcCameraBridgeFactory::GetForBrowserContext(context);
}

// static
ArcCameraBridge* ArcCameraBridge::GetForBrowserContextForTesting(
    content::BrowserContext* context) {
  return ArcCameraBridgeFactory::GetForBrowserContextForTesting(context);
}

ArcCameraBridge::ArcCameraBridge(content::BrowserContext* context,
                                 ArcBridgeService* bridge_service)
    : arc_bridge_service_(bridge_service) {
  arc_bridge_service_->camera()->SetHost(this);
}

ArcCameraBridge::~ArcCameraBridge() {
  arc_bridge_service_->camera()->SetHost(nullptr);
}

void ArcCameraBridge::StartCameraService(StartCameraServiceCallback callback) {
  uint8_t random_bytes[16];
  crypto::RandBytes(random_bytes);
  std::string token = base::HexEncode(random_bytes);

  mojo::OutgoingInvitation invitation;
  mojo::PlatformChannel channel;
  mojo::ScopedMessagePipeHandle server_pipe =
      invitation.AttachMessagePipe(token);

  // Run the callback after verifying the connection to the service process.
  auto pending_result = std::make_unique<PendingStartCameraServiceResult>(
      this, std::move(server_pipe), std::move(callback));
  auto* pending_result_ptr = pending_result.get();
  pending_start_camera_service_results_[pending_result_ptr] =
      std::move(pending_result);

  mojo::OutgoingInvitation::Send(std::move(invitation),
                                 base::kNullProcessHandle,
                                 channel.TakeLocalEndpoint());
  base::ScopedFD fd =
      channel.TakeRemoteEndpoint().TakePlatformHandle().TakeFD();

  ash::ArcCameraClient::Get()->StartService(
      fd.get(), token, base::BindOnce([](bool success) {}));
}

void ArcCameraBridge::RegisterCameraHalClientLegacy(
    mojo::PendingRemote<cros::mojom::CameraHalClient> client) {
  NOTREACHED() << "ArcCameraBridge::RegisterCameraHalClientLegacy is "
                  "deprecated. CameraHalClient will not be registered.";
}

void ArcCameraBridge::RegisterCameraHalClient(
    mojo::PendingRemote<cros::mojom::CameraHalClient> client,
    RegisterCameraHalClientCallback callback) {
  auto* dispatcher = media::CameraHalDispatcherImpl::GetInstance();
  auto type = cros::mojom::CameraClientType::ANDROID;
  dispatcher->RegisterClientWithToken(
      std::move(client), type, dispatcher->GetTokenForTrustedClient(type),
      std::move(callback));
}

// static
void ArcCameraBridge::EnsureFactoryBuilt() {
  ArcCameraBridgeFactory::GetInstance();
}

}  // namespace arc