blob: a3da576d8ebf7f0ebbca86f0909df1cb4739389d [file] [log] [blame]
// Copyright 2016 The Chromium 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 "modules/sensor/SensorProxy.h"
#include "core/frame/LocalFrame.h"
#include "core/page/ChromeClient.h"
#include "core/page/FocusController.h"
#include "modules/sensor/SensorProviderProxy.h"
#include "modules/sensor/SensorReadingRemapper.h"
#include "platform/LayoutTestSupport.h"
#include "platform/mojo/MojoHelper.h"
#include "public/platform/Platform.h"
#include "public/platform/TaskType.h"
#include "public/platform/WebScreenInfo.h"
#include "services/device/public/cpp/generic_sensor/sensor_traits.h"
#include "third_party/WebKit/common/page/page_visibility_state.mojom-blink.h"
namespace blink {
using namespace device::mojom::blink;
SensorProxy::SensorProxy(SensorType sensor_type,
SensorProviderProxy* provider,
Page* page)
: PageVisibilityObserver(page),
FocusChangedObserver(page),
type_(sensor_type),
mode_(ReportingMode::CONTINUOUS),
provider_(provider),
client_binding_(this),
state_(SensorProxy::kUninitialized),
suspended_(false),
polling_timer_(
provider->GetSupplementable()->GetTaskRunner(TaskType::kSensor),
this,
&SensorProxy::OnPollingTimer) {}
SensorProxy::~SensorProxy() = default;
void SensorProxy::Dispose() {
client_binding_.Close();
}
void SensorProxy::Trace(blink::Visitor* visitor) {
visitor->Trace(observers_);
visitor->Trace(provider_);
PageVisibilityObserver::Trace(visitor);
}
void SensorProxy::AddObserver(Observer* observer) {
if (!observers_.Contains(observer))
observers_.insert(observer);
}
void SensorProxy::RemoveObserver(Observer* observer) {
observers_.erase(observer);
}
void SensorProxy::Initialize() {
if (state_ != kUninitialized)
return;
if (!provider_->GetSensorProvider()) {
HandleSensorError();
return;
}
state_ = kInitializing;
auto callback =
WTF::Bind(&SensorProxy::OnSensorCreated, WrapWeakPersistent(this));
provider_->GetSensorProvider()->GetSensor(type_, std::move(callback));
}
void SensorProxy::AddConfiguration(SensorConfigurationPtr configuration,
base::OnceCallback<void(bool)> callback) {
DCHECK(IsInitialized());
AddActiveFrequency(configuration->frequency);
sensor_->AddConfiguration(std::move(configuration), std::move(callback));
}
void SensorProxy::RemoveConfiguration(SensorConfigurationPtr configuration) {
DCHECK(IsInitialized());
RemoveActiveFrequency(configuration->frequency);
sensor_->RemoveConfiguration(std::move(configuration));
}
void SensorProxy::Suspend() {
DCHECK(IsInitialized());
if (suspended_)
return;
sensor_->Suspend();
suspended_ = true;
UpdatePollingStatus();
}
void SensorProxy::Resume() {
DCHECK(IsInitialized());
if (!suspended_)
return;
sensor_->Resume();
suspended_ = false;
UpdatePollingStatus();
}
const device::SensorReading& SensorProxy::GetReading(bool remapped) const {
if (remapped) {
if (remapped_reading_.timestamp() != reading_.timestamp()) {
remapped_reading_ = reading_;
SensorReadingRemapper::RemapToScreenCoords(
type_, GetScreenOrientationAngle(), &remapped_reading_);
}
return remapped_reading_;
}
return reading_;
}
const SensorConfiguration* SensorProxy::DefaultConfig() const {
DCHECK(IsInitialized());
return default_config_.get();
}
void SensorProxy::UpdateSensorReading() {
DCHECK(ShouldProcessReadings());
device::SensorReading reading_data;
if (!shared_buffer_handle_->is_valid() ||
!shared_buffer_reader_->GetReading(&reading_data)) {
HandleSensorError();
return;
}
double latest_timestamp = reading_data.timestamp();
if (reading_.timestamp() != latest_timestamp &&
latest_timestamp != 0.0) // The shared buffer is zeroed when
// sensor is stopped, we skip this
// reading.
{
DCHECK_GT(latest_timestamp, reading_.timestamp())
<< "Timestamps must increase monotonically";
reading_ = reading_data;
for (Observer* observer : observers_)
observer->OnSensorReadingChanged();
}
}
void SensorProxy::RaiseError() {
HandleSensorError();
}
void SensorProxy::SensorReadingChanged() {
DCHECK_EQ(ReportingMode::ON_CHANGE, mode_);
if (ShouldProcessReadings())
UpdateSensorReading();
}
void SensorProxy::PageVisibilityChanged() {
UpdateSuspendedStatus();
}
void SensorProxy::FocusedFrameChanged() {
UpdateSuspendedStatus();
}
void SensorProxy::HandleSensorError(SensorCreationResult error) {
state_ = kUninitialized;
active_frequencies_.clear();
reading_ = device::SensorReading();
UpdatePollingStatus();
// The m_sensor.reset() will release all callbacks and its bound parameters,
// therefore, handleSensorError accepts messages by value.
sensor_.reset();
shared_buffer_.reset();
shared_buffer_handle_.reset();
default_config_.reset();
client_binding_.Close();
ExceptionCode code = kNotReadableError;
String description = "Could not connect to a sensor";
if (error == SensorCreationResult::ERROR_NOT_ALLOWED) {
code = kNotAllowedError;
description = "Permissions to access sensor are not granted";
}
auto copy = observers_;
for (Observer* observer : copy) {
observer->OnSensorError(code, description, String());
}
}
void SensorProxy::OnSensorCreated(SensorCreationResult result,
SensorInitParamsPtr params) {
DCHECK_EQ(kInitializing, state_);
if (!params) {
DCHECK_NE(SensorCreationResult::SUCCESS, result);
HandleSensorError(result);
return;
}
DCHECK_EQ(SensorCreationResult::SUCCESS, result);
const size_t kReadBufferSize = sizeof(ReadingBuffer);
DCHECK_EQ(0u, params->buffer_offset % kReadBufferSize);
mode_ = params->mode;
default_config_ = std::move(params->default_configuration);
if (!default_config_) {
HandleSensorError();
return;
}
sensor_.Bind(std::move(params->sensor));
client_binding_.Bind(std::move(params->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();
return;
}
const auto* buffer = static_cast<const device::SensorReadingSharedBuffer*>(
shared_buffer_.get());
shared_buffer_reader_.reset(
new device::SensorReadingSharedBufferReader(buffer));
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);
DCHECK_GE(device::GetSensorMaxAllowedFrequency(type_),
frequency_limits_.second);
auto error_callback =
WTF::Bind(&SensorProxy::HandleSensorError, WrapWeakPersistent(this),
SensorCreationResult::ERROR_NOT_AVAILABLE);
sensor_.set_connection_error_handler(std::move(error_callback));
state_ = kInitialized;
UpdateSuspendedStatus();
for (Observer* observer : observers_)
observer->OnSensorInitialized();
}
void SensorProxy::OnPollingTimer(TimerBase*) {
UpdateSensorReading();
}
bool SensorProxy::ShouldProcessReadings() const {
return IsInitialized() && !suspended_ && !active_frequencies_.IsEmpty();
}
uint16_t SensorProxy::GetScreenOrientationAngle() const {
DCHECK(IsInitialized());
if (LayoutTestSupport::IsRunningLayoutTest()) {
// Simulate that the device is turned 90 degrees on the right.
// 'orientation_angle' must be 270 as per
// https://w3c.github.io/screen-orientation/#dfn-update-the-orientation-information.
return 270;
}
return GetPage()->GetChromeClient().GetScreenInfo().orientation_angle;
}
void SensorProxy::UpdatePollingStatus() {
if (mode_ != ReportingMode::CONTINUOUS)
return;
if (ShouldProcessReadings()) {
// TODO(crbug/721297) : We need to find out an algorithm for resulting
// polling frequency.
polling_timer_.StartRepeating(
WTF::TimeDelta::FromSecondsD(1 / active_frequencies_.back()),
FROM_HERE);
} else {
polling_timer_.Stop();
}
}
void SensorProxy::UpdateSuspendedStatus() {
if (!IsInitialized())
return;
bool page_visible =
GetPage()->VisibilityState() == mojom::PageVisibilityState::kVisible;
LocalFrame* focused_frame = GetPage()->GetFocusController().FocusedFrame();
bool main_frame_focused =
focused_frame && !focused_frame->IsCrossOriginSubframe();
if (page_visible && main_frame_focused)
Resume();
else
Suspend();
}
void SensorProxy::RemoveActiveFrequency(double frequency) {
// Can use binary search as active_frequencies_ is sorted.
auto it = std::lower_bound(active_frequencies_.begin(),
active_frequencies_.end(), frequency);
if (it == active_frequencies_.end()) {
NOTREACHED() << "Attempted to remove active frequency which is not present "
"in the list";
return;
}
active_frequencies_.erase(it);
UpdatePollingStatus();
if (active_frequencies_.IsEmpty())
reading_ = device::SensorReading();
}
void SensorProxy::AddActiveFrequency(double frequency) {
active_frequencies_.push_back(frequency);
std::sort(active_frequencies_.begin(), active_frequencies_.end());
UpdatePollingStatus();
}
} // namespace blink