chromium/chrome/browser/ash/crosapi/payment_app_instance_ash.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/ash/crosapi/payment_app_instance_ash.h"

#include <utility>

#include "ash/components/arc/pay/arc_payment_app_bridge.h"
#include "ash/public/cpp/external_arc/overlay/arc_overlay_manager.h"
#include "base/check.h"
#include "base/check_is_test.h"
#include "base/logging.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "components/payments/core/native_error_strings.h"
#include "components/services/app_service/public/cpp/instance_registry.h"
#include "content/public/browser/browser_thread.h"

namespace {

void OnInvokePaymentApp(
    crosapi::PaymentAppInstanceAsh::InvokePaymentAppCallback callback,
    base::OnceClosure overlay_state,
    chromeos::payments::mojom::InvokePaymentAppResultPtr response) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);

  // Dismiss and prevent any further overlays
  if (overlay_state) {
    std::move(overlay_state).Run();
  }

  std::move(callback).Run(std::move(response));
}

}  // namespace

namespace crosapi {

PaymentAppInstanceAsh::PaymentAppInstanceAsh() = default;

PaymentAppInstanceAsh::~PaymentAppInstanceAsh() = default;

void PaymentAppInstanceAsh::BindReceiver(
    mojo::PendingReceiver<chromeos::payments::mojom::PaymentAppInstance>
        receiver) {
  receivers_.Add(this, std::move(receiver));
}

void PaymentAppInstanceAsh::Initialize(Profile* profile) {
  CHECK(profile);
  // This method is called during crosapi binding, which could happen more than
  // once if Lacros instance is created more than once (e.g. crash and restart).
  if (profile_observation_.IsObservingSource(profile)) {
    VLOG(1) << "PaymentAppInstanceAsh is already initialized. Skip init.";
    return;
  }
  profile_observation_.Observe(profile);
  payment_app_service_ =
      arc::ArcPaymentAppBridge::GetForBrowserContext(profile);
  instance_registry_ =
      &apps::AppServiceProxyFactory::GetForProfile(profile)->InstanceRegistry();
}

void PaymentAppInstanceAsh::IsPaymentImplemented(
    const std::string& package_name,
    IsPaymentImplementedCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (package_name.empty()) {
    // Chrome OS supports Android app payment only through a TWA. An empty
    // `package_name` indicates that Chrome was not launched from a TWA,
    // so there're no payment apps available.
    std::move(callback).Run(
        chromeos::payments::mojom::IsPaymentImplementedResult::NewValid(
            chromeos::payments::mojom::IsPaymentImplementedValidResult::New()));
    return;
  }

  if (!payment_app_service_) {
    std::move(callback).Run(
        chromeos::payments::mojom::IsPaymentImplementedResult::NewError(
            payments::errors::kUnableToInvokeAndroidPaymentApps));
    return;
  }

  payment_app_service_->IsPaymentImplemented(package_name, std::move(callback));
}

void PaymentAppInstanceAsh::IsReadyToPay(
    chromeos::payments::mojom::PaymentParametersPtr parameters,
    IsReadyToPayCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!payment_app_service_) {
    std::move(callback).Run(
        chromeos::payments::mojom::IsReadyToPayResult::NewError(
            payments::errors::kUnableToInvokeAndroidPaymentApps));
    return;
  }

  payment_app_service_->IsReadyToPay(std::move(parameters),
                                     std::move(callback));
}

void PaymentAppInstanceAsh::InvokePaymentApp(
    chromeos::payments::mojom::PaymentParametersPtr parameters,
    InvokePaymentAppCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!payment_app_service_ || !parameters->request_token.has_value() ||
      !parameters->twa_instance_identifier.has_value() || !instance_registry_) {
    std::move(callback).Run(
        chromeos::payments::mojom::InvokePaymentAppResult::NewError(
            payments::errors::kUnableToInvokeAndroidPaymentApps));
    return;
  }

  ash::ArcOverlayManager* const overlay_manager =
      ash::ArcOverlayManager::instance();

  aura::Window* host_window = nullptr;

  instance_registry_->ForOneInstance(
      parameters->twa_instance_identifier.value(),
      [&host_window](const apps::InstanceUpdate& update) {
        host_window = update.Window();
      });

  if (!host_window) {
    std::move(callback).Run(
        chromeos::payments::mojom::InvokePaymentAppResult::NewError(
            payments::errors::kUnableToInvokeAndroidPaymentApps));
    return;
  }

  base::OnceClosure overlay_state =
      overlay_manager
          ->RegisterHostWindow(parameters->request_token.value(), host_window)
          .Release();

  payment_app_service_->InvokePaymentApp(
      std::move(parameters),
      base::BindOnce(&OnInvokePaymentApp, std::move(callback),
                     std::move(overlay_state)));
}

void PaymentAppInstanceAsh::AbortPaymentApp(const std::string& request_token,
                                            AbortPaymentAppCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!payment_app_service_) {
    std::move(callback).Run(false);
    return;
  }

  payment_app_service_->AbortPaymentApp(request_token, std::move(callback));
}

void PaymentAppInstanceAsh::SetPaymentAppServiceForTesting(
    arc::ArcPaymentAppBridge* payment_app_service) {
  payment_app_service_ = payment_app_service;
}

void PaymentAppInstanceAsh::SetInstanceRegistryForTesting(
    apps::InstanceRegistry* instance_registry) {
  instance_registry_ = instance_registry;
}

void PaymentAppInstanceAsh::OnProfileWillBeDestroyed(Profile* profile) {
  payment_app_service_ = nullptr;
  instance_registry_ = nullptr;
  profile_observation_.Reset();
}

}  // namespace crosapi