blob: d718da141ea8637153d90a300fd78d65cc969a87 [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 "services/device/generic_sensor/platform_sensor_reader_linux.h"
#include <memory>
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/task/thread_pool.h"
#include "base/timer/timer.h"
#include "services/device/generic_sensor/linux/sensor_data_linux.h"
#include "services/device/generic_sensor/platform_sensor_linux.h"
#include "services/device/public/cpp/generic_sensor/sensor_reading.h"
namespace device {
class PollingSensorReader : public SensorReader {
public:
PollingSensorReader(const SensorInfoLinux& sensor_info,
base::WeakPtr<PlatformSensorLinux> sensor,
scoped_refptr<base::SingleThreadTaskRunner> task_runner);
~PollingSensorReader() override;
// SensorReader overrides
void StartFetchingData(
const PlatformSensorConfiguration& configuration) override;
void StopFetchingData() override;
private:
// Helper class that performs the actual I/O. It must run on a
// SequencedTaskRunner that is properly configured for blocking I/O
// operations.
class BlockingTaskRunnerHelper final {
public:
BlockingTaskRunnerHelper(
base::WeakPtr<PollingSensorReader> polling_sensor_reader,
const SensorInfoLinux& sensor_info);
~BlockingTaskRunnerHelper();
// Starts polling for data at a given |frequency|, and stops if there is an
// error while reading or parsing the data.
void StartPolling(double frequency);
// Stops polling for data and notifying PollingSensorReader.
void StopPolling();
private:
void StopWithError();
void PollForData();
// Repeating timer for data polling.
base::RepeatingTimer timer_;
// |polling_sensor_reader_| can only be checked for validity on
// |owner_task_runner_|'s sequence.
base::WeakPtr<PollingSensorReader> polling_sensor_reader_;
// Task runner belonging to PollingSensorReader. Calls to
// PollingSensorReader will be done via this task runner.
scoped_refptr<base::SequencedTaskRunner> owner_task_runner_;
// Sensor information we need in order to know where and how to read sensor
// data.
const SensorInfoLinux sensor_info_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(BlockingTaskRunnerHelper);
};
// Receives a reading from the platform sensor and forwards it to |sensor_|.
void OnReadingAvailable(SensorReading reading);
// Signals that an error occurred while trying to read from a platform sensor.
void OnReadingError();
// Initializes a read timer.
void InitializeTimer(const PlatformSensorConfiguration& configuration);
// In builds with DCHECK enabled, checks that methods of this class are
// called on the right thread.
SEQUENCE_CHECKER(sequence_checker_);
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_ =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
std::unique_ptr<BlockingTaskRunnerHelper, base::OnTaskRunnerDeleter>
blocking_task_helper_;
base::WeakPtrFactory<PollingSensorReader> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(PollingSensorReader);
};
PollingSensorReader::BlockingTaskRunnerHelper::BlockingTaskRunnerHelper(
base::WeakPtr<PollingSensorReader> polling_sensor_reader,
const SensorInfoLinux& sensor_info)
: polling_sensor_reader_(polling_sensor_reader),
owner_task_runner_(base::SequencedTaskRunnerHandle::Get()),
sensor_info_(sensor_info) {
// Detaches from the sequence on which this object was created. It will be
// bound to its owning sequence when StartPolling() is called.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
PollingSensorReader::BlockingTaskRunnerHelper::~BlockingTaskRunnerHelper() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void PollingSensorReader::BlockingTaskRunnerHelper::StartPolling(
double frequency) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!timer_.IsRunning());
timer_.Start(FROM_HERE,
base::TimeDelta::FromMicroseconds(
base::Time::kMicrosecondsPerSecond / frequency),
this,
&PollingSensorReader::BlockingTaskRunnerHelper::PollForData);
}
void PollingSensorReader::BlockingTaskRunnerHelper::StopPolling() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
timer_.Stop();
}
void PollingSensorReader::BlockingTaskRunnerHelper::StopWithError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
StopPolling();
owner_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PollingSensorReader::OnReadingError,
polling_sensor_reader_));
}
void PollingSensorReader::BlockingTaskRunnerHelper::PollForData() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
SensorReading readings;
DCHECK_LE(sensor_info_.device_reading_files.size(),
base::size(readings.raw.values));
int i = 0;
for (const auto& path : sensor_info_.device_reading_files) {
std::string new_read_value;
if (!base::ReadFileToString(path, &new_read_value)) {
StopWithError();
return;
}
double new_value = 0;
base::TrimWhitespaceASCII(new_read_value, base::TRIM_ALL, &new_read_value);
if (!base::StringToDouble(new_read_value, &new_value)) {
StopWithError();
return;
}
readings.raw.values[i++] = new_value;
}
const auto& scaling_function = sensor_info_.apply_scaling_func;
if (!scaling_function.is_null()) {
scaling_function.Run(sensor_info_.device_scaling_value,
sensor_info_.device_offset_value, readings);
}
owner_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&PollingSensorReader::OnReadingAvailable,
polling_sensor_reader_, readings));
}
PollingSensorReader::PollingSensorReader(
const SensorInfoLinux& sensor_info,
base::WeakPtr<PlatformSensorLinux> sensor,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: SensorReader(sensor, std::move(task_runner)),
blocking_task_helper_(nullptr,
base::OnTaskRunnerDeleter(blocking_task_runner_)) {
// We need to properly initialize |blocking_task_helper_| here because we need
// |weak_factory_| to be created first.
blocking_task_helper_.reset(
new BlockingTaskRunnerHelper(weak_factory_.GetWeakPtr(), sensor_info));
}
PollingSensorReader::~PollingSensorReader() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void PollingSensorReader::StartFetchingData(
const PlatformSensorConfiguration& configuration) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (is_reading_active_)
StopFetchingData();
is_reading_active_ = true;
blocking_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::StartPolling,
base::Unretained(blocking_task_helper_.get()),
configuration.frequency()));
}
void PollingSensorReader::StopFetchingData() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_reading_active_ = false;
blocking_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&BlockingTaskRunnerHelper::StopPolling,
base::Unretained(blocking_task_helper_.get())));
}
void PollingSensorReader::OnReadingAvailable(SensorReading reading) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!is_reading_active_)
return;
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PlatformSensorLinux::UpdatePlatformSensorReading, sensor_,
reading));
}
void PollingSensorReader::OnReadingError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
NotifyReadError();
}
// static
std::unique_ptr<SensorReader> SensorReader::Create(
const SensorInfoLinux& sensor_info,
base::WeakPtr<PlatformSensorLinux> sensor,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
// TODO(maksims): implement triggered reading. At the moment,
// only polling read is supported.
return std::make_unique<PollingSensorReader>(sensor_info, sensor,
std::move(task_runner));
}
SensorReader::SensorReader(
base::WeakPtr<PlatformSensorLinux> sensor,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: sensor_(sensor),
task_runner_(std::move(task_runner)),
is_reading_active_(false) {
}
SensorReader::~SensorReader() = default;
void SensorReader::NotifyReadError() {
if (is_reading_active_) {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&PlatformSensorLinux::NotifyPlatformSensorError,
sensor_));
}
}
} // namespace device