| // Copyright 2017 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 "services/device/generic_sensor/platform_sensor_fusion.h" |
| |
| #include <algorithm> |
| |
| #include "base/logging.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "services/device/generic_sensor/platform_sensor_fusion_algorithm.h" |
| #include "services/device/generic_sensor/platform_sensor_provider.h" |
| |
| namespace device { |
| |
| class PlatformSensorFusion::Factory : public base::RefCounted<Factory> { |
| public: |
| static void CreateSensorFusion( |
| SensorReadingSharedBuffer* reading_buffer, |
| std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm, |
| const PlatformSensorProviderBase::CreateSensorCallback& callback, |
| PlatformSensorProvider* provider) { |
| scoped_refptr<Factory> factory(new Factory(reading_buffer, |
| std::move(fusion_algorithm), |
| std::move(callback), provider)); |
| factory->FetchSources(); |
| } |
| |
| private: |
| friend class base::RefCounted<Factory>; |
| |
| Factory(SensorReadingSharedBuffer* reading_buffer, |
| std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm, |
| const PlatformSensorProviderBase::CreateSensorCallback& callback, |
| PlatformSensorProvider* provider) |
| : fusion_algorithm_(std::move(fusion_algorithm)), |
| result_callback_(std::move(callback)), |
| reading_buffer_(reading_buffer), |
| provider_(provider) { |
| const auto& types = fusion_algorithm_->source_types(); |
| DCHECK(!types.empty()); |
| // Make sure there are no dups. |
| DCHECK(std::adjacent_find(types.begin(), types.end()) == types.end()); |
| DCHECK(result_callback_); |
| DCHECK(reading_buffer_); |
| DCHECK(provider_); |
| } |
| |
| ~Factory() = default; |
| |
| void FetchSources() { |
| for (mojom::SensorType type : fusion_algorithm_->source_types()) { |
| scoped_refptr<PlatformSensor> sensor = provider_->GetSensor(type); |
| if (sensor) { |
| SensorCreated(std::move(sensor)); |
| } else { |
| provider_->CreateSensor(type, |
| base::Bind(&Factory::SensorCreated, this)); |
| } |
| } |
| } |
| |
| void SensorCreated(scoped_refptr<PlatformSensor> sensor) { |
| if (!result_callback_) { |
| // It is possible, if this callback has been already called |
| // with nullptr (i.e. failed to fetch some of the required |
| // source sensors). See the condition below. |
| return; |
| } |
| |
| if (!sensor) { |
| std::move(result_callback_).Run(nullptr); |
| return; |
| } |
| mojom::SensorType type = sensor->GetType(); |
| sources_map_[type] = std::move(sensor); |
| if (sources_map_.size() == fusion_algorithm_->source_types().size()) { |
| scoped_refptr<PlatformSensor> fusion_sensor(new PlatformSensorFusion( |
| reading_buffer_, provider_, std::move(fusion_algorithm_), |
| std::move(sources_map_))); |
| std::move(result_callback_).Run(fusion_sensor); |
| } |
| } |
| |
| std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm_; |
| PlatformSensorProviderBase::CreateSensorCallback result_callback_; |
| SensorReadingSharedBuffer* reading_buffer_; // NOTE: Owned by |provider_|. |
| PlatformSensorProvider* provider_; |
| PlatformSensorFusion::SourcesMap sources_map_; |
| }; |
| |
| // static |
| void PlatformSensorFusion::Create( |
| SensorReadingSharedBuffer* reading_buffer, |
| PlatformSensorProvider* provider, |
| std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm, |
| const PlatformSensorProviderBase::CreateSensorCallback& callback) { |
| Factory::CreateSensorFusion(reading_buffer, std::move(fusion_algorithm), |
| callback, provider); |
| } |
| |
| PlatformSensorFusion::PlatformSensorFusion( |
| SensorReadingSharedBuffer* reading_buffer, |
| PlatformSensorProvider* provider, |
| std::unique_ptr<PlatformSensorFusionAlgorithm> fusion_algorithm, |
| PlatformSensorFusion::SourcesMap sources) |
| : PlatformSensor(fusion_algorithm->fused_type(), reading_buffer, provider), |
| fusion_algorithm_(std::move(fusion_algorithm)), |
| source_sensors_(std::move(sources)), |
| reporting_mode_(mojom::ReportingMode::CONTINUOUS) { |
| for (const auto& pair : source_sensors_) |
| pair.second->AddClient(this); |
| |
| fusion_algorithm_->set_fusion_sensor(this); |
| |
| if (std::any_of(source_sensors_.begin(), source_sensors_.end(), |
| [](const SourcesMapEntry& pair) { |
| return pair.second->GetReportingMode() == |
| mojom::ReportingMode::ON_CHANGE; |
| })) { |
| reporting_mode_ = mojom::ReportingMode::ON_CHANGE; |
| } |
| } |
| |
| PlatformSensorFusion::~PlatformSensorFusion() { |
| for (const auto& pair : source_sensors_) |
| pair.second->RemoveClient(this); |
| } |
| |
| mojom::ReportingMode PlatformSensorFusion::GetReportingMode() { |
| return reporting_mode_; |
| } |
| |
| PlatformSensorConfiguration PlatformSensorFusion::GetDefaultConfiguration() { |
| PlatformSensorConfiguration default_configuration; |
| for (const auto& pair : source_sensors_) { |
| double frequency = pair.second->GetDefaultConfiguration().frequency(); |
| if (frequency > default_configuration.frequency()) |
| default_configuration.set_frequency(frequency); |
| } |
| return default_configuration; |
| } |
| |
| bool PlatformSensorFusion::StartSensor( |
| const PlatformSensorConfiguration& configuration) { |
| // Remove all the previously added source configs. |
| StopSensor(); |
| for (const auto& pair : source_sensors_) { |
| if (!pair.second->StartListening( |
| this, PlatformSensorConfiguration( |
| std::min(configuration.frequency(), |
| pair.second->GetMaximumSupportedFrequency())))) { |
| StopSensor(); |
| return false; |
| } |
| } |
| |
| fusion_algorithm_->SetFrequency(configuration.frequency()); |
| return true; |
| } |
| |
| void PlatformSensorFusion::StopSensor() { |
| for (const auto& pair : source_sensors_) |
| pair.second->StopListening(this); |
| |
| fusion_algorithm_->Reset(); |
| } |
| |
| bool PlatformSensorFusion::CheckSensorConfiguration( |
| const PlatformSensorConfiguration& configuration) { |
| for (const auto& pair : source_sensors_) { |
| if (!pair.second->CheckSensorConfiguration(PlatformSensorConfiguration( |
| std::min(configuration.frequency(), |
| pair.second->GetMaximumSupportedFrequency())))) |
| return false; |
| } |
| return true; |
| } |
| |
| double PlatformSensorFusion::GetMaximumSupportedFrequency() { |
| double maximum_frequency = 0.0; |
| for (const auto& pair : source_sensors_) { |
| maximum_frequency = std::max(maximum_frequency, |
| pair.second->GetMaximumSupportedFrequency()); |
| } |
| return maximum_frequency; |
| } |
| |
| void PlatformSensorFusion::OnSensorReadingChanged(mojom::SensorType type) { |
| SensorReading reading; |
| reading.raw.timestamp = |
| (base::TimeTicks::Now() - base::TimeTicks()).InSecondsF(); |
| |
| if (!fusion_algorithm_->GetFusedData(type, &reading)) |
| return; |
| |
| if (GetReportingMode() == mojom::ReportingMode::ON_CHANGE && |
| !fusion_algorithm_->IsReadingSignificantlyDifferent(reading_, reading)) { |
| return; |
| } |
| |
| reading_ = reading; |
| UpdateSharedBufferAndNotifyClients(reading_); |
| } |
| |
| void PlatformSensorFusion::OnSensorError() { |
| NotifySensorError(); |
| } |
| |
| bool PlatformSensorFusion::IsSuspended() { |
| for (auto& client : clients_) { |
| if (!client.IsSuspended()) |
| return false; |
| } |
| return true; |
| } |
| |
| bool PlatformSensorFusion::GetSourceReading(mojom::SensorType type, |
| SensorReading* result) { |
| auto it = source_sensors_.find(type); |
| if (it != source_sensors_.end()) |
| return it->second->GetLatestReading(result); |
| NOTREACHED(); |
| return false; |
| } |
| |
| } // namespace device |