chromium/chrome/browser/lacros/lacros_apps_publisher.cc

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

#include "chrome/browser/lacros/lacros_apps_publisher.h"

#include <utility>

#include "chrome/browser/apps/app_service/media_requests.h"
#include "chrome/browser/lacros/for_which_extension_type.h"
#include "chrome/browser/lacros/lacros_extensions_util.h"
#include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h"
#include "chrome/browser/web_applications/web_app_tab_helper.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/app_constants/constants.h"
#include "content/public/browser/web_contents.h"
#include "extensions/common/extension.h"

LacrosAppsPublisher::LacrosAppsPublisher() {}

LacrosAppsPublisher::~LacrosAppsPublisher() = default;

void LacrosAppsPublisher::Initialize() {
  if (!InitializeCrosapi()) {
    return;
  }

  media_dispatcher_.Observe(MediaCaptureDevicesDispatcher::GetInstance()
                                ->GetMediaStreamCaptureIndicator()
                                .get());
}

bool LacrosAppsPublisher::InitializeCrosapi() {
  auto* service = chromeos::LacrosService::Get();
  if (!service) {
    return false;
  }

  // Ash is too old to support the Lacros publisher interface.
  if (service->GetInterfaceVersion<crosapi::mojom::Crosapi>() <
      int{crosapi::mojom::Crosapi::kBindLacrosAppPublisherMinVersion}) {
    return false;
  }

  service->BindPendingReceiverOrRemote<
      mojo::PendingReceiver<crosapi::mojom::AppPublisher>,
      &crosapi::mojom::Crosapi::BindLacrosAppPublisher>(
      publisher_.BindNewPipeAndPassReceiver());
  return true;
}

void LacrosAppsPublisher::PublishCapabilityAccesses(
    std::vector<apps::CapabilityAccessPtr> accesses) {
  publisher_->OnCapabilityAccesses(std::move(accesses));
}

void LacrosAppsPublisher::OnIsCapturingVideoChanged(
    content::WebContents* web_contents,
    bool is_capturing_video) {
  if (!ShouldModifyCapabilityAccess(web_contents)) {
    return;
  }

  auto result = media_requests_.UpdateCameraState(
      app_constants::kChromeAppId, web_contents, is_capturing_video);
  ModifyCapabilityAccess(result.camera, result.microphone);
}

void LacrosAppsPublisher::OnIsCapturingAudioChanged(
    content::WebContents* web_contents,
    bool is_capturing_audio) {
  if (!ShouldModifyCapabilityAccess(web_contents)) {
    return;
  }

  auto result = media_requests_.UpdateMicrophoneState(
      app_constants::kChromeAppId, web_contents, is_capturing_audio);
  ModifyCapabilityAccess(result.camera, result.microphone);
}

bool LacrosAppsPublisher::ShouldModifyCapabilityAccess(
    content::WebContents* web_contents) {
  // The web app publisher is responsible to handle `web_contents` for web
  // apps.
  const webapps::AppId* web_app_id =
      web_app::WebAppTabHelper::GetAppId(web_contents);
  if (web_app_id) {
    return false;
  }

  // The lacros extension app publisher is responsible to handle `web_contents`
  // for chrome apps.
  const auto* extension =
      lacros_extensions_util::MaybeGetExtension(web_contents);
  if (extension && InitForChromeApps().Matches(extension)) {
    return false;
  }

  return true;
}

void LacrosAppsPublisher::ModifyCapabilityAccess(
    std::optional<bool> accessing_camera,
    std::optional<bool> accessing_microphone) {
  if (!accessing_camera.has_value() && !accessing_microphone.has_value()) {
    return;
  }

  std::vector<apps::CapabilityAccessPtr> capability_accesses;
  auto capability_access =
      std::make_unique<apps::CapabilityAccess>(app_constants::kLacrosAppId);
  capability_access->camera = accessing_camera;
  capability_access->microphone = accessing_microphone;
  capability_accesses.push_back(std::move(capability_access));

  PublishCapabilityAccesses(std::move(capability_accesses));
}