chromium/fuchsia_web/runners/cast/cast_resolver.cc

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

#include "fuchsia_web/runners/cast/cast_resolver.h"

#include <fidl/fuchsia.component.decl/cpp/fidl.h>
#include <fidl/fuchsia.component/cpp/fidl.h>
#include <fidl/fuchsia.ui.app/cpp/wire_messaging.h>
#include <lib/fidl/cpp/natural_types.h>

#include <stdint.h>

#include <string>
#include <utility>
#include <vector>

#include "base/files/file_util.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/notreached.h"
#include "base/numerics/byte_conversions.h"

namespace {

using fuchsia_component_decl::Ref;

template <typename Protocol>
void DeclareAndExposeProtocol(fuchsia_component_decl::Component& decl) {
  constexpr const char* kProtocolName =
      fidl::DiscoverableProtocolName<Protocol>;
  if (!decl.capabilities()) {
    decl.capabilities().emplace();
  }
  decl.capabilities()->push_back(
      fuchsia_component_decl::Capability::WithProtocol({{
          .name = kProtocolName,
          .source_path = fidl::DiscoverableProtocolDefaultPath<Protocol>,
      }}));
  CHECK(decl.exposes());
  decl.exposes()->push_back(fuchsia_component_decl::Expose::WithProtocol({{
      .source = Ref::WithSelf({}),
      .source_name = kProtocolName,
      .target = Ref::WithParent({}),
      .target_name = kProtocolName,
  }}));
}

uint64_t FetchAbiRevision() {
  constexpr char kPkgAbiRevisionPath[] = "/pkg/meta/fuchsia.abi/abi-revision";

  // Read the Little Endian representation of the unsigned 64-bit integer ABI
  // revision from the file in the metadata directory.
  std::array<uint8_t, 8u> abi_revision_le = {};
  std::optional<uint64_t> read_bytes =
      base::ReadFile(base::FilePath(kPkgAbiRevisionPath), abi_revision_le);
  CHECK_EQ(read_bytes.value(), sizeof(abi_revision_le));
  return base::numerics::U64FromLittleEndian(abi_revision_le);
}

}  // namespace

CastResolver::CastResolver() = default;

CastResolver::~CastResolver() = default;

void CastResolver::Resolve(CastResolver::ResolveRequest& request,
                           CastResolver::ResolveCompleter::Sync& completer) {
  fuchsia_component_decl::Component decl{{
      .program = fuchsia_component_decl::Program{{
          .runner = "cast-runner",
          .info = fuchsia_data::Dictionary{{
              .entries = {},
          }},
      }},

      // TODO(crbug.com/40875550): Replace with attributed-capability expose
      // rules for each protocol, when supported by the framework.
      .uses =
          std::vector{
              fuchsia_component_decl::Use::WithDirectory({{
                  .source = Ref::WithParent({}),
                  .source_name = "svc",
                  .target_path = "/svc",
                  .rights = fuchsia_io::kRwStarDir,
                  .dependency_type =
                      fuchsia_component_decl::DependencyType::kStrong,
              }}),
          },

      // Expose the Binder, from the framework, to allow callers to explicitly
      // start the component.
      .exposes = std::vector{fuchsia_component_decl::Expose::WithProtocol({{
          .source = Ref::WithFramework({}),
          .source_name =
              fidl::DiscoverableProtocolName<fuchsia_component::Binder>,
          .target = Ref::WithParent({}),
          .target_name =
              fidl::DiscoverableProtocolName<fuchsia_component::Binder>,
      }})},
  }};

  // Declare and expose capabilities implemented by the component.
  DeclareAndExposeProtocol<fuchsia_ui_app::ViewProvider>(decl);

  fit::result<fidl::Error, std::vector<uint8_t>> persisted_decl =
      fidl::Persist(decl);
  if (persisted_decl.is_error()) {
    ZX_DLOG(ERROR, persisted_decl.error_value().status())
        << "Error creating persisted decl";
    completer.Reply(
        fit::error(fuchsia_component_resolution::ResolverError::kInternal));
    return;
  }

  // Encode the component manifest into the resolver result.
  static const uint64_t abi_revision = FetchAbiRevision();
  fuchsia_component_resolution::ResolverResolveResponse result{{
      .component = fuchsia_component_resolution::Component{{
          .url = std::move(request.component_url()),
          .decl =
              fuchsia_mem::Data::WithBytes(std::move(persisted_decl.value())),
          .abi_revision = abi_revision,
      }},
  }};

  completer.Reply(fit::ok(std::move(result)));
}

void CastResolver::ResolveWithContext(
    CastResolver::ResolveWithContextRequest& request,
    CastResolver::ResolveWithContextCompleter::Sync& completer) {
  NOTIMPLEMENTED();

  completer.Reply(
      fit::error(fuchsia_component_resolution::ResolverError::kNotSupported));
}