blob: 822ae122a7432ab032c51b6c51dcd9e23e054a54 [file] [log] [blame]
// Copyright 2024 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.
#ifndef SRC_SENSORS_PLAYBACK_DRIVER_IMPL_H_
#define SRC_SENSORS_PLAYBACK_DRIVER_IMPL_H_
#include <fidl/fuchsia.hardware.sensors/cpp/fidl.h>
#include <lib/fpromise/promise.h>
#include <lib/fpromise/scope.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include "src/camera/lib/actor/actor_base.h"
#include "src/sensors/playback/playback_controller.h"
namespace sensors::playback {
class DriverImpl : public camera::actor::ActorBase,
public fidl::Server<fuchsia_hardware_sensors::Driver> {
using Driver = fuchsia_hardware_sensors::Driver;
public:
DriverImpl(async_dispatcher_t* dispatcher, PlaybackController& controller);
// Driver protocol method implementations.
void GetSensorsList(GetSensorsListCompleter::Sync& completer) override;
void ActivateSensor(ActivateSensorRequest& request,
ActivateSensorCompleter::Sync& completer) override;
void DeactivateSensor(DeactivateSensorRequest& request,
DeactivateSensorCompleter::Sync& completer) override;
void ConfigureSensorRate(ConfigureSensorRateRequest& request,
ConfigureSensorRateCompleter::Sync& completer) override;
void handle_unknown_method(fidl::UnknownMethodMetadata<Driver> metadata,
fidl::UnknownMethodCompleter::Sync& completer) override;
// Server connection management.
/* LIFECYCLE NOTES:
Cleaning up when a Driver protocol client disconnects involves waiting for some cleanup to
happen on the playback controller thread. In order to do this, the version of BindServer which
accepts a shared_ptr is used so that the DriverImpl instance can be kept alive after the
OnUnbound callback returns.
A copy of the shared_ptr is stored in a callback which is given to the controller when it is
notified of the client's disconnection. When the controller cleanup is done, it will call the
callback which in turn schedules a promise that resets the shared_ptr (deleting the DriverImpl
instance). It's not legal for the DriverImpl to delete itself in a task scheduled on it's own
Executor. Instead, a separate Executor which schedules on the same thread but outlives the
DriverImpl instance is provided for the final part of the cleanup.
*/
static void BindSelfManagedServer(async_dispatcher_t* dispatcher,
async::Executor& cleanup_executor,
PlaybackController& controller,
fidl::ServerEnd<Driver> server_end) {
std::shared_ptr impl = std::make_unique<DriverImpl>(dispatcher, controller);
std::shared_ptr unbind_handle = impl;
DriverImpl* impl_ptr = impl.get();
fpromise::promise<void> unbind_promise =
fpromise::make_promise([client_connected = &client_connected_,
unbind_handle = std::move(unbind_handle)]() mutable {
unbind_handle.reset();
*client_connected = false;
});
fit::callback<void()> unbind_callback = [&cleanup_executor,
unbind_promise = std::move(unbind_promise)]() mutable {
cleanup_executor.schedule_task(fpromise::pending_task(std::move(unbind_promise)));
};
fidl::ServerBindingRef binding_ref = fidl::BindServer(
dispatcher, std::move(server_end), std::move(impl),
[unbind_callback = std::move(unbind_callback)](DriverImpl* impl, fidl::UnbindInfo info,
fidl::ServerEnd<Driver> server_end) mutable {
impl->OnUnbound(info, std::move(server_end), std::move(unbind_callback));
});
// Only one client permitted at a time.
if (client_connected_) {
FX_LOGS(WARNING) << "Driver client already connected, closing new connection.";
binding_ref.Close(ZX_ERR_ALREADY_BOUND);
return;
}
impl_ptr->AttachToController();
impl_ptr->binding_ref_.emplace(std::move(binding_ref));
client_connected_ = true;
}
void OnUnbound(fidl::UnbindInfo info, fidl::ServerEnd<Driver> server_end,
fit::callback<void()> unbind_callback);
fpromise::promise<void> DisconnectClient(zx_status_t epitaph);
private:
void AttachToController();
static bool client_connected_;
std::optional<fidl::ServerBindingRef<fuchsia_hardware_sensors::Driver>> binding_ref_;
PlaybackController& controller_;
// This should always be the last thing in the object. Otherwise scheduled tasks within this scope
// which reference members of this object may be allowed to run after destruction of this object
// has started. Keeping this at the end ensures that the scope is destroyed first, cancelling any
// scheduled tasks before the rest of the members are destroyed.
fpromise::scope scope_;
};
} // namespace sensors::playback
#endif // SRC_SENSORS_PLAYBACK_DRIVER_IMPL_H_