// 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