blob: 314d439854bc60b47e354fbfc41105c31bdcbea6 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/gfx/engine/view_ref_installed_impl.h"
#include <lib/async/default.h>
#include <lib/syslog/cpp/macros.h>
#include <zircon/rights.h>
#include "src/ui/scenic/lib/utils/helpers.h"
namespace scenic_impl::gfx {
using fuchsia::ui::views::ViewRefInstalled;
namespace {
// Check if a ViewRef has the correct rights.
bool IsValidViewRef(fuchsia::ui::views::ViewRef& view_ref) {
if (view_ref.reference.get_info(ZX_INFO_HANDLE_VALID, nullptr, /*buffer size*/ 0, nullptr,
nullptr) != ZX_OK) {
FX_LOGS(INFO) << "Bad handle";
return false; // bad handle
}
zx_info_handle_basic_t info{};
if (view_ref.reference.get_info(ZX_INFO_HANDLE_BASIC, &info, /*buffer size*/ sizeof(info),
nullptr, nullptr) != ZX_OK) {
FX_LOGS(INFO) << "No info";
return false; // no info
}
if (!(info.rights & ZX_RIGHT_WAIT)) {
FX_LOGS(INFO) << "Bad rights";
return false; // unexpected rights
}
return true;
}
fuchsia::ui::views::ViewRefInstalled_Watch_Result InvalidMessage() {
return fuchsia::ui::views::ViewRefInstalled_Watch_Result::WithErr(
fuchsia::ui::views::ViewRefInstalledError::INVALID_VIEW_REF);
}
fuchsia::ui::views::ViewRefInstalled_Watch_Result InstalledMessage() {
return fuchsia::ui::views::ViewRefInstalled_Watch_Result::WithResponse({});
}
} // namespace
void ViewRefInstalledImpl::Publish(sys::ComponentContext* app_context) {
FX_DCHECK(app_context);
app_context->outgoing()->AddPublicService<ViewRefInstalled>(bindings_.GetHandler(this));
}
// |ViewRefInstalled|
void ViewRefInstalledImpl::Watch(fuchsia::ui::views::ViewRef view_ref,
ViewRefInstalled::WatchCallback callback) {
if (!IsValidViewRef(view_ref)) {
callback(InvalidMessage());
return;
}
const zx_koid_t view_ref_koid = utils::ExtractKoid(view_ref);
if (is_installed_(view_ref_koid)) {
callback(InstalledMessage());
return;
}
// Not invalid, not installed. Save callback until installation or invalidation.
pending_callbacks_[view_ref_koid].emplace_back(std::move(callback));
// Handle state that only needs one instance per ViewRef.
if (invalidation_waiters_.find(view_ref_koid) == invalidation_waiters_.end()) {
// When the event is invalidated, send error message and clean up.
auto [waiter_it, success] = invalidation_waiters_.emplace(view_ref_koid, std::move(view_ref));
FX_DCHECK(success);
auto& waiter = waiter_it->second.waiter;
zx_status_t status =
waiter.Begin(async_get_default_dispatcher(),
std::bind(&ViewRefInstalledImpl::OnViewRefInvalidated, this, view_ref_koid,
std::placeholders::_3, std::placeholders::_4));
FX_DCHECK(status == ZX_OK);
}
}
void ViewRefInstalledImpl::OnViewRefInstalled(zx_koid_t view_ref_koid) {
const auto it = pending_callbacks_.find(view_ref_koid);
if (it == pending_callbacks_.end()) {
return;
}
for (auto& callback : it->second) {
callback(InstalledMessage());
}
CleanUp(view_ref_koid);
}
void ViewRefInstalledImpl::OnViewRefInvalidated(zx_koid_t view_ref_koid, zx_status_t status,
const zx_packet_signal* signal) {
if (status != ZX_OK) {
FX_LOGS(WARNING)
<< "ViewRefInstalledImpl received an error status code on viewref invalidation: " << status;
}
for (auto& callback : pending_callbacks_.at(view_ref_koid)) {
callback(InvalidMessage());
}
CleanUp(view_ref_koid);
}
void ViewRefInstalledImpl::CleanUp(zx_koid_t view_ref_koid) {
pending_callbacks_.erase(view_ref_koid);
invalidation_waiters_.erase(view_ref_koid);
}
} // namespace scenic_impl::gfx