blob: 9b48617536ac6123c2e128fdf369c39846b44abb [file] [log] [blame]
// Copyright 2021 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/view_tree/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 view_tree {
using fuchsia::ui::views::ViewRefInstalled;
namespace {
// Check if a ViewRef is valid and 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;
}
// Check if already installed.
const zx_koid_t view_ref_koid = utils::ExtractKoid(view_ref);
if (installed_views_.count(view_ref_koid) != 0) {
callback(InstalledMessage());
return;
}
// Not invalid, not installed.
if (watched_views_.count(view_ref_koid) == 0) {
// If it doesn't exist: add a new entry and setup the invalidation waiter.
auto [it, success] = watched_views_.emplace(view_ref_koid, std::move(view_ref));
FX_DCHECK(success);
// When the event is invalidated, send error message and clean up.
const zx_status_t status =
watched_views_.at(view_ref_koid)
.invalidation_waiter.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);
}
// Save callback until installation or invalidation
watched_views_.at(view_ref_koid).callbacks.emplace_back(std::move(callback));
}
void ViewRefInstalledImpl::OnNewViewTreeSnapshot(std::shared_ptr<const Snapshot> snapshot) {
// Remove any stale views from the installed_views_ set.
for (auto it = installed_views_.begin(); it != installed_views_.end();) {
if (snapshot->view_tree.count(*it) == 0 && snapshot->unconnected_views.count(*it) == 0) {
it = installed_views_.erase(it);
} else {
++it;
}
}
// Update any newly installed views.
for (const auto& [koid, _] : snapshot->view_tree) {
const auto [__, success] = installed_views_.emplace(koid);
if (success) {
OnViewRefInstalled(koid);
}
}
}
void ViewRefInstalledImpl::OnViewRefInstalled(zx_koid_t view_ref_koid) {
const auto it = watched_views_.find(view_ref_koid);
if (it == watched_views_.end()) {
return;
}
for (auto& callback : it->second.callbacks) {
callback(InstalledMessage());
}
watched_views_.erase(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;
}
// No need to check for existence. OnViewRefInvalidated is only called from invalidation_waiter.
for (auto& callback : watched_views_.at(view_ref_koid).callbacks) {
callback(InvalidMessage());
}
watched_views_.erase(view_ref_koid);
}
} // namespace view_tree