| Index: content/renderer/device_sensors/device_sensor_event_pump.h
|
| diff --git a/content/renderer/device_sensors/device_sensor_event_pump.h b/content/renderer/device_sensors/device_sensor_event_pump.h
|
| index 17a8ff96b4aee4e1902208197f187af685feb4b4..435a0a080395e262147e03b6cb6c53cde7c0bb63 100644
|
| --- a/content/renderer/device_sensors/device_sensor_event_pump.h
|
| +++ b/content/renderer/device_sensors/device_sensor_event_pump.h
|
| @@ -5,142 +5,283 @@
|
| #ifndef CONTENT_RENDERER_DEVICE_SENSORS_DEVICE_SENSOR_EVENT_PUMP_H_
|
| #define CONTENT_RENDERER_DEVICE_SENSORS_DEVICE_SENSOR_EVENT_PUMP_H_
|
|
|
| +#include <memory>
|
| +#include <utility>
|
| +#include <vector>
|
| +
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| #include "base/macros.h"
|
| -#include "base/memory/shared_memory.h"
|
| #include "base/time/time.h"
|
| #include "base/timer/timer.h"
|
| #include "content/public/common/service_names.mojom.h"
|
| #include "content/public/renderer/platform_event_observer.h"
|
| #include "content/renderer/render_thread_impl.h"
|
| +#include "device/generic_sensor/public/cpp/sensor_reading.h"
|
| +#include "device/generic_sensor/public/interfaces/sensor.mojom.h"
|
| +#include "device/generic_sensor/public/interfaces/sensor_provider.mojom.h"
|
| +#include "mojo/public/cpp/bindings/binding.h"
|
| #include "mojo/public/cpp/system/platform_handle.h"
|
| #include "services/device/public/interfaces/constants.mojom.h"
|
| #include "services/service_manager/public/cpp/connector.h"
|
|
|
| namespace content {
|
|
|
| -template <typename Base, typename MojoInterface>
|
| -class CONTENT_EXPORT DeviceSensorMojoClientMixin : public Base {
|
| - public:
|
| - template <typename... Args>
|
| - explicit DeviceSensorMojoClientMixin(Args&&... args)
|
| - : Base(std::forward<Args>(args)...) {
|
| - auto request = mojo::MakeRequest(&mojo_interface_);
|
| -
|
| - // When running layout tests, those observers should not listen to the
|
| - // actual hardware changes. In order to make that happen, don't connect
|
| - // the other end of the mojo pipe to anything.
|
| - //
|
| - // TODO(sammc): Remove this when JS layout test support for shared buffers
|
| - // is ready and the layout tests are converted to use that for mocking.
|
| - if (RenderThreadImpl::current() &&
|
| - !RenderThreadImpl::current()->layout_test_mode()) {
|
| - RenderThread::Get()->GetConnector()->BindInterface(
|
| - device::mojom::kServiceName, std::move(request));
|
| - }
|
| - }
|
| -
|
| - void SendStartMessage() override {
|
| - mojo_interface_->StartPolling(
|
| - base::Bind(&DeviceSensorMojoClientMixin<Base, MojoInterface>::DidStart,
|
| - base::Unretained(this)));
|
| - }
|
| - void SendStopMessage() override { mojo_interface_->StopPolling(); }
|
| -
|
| - protected:
|
| - void DidStart(mojo::ScopedSharedBufferHandle buffer_handle) {
|
| - Base::DidStart(std::move(buffer_handle));
|
| - }
|
| -
|
| - private:
|
| - mojo::InterfacePtr<MojoInterface> mojo_interface_;
|
| -};
|
| -
|
| template <typename ListenerType>
|
| class CONTENT_EXPORT DeviceSensorEventPump
|
| : NON_EXPORTED_BASE(public PlatformEventObserver<ListenerType>) {
|
| public:
|
| - // Default rate for firing events.
|
| - static const int kDefaultPumpFrequencyHz = 60;
|
| - static const int kDefaultPumpDelayMicroseconds =
|
| - base::Time::kMicrosecondsPerSecond / kDefaultPumpFrequencyHz;
|
| -
|
| - // PlatformEventObserver
|
| + // PlatformEventObserver:
|
| void Start(blink::WebPlatformEventListener* listener) override {
|
| DVLOG(2) << "requested start";
|
|
|
| - if (state_ != STOPPED)
|
| + if (state_ != PumpState::STOPPED)
|
| return;
|
|
|
| DCHECK(!timer_.IsRunning());
|
|
|
| PlatformEventObserver<ListenerType>::Start(listener);
|
| - state_ = PENDING_START;
|
| + state_ = PumpState::PENDING_START;
|
| }
|
|
|
| + // PlatformEventObserver:
|
| void Stop() override {
|
| - DVLOG(2) << "stop";
|
| + DVLOG(2) << "requested stop";
|
|
|
| - if (state_ == STOPPED)
|
| + if (state_ == PumpState::STOPPED)
|
| return;
|
|
|
| - DCHECK((state_ == PENDING_START && !timer_.IsRunning()) ||
|
| - (state_ == RUNNING && timer_.IsRunning()));
|
| + DCHECK((state_ == PumpState::PENDING_START && !timer_.IsRunning()) ||
|
| + (state_ == PumpState::RUNNING && timer_.IsRunning()));
|
|
|
| if (timer_.IsRunning())
|
| timer_.Stop();
|
| +
|
| PlatformEventObserver<ListenerType>::Stop();
|
| - state_ = STOPPED;
|
| + state_ = PumpState::STOPPED;
|
| + }
|
| +
|
| + // PlatformEventObserver:
|
| + void SendStopMessage() override {
|
| + sensors_.clear();
|
| + num_sensors_tried_ = 0;
|
| + num_available_sensors_ = 0;
|
| }
|
|
|
| protected:
|
| explicit DeviceSensorEventPump(RenderThread* thread)
|
| : PlatformEventObserver<ListenerType>(thread),
|
| pump_delay_microseconds_(kDefaultPumpDelayMicroseconds),
|
| - state_(STOPPED) {}
|
| + num_sensors_tried_(0),
|
| + num_available_sensors_(0),
|
| + state_(PumpState::STOPPED) {
|
| + auto request = mojo::MakeRequest(&sensor_provider_);
|
| +
|
| + // When running layout tests, those observers should not listen to the
|
| + // actual hardware changes. In order to make that happen, don't connect
|
| + // the other end of the mojo pipe to anything.
|
| + if (RenderThreadImpl::current() &&
|
| + !RenderThreadImpl::current()->layout_test_mode()) {
|
| + RenderThread::Get()->GetConnector()->BindInterface(
|
| + device::mojom::kServiceName, std::move(request));
|
| + sensor_provider_.set_connection_error_handler(
|
| + base::Bind(&DeviceSensorEventPump::HandleSensorProviderError,
|
| + base::Unretained(this)));
|
| + }
|
| + }
|
|
|
| ~DeviceSensorEventPump() override {
|
| PlatformEventObserver<ListenerType>::StopIfObserving();
|
| }
|
|
|
| + // Default rate for firing events.
|
| + static constexpr int kDefaultPumpFrequencyHz = 60;
|
| + static constexpr int kDefaultPumpDelayMicroseconds =
|
| + base::Time::kMicrosecondsPerSecond / kDefaultPumpFrequencyHz;
|
| +
|
| + static constexpr int kMaxReadAttemptsCount = 10;
|
| +
|
| + struct CONTENT_EXPORT SensorEntry : public device::mojom::SensorClient {
|
| + explicit SensorEntry(DeviceSensorEventPump* pump)
|
| + : event_pump(pump), active(false), client_binding(this) {}
|
| +
|
| + ~SensorEntry() { client_binding.Close(); }
|
| +
|
| + // device::mojom::SensorClient:
|
| + void RaiseError() override { HandleSensorError(); }
|
| +
|
| + // device::mojom::SensorClient:
|
| + void SensorReadingChanged() override {
|
| + // Since DeviceSensorEventPump::FireEvent is called in a certain
|
| + // frequency, the |shared_buffer| is read frequently, so this
|
| + // method doesn't need to be implemented.
|
| + }
|
| +
|
| + // Mojo callback for SensorProvider::GetSensor().
|
| + void OnSensorCreated(device::mojom::SensorInitParamsPtr params,
|
| + device::mojom::SensorClientRequest client_request) {
|
| + ++event_pump->num_sensors_tried_;
|
| +
|
| + if (!params) {
|
| + HandleSensorError();
|
| + if (event_pump->CanStart())
|
| + event_pump->DidStart();
|
| + return;
|
| + }
|
| +
|
| + constexpr size_t kReadBufferSize =
|
| + sizeof(device::SensorReadingSharedBuffer);
|
| +
|
| + DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
|
| +
|
| + mode = params->mode;
|
| + default_config = params->default_configuration;
|
| +
|
| + DCHECK(sensor.is_bound());
|
| + client_binding.Bind(std::move(client_request));
|
| +
|
| + shared_buffer_handle = std::move(params->memory);
|
| + DCHECK(!shared_buffer);
|
| + shared_buffer = shared_buffer_handle->MapAtOffset(kReadBufferSize,
|
| + params->buffer_offset);
|
| +
|
| + if (!shared_buffer) {
|
| + HandleSensorError();
|
| + if (event_pump->CanStart())
|
| + event_pump->DidStart();
|
| + return;
|
| + }
|
| +
|
| + ++event_pump->num_available_sensors_;
|
| +
|
| + frequency_limits.first = params->minimum_frequency;
|
| + frequency_limits.second = params->maximum_frequency;
|
| +
|
| + DCHECK_GT(frequency_limits.first, 0.0);
|
| + DCHECK_GE(frequency_limits.second, frequency_limits.first);
|
| + constexpr double kMaxAllowedFrequency =
|
| + device::mojom::SensorConfiguration::kMaxAllowedFrequency;
|
| + DCHECK_GE(kMaxAllowedFrequency, frequency_limits.second);
|
| +
|
| + sensor.set_connection_error_handler(
|
| + base::Bind(&SensorEntry::HandleSensorError, base::Unretained(this)));
|
| + sensor->AddConfiguration(
|
| + default_config, base::Bind(&SensorEntry::OnSensorAddConfiguration,
|
| + base::Unretained(this)));
|
| + }
|
| +
|
| + // Mojo callback for Sensor::AddConfiguration().
|
| + void OnSensorAddConfiguration(bool success) {
|
| + active = success;
|
| + if (event_pump->CanStart())
|
| + event_pump->DidStart();
|
| + }
|
| +
|
| + void HandleSensorError() {
|
| + sensor.reset();
|
| + active = false;
|
| + shared_buffer_handle.reset();
|
| + shared_buffer.reset();
|
| + client_binding.Close();
|
| + }
|
| +
|
| + // Updates sensor reading from shared buffer.
|
| + bool UpdateSensorReading() {
|
| + int read_attempts = 0;
|
| + device::SensorReading reading_data;
|
| + while (!TryReadFromBuffer(&reading_data)) {
|
| + if (++read_attempts == kMaxReadAttemptsCount) {
|
| + HandleSensorError();
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + reading = reading_data;
|
| + return true;
|
| + }
|
| +
|
| + bool TryReadFromBuffer(device::SensorReading* result) {
|
| + const device::SensorReadingSharedBuffer* buffer =
|
| + static_cast<const device::SensorReadingSharedBuffer*>(
|
| + shared_buffer.get());
|
| + const device::OneWriterSeqLock& seqlock = buffer->seqlock.value();
|
| + auto version = seqlock.ReadBegin();
|
| + auto reading_data = buffer->reading;
|
| + if (seqlock.ReadRetry(version))
|
| + return false;
|
| + *result = reading_data;
|
| + return true;
|
| + }
|
| +
|
| + bool SensorReadingUpdated() { return active && UpdateSensorReading(); }
|
| +
|
| + DeviceSensorEventPump* event_pump;
|
| + device::mojom::SensorPtr sensor;
|
| + device::mojom::SensorType type;
|
| + bool active;
|
| + device::mojom::ReportingMode mode;
|
| + device::PlatformSensorConfiguration default_config;
|
| + mojo::ScopedSharedBufferHandle shared_buffer_handle;
|
| + mojo::ScopedSharedBufferMapping shared_buffer;
|
| + device::SensorReading reading;
|
| + std::pair<double, double> frequency_limits;
|
| + mojo::Binding<device::mojom::SensorClient> client_binding;
|
| + };
|
| +
|
| + friend struct SensorEntry;
|
| +
|
| // The pump is a tri-state automaton with allowed transitions as follows:
|
| // STOPPED -> PENDING_START
|
| // PENDING_START -> RUNNING
|
| // PENDING_START -> STOPPED
|
| // RUNNING -> STOPPED
|
| - enum PumpState {
|
| - STOPPED,
|
| - RUNNING,
|
| - PENDING_START
|
| - };
|
| + enum class PumpState { STOPPED, RUNNING, PENDING_START };
|
|
|
| - void DidStart(mojo::ScopedSharedBufferHandle buffer_handle) {
|
| + void GetSensor(device::mojom::SensorType type, SensorEntry* sensor_entry) {
|
| + sensor_entry->type = type;
|
| + auto request = mojo::MakeRequest(&sensor_entry->sensor);
|
| + sensor_provider_->GetSensor(type, std::move(request),
|
| + base::Bind(&SensorEntry::OnSensorCreated,
|
| + base::Unretained(sensor_entry)));
|
| + }
|
| +
|
| + void DidStart() {
|
| DVLOG(2) << "did start sensor event pump";
|
|
|
| - if (state_ != PENDING_START)
|
| + if (state_ != PumpState::PENDING_START)
|
| return;
|
|
|
| DCHECK(!timer_.IsRunning());
|
|
|
| - base::SharedMemoryHandle handle;
|
| - MojoResult result = mojo::UnwrapSharedMemoryHandle(
|
| - std::move(buffer_handle), &handle, nullptr, nullptr);
|
| - DCHECK_EQ(MOJO_RESULT_OK, result);
|
| -
|
| - if (InitializeReader(handle)) {
|
| - timer_.Start(FROM_HERE,
|
| - base::TimeDelta::FromMicroseconds(pump_delay_microseconds_),
|
| - this,
|
| - &DeviceSensorEventPump::FireEvent);
|
| - state_ = RUNNING;
|
| - }
|
| + timer_.Start(FROM_HERE,
|
| + base::TimeDelta::FromMicroseconds(pump_delay_microseconds_),
|
| + this, &DeviceSensorEventPump::FireEvent);
|
| + state_ = PumpState::RUNNING;
|
| }
|
|
|
| + void HandleSensorProviderError() {
|
| + sensor_provider_.reset();
|
| + for (auto& sensor : sensors_)
|
| + sensor->HandleSensorError();
|
| + }
|
| +
|
| + // Notify the renderer of a certain device event.
|
| virtual void FireEvent() = 0;
|
| - virtual bool InitializeReader(base::SharedMemoryHandle handle) = 0;
|
|
|
| + mojo::InterfacePtr<device::mojom::SensorProvider> sensor_provider_;
|
| + std::vector<std::unique_ptr<SensorEntry>> sensors_;
|
| int pump_delay_microseconds_;
|
| + // The number of sensors that are tried obtaining by calling
|
| + // SensorProvider::GetSensor().
|
| + int num_sensors_tried_;
|
| + // The number of sensors that are available from a successful
|
| + // SensorProvider::GetSensor() call.
|
| + int num_available_sensors_;
|
| +
|
| + private:
|
| + // Returns if the |sensors_| are ready to start a certain device event.
|
| + virtual bool CanStart() const = 0;
|
| +
|
| PumpState state_;
|
| base::RepeatingTimer timer_;
|
|
|
|
|