blob: bdab60fd38921b3334edfddc735cac22c0f1034d [file] [log] [blame]
// Copyright 2022 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/developer/forensics/feedback/annotations/ui_state_provider.h"
#include <fuchsia/ui/activity/cpp/fidl.h>
#include <lib/async/cpp/task.h>
#include <lib/fit/function.h>
#include <lib/zx/time.h>
#include "src/developer/forensics/feedback/annotations/constants.h"
#include "src/developer/forensics/utils/errors.h"
#include "src/developer/forensics/utils/time.h"
namespace forensics::feedback {
namespace {
std::string GetUIStateString(fuchsia::ui::activity::State state) {
switch (state) {
case fuchsia::ui::activity::State::UNKNOWN:
return "unknown";
case fuchsia::ui::activity::State::IDLE:
return "idle";
case fuchsia::ui::activity::State::ACTIVE:
return "active";
}
}
} // namespace
UIStateProvider::UIStateProvider(async_dispatcher_t* dispatcher,
std::shared_ptr<sys::ServiceDirectory> services,
std::unique_ptr<timekeeper::Clock> clock,
std::unique_ptr<backoff::Backoff> backoff)
: dispatcher_(dispatcher),
services_(std::move(services)),
clock_(std::move(clock)),
backoff_(std::move(backoff)) {
StartListening();
}
void UIStateProvider::StartListening() {
provider_ptr_ = services_->Connect<fuchsia::ui::activity::Provider>();
provider_ptr_.set_error_handler([this](zx_status_t status) {
FX_PLOGS(WARNING, status) << "Lost connection to fuchsia.ui.activity.Provider";
// The provider pointer and listener binding connections are not expected to close. Ensure both
// are unbound at the same time to simplify reconnections.
binding_.Unbind();
OnDisconnect();
});
binding_.set_error_handler([this](const zx_status_t status) {
FX_PLOGS(WARNING, status) << "Lost connection to fuchsia.ui.activity.Listener";
// The provider pointer and listener binding connections are not expected to close. Ensure both
// are unbound at the same time to simplify reconnections.
provider_ptr_.Unbind();
OnDisconnect();
});
provider_ptr_->WatchState(binding_.NewBinding(dispatcher_));
}
void UIStateProvider::OnDisconnect() {
current_state_ = ErrorOrString(Error::kConnectionError);
last_transition_time_ = Error::kConnectionError;
if (on_update_) {
on_update_({{kSystemUserActivityCurrentStateKey, *current_state_}});
}
reconnect_task_.PostDelayed(dispatcher_, backoff_->GetNext());
}
std::set<std::string> UIStateProvider::GetAnnotationKeys() {
return {
kSystemUserActivityCurrentStateKey,
kSystemUserActivityCurrentDurationKey,
};
}
std::set<std::string> UIStateProvider::GetKeys() const {
return UIStateProvider::GetAnnotationKeys();
}
void UIStateProvider::OnStateChanged(fuchsia::ui::activity::State state, int64_t transition_time,
OnStateChangedCallback callback) {
current_state_ = ErrorOrString(GetUIStateString(state));
last_transition_time_ = zx::time_monotonic(transition_time);
callback();
if (on_update_) {
on_update_({{kSystemUserActivityCurrentStateKey, *current_state_}});
}
}
Annotations UIStateProvider::Get() {
if (std::holds_alternative<std::monostate>(last_transition_time_)) {
return {};
}
if (std::holds_alternative<Error>(last_transition_time_)) {
return {{kSystemUserActivityCurrentDurationKey,
ErrorOrString(std::get<Error>(last_transition_time_))}};
}
const auto& time = std::get<zx::time_monotonic>(last_transition_time_);
const auto formatted_duration = FormatDuration(clock_->MonotonicNow() - time);
// FormatDuration returns std::nullopt if duration was negative- if so, send Error::kBadValue as
// annotation value
const auto duration = formatted_duration.has_value() ? ErrorOrString(formatted_duration.value())
: ErrorOrString(Error::kBadValue);
return {{kSystemUserActivityCurrentDurationKey, duration}};
}
void UIStateProvider::GetOnUpdate(::fit::function<void(Annotations)> callback) {
on_update_ = std::move(callback);
if (current_state_.has_value()) {
on_update_({{kSystemUserActivityCurrentStateKey, *current_state_}});
}
}
} // namespace forensics::feedback