Commit 2079112a authored by Harvey Yang's avatar Harvey Yang Committed by Chromium LUCI CQ

accelerometer: Refactor AccelerometerFileReader

This commit simplifies the logic and puts only the blocking code in the
created USER_VISIBLE sequenced task runner. In this way, most of the
tasks are run on the UI thread.

BUG=b:175355860
TEST=run on octopus

Change-Id: I50b9380c5d0667513430f4f3b685317687be844d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2586288
Commit-Queue: Cheng-Hao Yang <chenghaoyang@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839057}
parent b7de6671
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/system/sys_info.h" #include "base/system/sys_info.h"
#include "base/task/current_thread.h"
#include "base/task/task_traits.h" #include "base/task/task_traits.h"
#include "base/task/thread_pool.h" #include "base/task/thread_pool.h"
#include "base/task_runner.h" #include "base/task_runner.h"
...@@ -142,15 +143,18 @@ bool ReadFileToDouble(const base::FilePath& path, double* value) { ...@@ -142,15 +143,18 @@ bool ReadFileToDouble(const base::FilePath& path, double* value) {
} // namespace } // namespace
AccelerometerFileReader::AccelerometerFileReader() AccelerometerFileReader::AccelerometerFileReader()
: observers_( : ui_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
new base::ObserverListThreadSafe<AccelerometerReader::Observer>()) {}
void AccelerometerFileReader::PrepareAndInitialize() { void AccelerometerFileReader::PrepareAndInitialize() {
DCHECK(base::CurrentUIThread::IsSet());
DETACH_FROM_SEQUENCE(sequence_checker_);
// AccelerometerReader is important for screen orientation so we need // AccelerometerReader is important for screen orientation so we need
// USER_VISIBLE priority. // USER_VISIBLE priority.
// Use CONTINUE_ON_SHUTDOWN to avoid blocking shutdown since the datareading // Use CONTINUE_ON_SHUTDOWN to avoid blocking shutdown since the datareading
// could get blocked on certain devices. See https://crbug.com/1023989. // could get blocked on certain devices. See https://crbug.com/1023989.
task_runner_ = base::ThreadPool::CreateSequencedTaskRunner( blocking_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN});
...@@ -158,36 +162,165 @@ void AccelerometerFileReader::PrepareAndInitialize() { ...@@ -158,36 +162,165 @@ void AccelerometerFileReader::PrepareAndInitialize() {
initialization_timeout_ = base::TimeTicks::Now() + kInitializeTimeout; initialization_timeout_ = base::TimeTicks::Now() + kInitializeTimeout;
// Asynchronously detect and initialize the accelerometer to avoid delaying TryScheduleInitialize();
// startup.
task_runner_->PostNonNestableTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::InitializeInternal, this));
} }
void AccelerometerFileReader::TryScheduleInitializeInternal() { void AccelerometerFileReader::AddObserver(
// If we haven't yet passed the timeout cutoff, try this again. This will AccelerometerReader::Observer* observer) {
// be scheduled at the same rate as reading. DCHECK(base::CurrentUIThread::IsSet());
if (base::TimeTicks::Now() < initialization_timeout_) { DCHECK(blocking_task_runner_);
DCHECK_EQ(State::INITIALIZING, initialization_state_); observers_.AddObserver(observer);
task_runner_->PostDelayedTask(
if (initialization_state_ != State::SUCCESS)
return;
blocking_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AccelerometerFileReader::ReadSample, this));
}
void AccelerometerFileReader::RemoveObserver(
AccelerometerReader::Observer* observer) {
DCHECK(base::CurrentUIThread::IsSet());
observers_.RemoveObserver(observer);
}
void AccelerometerFileReader::StartListenToTabletModeController() {
DCHECK(base::CurrentUIThread::IsSet());
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
void AccelerometerFileReader::StopListenToTabletModeController() {
DCHECK(base::CurrentUIThread::IsSet());
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void AccelerometerFileReader::SetEmitEvents(bool emit_events) {
DCHECK(base::CurrentUIThread::IsSet());
emit_events_ = emit_events;
}
void AccelerometerFileReader::EnableAccelerometerReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (read_refresh_timer_.IsRunning())
return;
read_refresh_timer_.Start(FROM_HERE, kDelayBetweenReads, this,
&AccelerometerFileReader::ReadSample);
}
void AccelerometerFileReader::DisableAccelerometerReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!read_refresh_timer_.IsRunning())
return;
read_refresh_timer_.Stop();
}
void AccelerometerFileReader::TriggerRead() {
DCHECK(base::CurrentUIThread::IsSet());
switch (initialization_state_) {
case State::SUCCESS:
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
blocking_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&AccelerometerFileReader::InitializeInternal, this), base::BindOnce(&AccelerometerFileReader::EnableAccelerometerReading,
kDelayBetweenReads); this));
} else { }
break;
case State::FAILED:
LOG(ERROR) << "Failed to initialize for accelerometer read.\n"; LOG(ERROR) << "Failed to initialize for accelerometer read.\n";
initialization_state_ = State::FAILED; break;
case State::INITIALIZING:
ui_task_runner_->PostNonNestableDelayedTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::TriggerRead, this),
kDelayBetweenInitChecks);
break;
}
}
void AccelerometerFileReader::CancelRead() {
DCHECK(base::CurrentUIThread::IsSet());
if (initialization_state_ == State::SUCCESS &&
ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::DisableAccelerometerReading,
this));
}
}
void AccelerometerFileReader::OnTabletPhysicalStateChanged() {
DCHECK(base::CurrentUIThread::IsSet());
// When CrOS EC lid angle driver is not present, accelerometer read is always
// ON and can't be tuned. Thus AccelerometerFileReader no longer listens to
// tablet mode event.
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::NOT_SUPPORTED) {
tablet_mode_controller->RemoveObserver(this);
return;
} }
// Auto rotation is turned on when the device is physically used as a tablet
// (i.e. flipped or detached), regardless of the UI state (i.e. whether tablet
// mode is turned on or off).
const bool is_auto_rotation_on =
tablet_mode_controller->is_in_tablet_physical_state();
if (is_auto_rotation_on)
TriggerRead();
else
CancelRead();
} }
void AccelerometerFileReader::InitializeInternal() { AccelerometerFileReader::InitializationResult::InitializationResult()
DCHECK(base::SequencedTaskRunnerHandle::IsSet()); : initialization_state(State::INITIALIZING),
DCHECK_EQ(State::INITIALIZING, initialization_state_); ec_lid_angle_driver_status(ECLidAngleDriverStatus::UNKNOWN) {}
AccelerometerFileReader::InitializationResult::~InitializationResult() =
default;
AccelerometerFileReader::ReadingData::ReadingData() = default;
AccelerometerFileReader::ReadingData::ReadingData(const ReadingData&) = default;
AccelerometerFileReader::ReadingData::~ReadingData() = default;
AccelerometerFileReader::ConfigurationData::ConfigurationData() : count(0) {
for (int i = 0; i < ACCELEROMETER_SOURCE_COUNT; ++i) {
has[i] = false;
for (int j = 0; j < 3; ++j) {
scale[i][j] = 0;
index[i][j] = -1;
}
}
}
AccelerometerFileReader::ConfigurationData::~ConfigurationData() = default;
AccelerometerFileReader::~AccelerometerFileReader() = default;
void AccelerometerFileReader::TryScheduleInitialize() {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK(blocking_task_runner_);
// Asynchronously detect and initialize the accelerometer to avoid delaying
// startup.
blocking_task_runner_->PostTaskAndReplyWithResult(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::InitializeInternal, this),
base::BindOnce(
&AccelerometerFileReader::SetStatesWithInitializationResult, this));
}
AccelerometerFileReader::InitializationResult
AccelerometerFileReader::InitializeInternal() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Log the warning/error messages only in the first initialization to prevent // Log the warning/error messages only in the first initialization to prevent
// spamming during the retries of initialization. // spamming during the retries of initialization.
static bool first_initialization_ = true; static bool first_initialization_ = true;
InitializationResult result;
// Check for accelerometer symlink which will be created by the udev rules // Check for accelerometer symlink which will be created by the udev rules
// file on detecting the device. // file on detecting the device.
if (base::IsDirectoryEmpty(base::FilePath(kAccelerometerDevicePath))) { if (base::IsDirectoryEmpty(base::FilePath(kAccelerometerDevicePath))) {
...@@ -197,18 +330,11 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -197,18 +330,11 @@ void AccelerometerFileReader::InitializeInternal() {
<< kAccelerometerDevicePath; << kAccelerometerDevicePath;
} }
first_initialization_ = false; first_initialization_ = false;
TryScheduleInitializeInternal(); return result;
} else {
initialization_state_ = State::FAILED;
}
return;
} }
if (base::SysInfo::IsRunningOnChromeOS() && result.initialization_state = State::FAILED;
!base::IsDirectoryEmpty(base::FilePath(kECLidAngleDriverPath))) { return result;
ec_lid_angle_driver_status_ = ECLidAngleDriverStatus::SUPPORTED;
} else {
ec_lid_angle_driver_status_ = ECLidAngleDriverStatus::NOT_SUPPORTED;
} }
// Find trigger to use: // Find trigger to use:
...@@ -233,8 +359,8 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -233,8 +359,8 @@ void AccelerometerFileReader::InitializeInternal() {
LOG(ERROR) << "Accelerometer trigger does not exist at " LOG(ERROR) << "Accelerometer trigger does not exist at "
<< trigger_now.value(); << trigger_now.value();
} }
initialization_state_ = State::FAILED; result.initialization_state = State::FAILED;
return; return result;
} else { } else {
configuration_.trigger_now = trigger_now; configuration_.trigger_now = trigger_now;
break; break;
...@@ -246,11 +372,11 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -246,11 +372,11 @@ void AccelerometerFileReader::InitializeInternal() {
if (first_initialization_) if (first_initialization_)
LOG(ERROR) << "Accelerometer trigger not found"; LOG(ERROR) << "Accelerometer trigger not found";
first_initialization_ = false; first_initialization_ = false;
TryScheduleInitializeInternal(); return result;
} else {
initialization_state_ = State::FAILED;
} }
return;
result.initialization_state = State::FAILED;
return result;
} }
base::FileEnumerator symlink_dir(base::FilePath(kAccelerometerDevicePath), base::FileEnumerator symlink_dir(base::FilePath(kAccelerometerDevicePath),
...@@ -262,8 +388,8 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -262,8 +388,8 @@ void AccelerometerFileReader::InitializeInternal() {
if (!base::ReadSymbolicLink(name, &iio_device)) { if (!base::ReadSymbolicLink(name, &iio_device)) {
LOG(ERROR) << "Failed to read symbolic link " << kAccelerometerDevicePath LOG(ERROR) << "Failed to read symbolic link " << kAccelerometerDevicePath
<< "/" << name.MaybeAsASCII() << "\n"; << "/" << name.MaybeAsASCII() << "\n";
initialization_state_ = State::FAILED; result.initialization_state = State::FAILED;
return; return result;
} }
base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath) base::FilePath iio_path(base::FilePath(kAccelerometerIioBasePath)
...@@ -274,14 +400,14 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -274,14 +400,14 @@ void AccelerometerFileReader::InitializeInternal() {
&location); &location);
if (legacy_cross_accel) { if (legacy_cross_accel) {
if (!InitializeLegacyAccelerometers(iio_path, name)) { if (!InitializeLegacyAccelerometers(iio_path, name)) {
initialization_state_ = State::FAILED; result.initialization_state = State::FAILED;
return; return result;
} }
} else { } else {
base::TrimWhitespaceASCII(location, base::TRIM_ALL, &location); base::TrimWhitespaceASCII(location, base::TRIM_ALL, &location);
if (!InitializeAccelerometer(iio_path, name, location)) { if (!InitializeAccelerometer(iio_path, name, location)) {
initialization_state_ = State::FAILED; result.initialization_state = State::FAILED;
return; return result;
} }
} }
} }
...@@ -298,154 +424,77 @@ void AccelerometerFileReader::InitializeInternal() { ...@@ -298,154 +424,77 @@ void AccelerometerFileReader::InitializeInternal() {
: kAccelerometerAxes[j]; : kAccelerometerAxes[j];
LOG(ERROR) << "Field index for " << kLocationStrings[i] << " " << axis LOG(ERROR) << "Field index for " << kLocationStrings[i] << " " << axis
<< " axis out of bounds."; << " axis out of bounds.";
initialization_state_ = State::FAILED; result.initialization_state = State::FAILED;
return; return result;
} }
} }
} }
initialization_state_ = State::SUCCESS; result.initialization_state = State::SUCCESS;
result.ec_lid_angle_driver_status =
// If ChromeOS lid angle driver is not present, start accelerometer read and (base::SysInfo::IsRunningOnChromeOS() &&
// read is always on. !base::IsDirectoryEmpty(base::FilePath(kECLidAngleDriverPath)))
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::NOT_SUPPORTED) ? ECLidAngleDriverStatus::SUPPORTED
EnableAccelerometerReading(); : ECLidAngleDriverStatus::NOT_SUPPORTED;
}
void AccelerometerFileReader::Read() {
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
if (!accelerometer_read_on_)
return;
ReadFileAndNotify();
task_runner_->PostNonNestableDelayedTask(
FROM_HERE, base::BindOnce(&AccelerometerFileReader::Read, this),
kDelayBetweenReads);
}
void AccelerometerFileReader::EnableAccelerometerReading() {
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
if (accelerometer_read_on_)
return;
accelerometer_read_on_ = true; return result;
Read();
} }
void AccelerometerFileReader::DisableAccelerometerReading() { void AccelerometerFileReader::SetStatesWithInitializationResult(
DCHECK(base::SequencedTaskRunnerHandle::IsSet()); InitializationResult result) {
if (!accelerometer_read_on_) DCHECK(base::CurrentUIThread::IsSet());
return;
accelerometer_read_on_ = false;
}
void AccelerometerFileReader::CheckInitStatus() { initialization_state_ = result.initialization_state;
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
switch (initialization_state_) { switch (initialization_state_) {
case State::SUCCESS:
EnableAccelerometerReading();
break;
case State::FAILED:
LOG(ERROR) << "Failed to initialize for accelerometer read.\n";
break;
case State::INITIALIZING: case State::INITIALIZING:
task_runner_->PostNonNestableDelayedTask( // If we haven't yet passed the timeout cutoff, try this again. This will
// be scheduled at the same rate as reading.
if (base::TimeTicks::Now() < initialization_timeout_) {
blocking_task_runner_->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&AccelerometerFileReader::CheckInitStatus, this), base::BindOnce(&AccelerometerFileReader::TryScheduleInitialize,
kDelayBetweenInitChecks); this),
break; kDelayBetweenReads);
} } else {
}
void AccelerometerFileReader::TriggerRead() {
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
switch (initialization_state_) {
case State::SUCCESS:
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED)
EnableAccelerometerReading();
break;
case State::FAILED:
LOG(ERROR) << "Failed to initialize for accelerometer read.\n"; LOG(ERROR) << "Failed to initialize for accelerometer read.\n";
break; initialization_state_ = State::FAILED;
case State::INITIALIZING:
task_runner_->PostNonNestableTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::CheckInitStatus, this));
break;
} }
}
void AccelerometerFileReader::CancelRead() { break;
DCHECK(base::SequencedTaskRunnerHandle::IsSet());
if (initialization_state_ == State::SUCCESS &&
ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED)
DisableAccelerometerReading();
}
void AccelerometerFileReader::AddObserver( case State::SUCCESS:
AccelerometerReader::Observer* observer) { DCHECK_NE(result.ec_lid_angle_driver_status,
observers_->AddObserver(observer); ECLidAngleDriverStatus::UNKNOWN);
if (initialization_state_ == State::SUCCESS) { ec_lid_angle_driver_status_ = result.ec_lid_angle_driver_status;
task_runner_->PostNonNestableTask(
if (ec_lid_angle_driver_status_ ==
ECLidAngleDriverStatus::NOT_SUPPORTED) {
// If ChromeOS lid angle driver is not present, start accelerometer read
// and read is always on.
blocking_task_runner_->PostTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&AccelerometerFileReader::ReadFileAndNotify, this)); base::BindOnce(&AccelerometerFileReader::EnableAccelerometerReading,
this));
} }
}
void AccelerometerFileReader::RemoveObserver( break;
AccelerometerReader::Observer* observer) {
observers_->RemoveObserver(observer);
}
void AccelerometerFileReader::StartListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
void AccelerometerFileReader::StopListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void AccelerometerFileReader::SetEmitEvents(bool emit_events) {
task_runner_->PostNonNestableTask(
FROM_HERE, base::BindOnce(&AccelerometerFileReader::SetEmitEventsInternal,
this, emit_events));
}
void AccelerometerFileReader::SetEmitEventsInternal(bool emit_events) { case State::FAILED:
DCHECK(base::SequencedTaskRunnerHandle::IsSet()); break;
emit_events_ = emit_events;
}
void AccelerometerFileReader::OnTabletPhysicalStateChanged() { default:
// When CrOS EC lid angle driver is not present, accelerometer read is always LOG(FATAL) << "Unexpected state: "
// ON and can't be tuned. Thus AccelerometerFileReader no longer listens to << static_cast<int>(initialization_state_);
// tablet mode event. break;
auto* tablet_mode_controller = Shell::Get()->tablet_mode_controller();
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::NOT_SUPPORTED) {
tablet_mode_controller->RemoveObserver(this);
return;
} }
// Auto rotation is turned on when the device is physically used as a tablet
// (i.e. flipped or detached), regardless of the UI state (i.e. whether tablet
// mode is turned on or off).
const bool is_auto_rotation_on =
tablet_mode_controller->is_in_tablet_physical_state();
task_runner_->PostNonNestableTask(
FROM_HERE,
is_auto_rotation_on
? base::BindOnce(&AccelerometerFileReader::TriggerRead, this)
: base::BindOnce(&AccelerometerFileReader::CancelRead, this));
} }
AccelerometerFileReader::~AccelerometerFileReader() = default;
bool AccelerometerFileReader::InitializeAccelerometer( bool AccelerometerFileReader::InitializeAccelerometer(
const base::FilePath& iio_path, const base::FilePath& iio_path,
const base::FilePath& name, const base::FilePath& name,
const std::string& location) { const std::string& location) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
size_t config_index = 0; size_t config_index = 0;
for (; config_index < base::size(kLocationStrings); ++config_index) { for (; config_index < base::size(kLocationStrings); ++config_index) {
if (location == kLocationStrings[config_index]) if (location == kLocationStrings[config_index])
...@@ -491,6 +540,8 @@ bool AccelerometerFileReader::InitializeAccelerometer( ...@@ -491,6 +540,8 @@ bool AccelerometerFileReader::InitializeAccelerometer(
bool AccelerometerFileReader::InitializeLegacyAccelerometers( bool AccelerometerFileReader::InitializeLegacyAccelerometers(
const base::FilePath& iio_path, const base::FilePath& iio_path,
const base::FilePath& name) { const base::FilePath& name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ReadingData reading_data; ReadingData reading_data;
reading_data.path = reading_data.path =
base::FilePath(kAccelerometerDevicePath).Append(name.BaseName()); base::FilePath(kAccelerometerDevicePath).Append(name.BaseName());
...@@ -542,8 +593,8 @@ bool AccelerometerFileReader::InitializeLegacyAccelerometers( ...@@ -542,8 +593,8 @@ bool AccelerometerFileReader::InitializeLegacyAccelerometers(
return true; return true;
} }
void AccelerometerFileReader::ReadFileAndNotify() { void AccelerometerFileReader::ReadSample() {
DCHECK_EQ(State::SUCCESS, initialization_state_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Initiate the trigger to read accelerometers simultaneously. // Initiate the trigger to read accelerometers simultaneously.
int bytes_written = base::WriteFile(configuration_.trigger_now, "1\n", 2); int bytes_written = base::WriteFile(configuration_.trigger_now, "1\n", 2);
...@@ -553,7 +604,7 @@ void AccelerometerFileReader::ReadFileAndNotify() { ...@@ -553,7 +604,7 @@ void AccelerometerFileReader::ReadFileAndNotify() {
} }
// Read resulting sample from /dev/cros-ec-accel. // Read resulting sample from /dev/cros-ec-accel.
update_ = new AccelerometerUpdate(); scoped_refptr<AccelerometerUpdate> update = new AccelerometerUpdate();
for (auto reading_data : configuration_.reading_data) { for (auto reading_data : configuration_.reading_data) {
int reading_size = reading_data.sources.size() * kSizeOfReading; int reading_size = reading_data.sources.size() * kSizeOfReading;
DCHECK_GT(reading_size, 0); DCHECK_GT(reading_size, 0);
...@@ -577,7 +628,7 @@ void AccelerometerFileReader::ReadFileAndNotify() { ...@@ -577,7 +628,7 @@ void AccelerometerFileReader::ReadFileAndNotify() {
for (AccelerometerSource source : reading_data.sources) { for (AccelerometerSource source : reading_data.sources) {
DCHECK(configuration_.has[source]); DCHECK(configuration_.has[source]);
int16_t* values = reinterpret_cast<int16_t*>(reading); int16_t* values = reinterpret_cast<int16_t*>(reading);
update_->Set(source, update->Set(source,
values[configuration_.index[source][0]] * values[configuration_.index[source][0]] *
configuration_.scale[source][0], configuration_.scale[source][0],
values[configuration_.index[source][1]] * values[configuration_.index[source][1]] *
...@@ -587,28 +638,22 @@ void AccelerometerFileReader::ReadFileAndNotify() { ...@@ -587,28 +638,22 @@ void AccelerometerFileReader::ReadFileAndNotify() {
} }
} }
if (!emit_events_) ui_task_runner_->PostTask(
return; FROM_HERE,
base::BindOnce(&AccelerometerFileReader::NotifyObserversWithUpdate, this,
observers_->Notify(FROM_HERE, update));
&AccelerometerReader::Observer::OnAccelerometerUpdated,
update_);
} }
AccelerometerFileReader::ReadingData::ReadingData() = default; void AccelerometerFileReader::NotifyObserversWithUpdate(
AccelerometerFileReader::ReadingData::ReadingData(const ReadingData&) = default; scoped_refptr<AccelerometerUpdate> update) {
AccelerometerFileReader::ReadingData::~ReadingData() = default; DCHECK(base::CurrentUIThread::IsSet());
DCHECK(update);
AccelerometerFileReader::ConfigurationData::ConfigurationData() : count(0) { if (!emit_events_)
for (int i = 0; i < ACCELEROMETER_SOURCE_COUNT; ++i) { return;
has[i] = false;
for (int j = 0; j < 3; ++j) {
scale[i][j] = 0;
index[i][j] = -1;
}
}
}
AccelerometerFileReader::ConfigurationData::~ConfigurationData() = default; for (auto& observer : observers_)
observer.OnAccelerometerUpdated(update);
}
} // namespace ash } // namespace ash
...@@ -11,7 +11,9 @@ ...@@ -11,7 +11,9 @@
#include "ash/accelerometer/accelerometer_reader.h" #include "ash/accelerometer/accelerometer_reader.h"
#include "ash/public/cpp/tablet_mode_observer.h" #include "ash/public/cpp/tablet_mode_observer.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/observer_list_threadsafe.h" #include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
namespace ash { namespace ash {
...@@ -33,18 +35,10 @@ class AccelerometerFileReader : public AccelerometerProviderInterface, ...@@ -33,18 +35,10 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
void StopListenToTabletModeController() override; void StopListenToTabletModeController() override;
void SetEmitEvents(bool emit_events) override; void SetEmitEvents(bool emit_events) override;
// Attempts to read the accelerometer data. Upon success, converts the raw
// reading to an AccelerometerUpdate and notifies observers. Triggers another
// read at the current sampling rate.
void Read();
// Controls accelerometer reading. // Controls accelerometer reading.
void EnableAccelerometerReading(); void EnableAccelerometerReading();
void DisableAccelerometerReading(); void DisableAccelerometerReading();
// Tracks if accelerometer initialization is completed.
void CheckInitStatus();
// With ChromeOS EC lid angle driver present, it's triggered when the device // With ChromeOS EC lid angle driver present, it's triggered when the device
// is physically used as a tablet (even thought its UI might be in clamshell // is physically used as a tablet (even thought its UI might be in clamshell
// mode), cancelled otherwise. // mode), cancelled otherwise.
...@@ -55,6 +49,14 @@ class AccelerometerFileReader : public AccelerometerProviderInterface, ...@@ -55,6 +49,14 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
void OnTabletPhysicalStateChanged() override; void OnTabletPhysicalStateChanged() override;
private: private:
struct InitializationResult {
InitializationResult();
~InitializationResult();
State initialization_state;
ECLidAngleDriverStatus ec_lid_angle_driver_status;
};
// Represents necessary information in order to read an accelerometer device. // Represents necessary information in order to read an accelerometer device.
struct ReadingData { struct ReadingData {
ReadingData(); ReadingData();
...@@ -95,18 +97,24 @@ class AccelerometerFileReader : public AccelerometerProviderInterface, ...@@ -95,18 +97,24 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
~AccelerometerFileReader() override; ~AccelerometerFileReader() override;
// Detects the accelerometer configuration. // Post a task to initialize on |task_runner_| and process the result on the
// UI thread. May be called multiple times in the retries.
void TryScheduleInitialize();
// Detects the accelerometer configuration in |task_runner_|.
// If an accelerometer is available, it triggers reads. // If an accelerometer is available, it triggers reads.
// This function MAY be called more than once. // This function MAY be called more than once.
// This function contains the actual initialization code to be run by the // This function contains the actual initialization code to be run by the
// Initialize function. It is needed because on some devices the sensor hub // Initialize function. It is needed because on some devices the sensor hub
// isn't available at the time the call to Initialize is made. If the sensor // isn't available at the time the call to Initialize is made.
// is found to be missing we'll make a call to TryScheduleInitializeInternal. // If the sensor is found to be missing we'll request a re-run of this
void InitializeInternal(); // function by returning State::INITIALIZING. TryScheduleInitializeInternal.
InitializationResult InitializeInternal();
// Attempt to reschedule a run of InitializeInternal(). The function will be // Attempt to finish the initialization with the result state. If it's
// scheduled to run again if Now() < initialization_timeout_. // State::INITIALIZING, it means something is missing and need to re-run
void TryScheduleInitializeInternal(); // |TryScheduleInitialize|, if not timed out yet.
void SetStatesWithInitializationResult(InitializationResult result);
// When accelerometers are presented as separate iio_devices this will perform // When accelerometers are presented as separate iio_devices this will perform
// the initialize for one of the devices, at the given |iio_path| and the // the initialize for one of the devices, at the given |iio_path| and the
...@@ -122,14 +130,10 @@ class AccelerometerFileReader : public AccelerometerProviderInterface, ...@@ -122,14 +130,10 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
bool InitializeLegacyAccelerometers(const base::FilePath& iio_path, bool InitializeLegacyAccelerometers(const base::FilePath& iio_path,
const base::FilePath& name); const base::FilePath& name);
// Attempts to read the accelerometer data. Upon a success, converts the raw // Attempts to read the accelerometer data in |task_runner_|. Upon a success,
// reading to an AccelerometerUpdate and notifies observers. // converts the raw reading to an AccelerometerUpdate and notifies observers.
void ReadFileAndNotify(); void ReadSample();
void NotifyObserversWithUpdate(scoped_refptr<AccelerometerUpdate> update);
void SetEmitEventsInternal(bool emit_events);
// True if periodical accelerometer read is on.
bool accelerometer_read_on_ = false;
bool emit_events_ = true; bool emit_events_ = true;
...@@ -137,17 +141,24 @@ class AccelerometerFileReader : public AccelerometerProviderInterface, ...@@ -137,17 +141,24 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
base::TimeTicks initialization_timeout_; base::TimeTicks initialization_timeout_;
// The accelerometer configuration. // The accelerometer configuration.
ConfigurationData configuration_; // Only used in |blocking_task_runner_|.
ConfigurationData configuration_ GUARDED_BY_CONTEXT(sequence_checker_);
// The timer to repeatedly read samples.
// Only used in |blocking_task_runner_|.
base::RepeatingTimer read_refresh_timer_
GUARDED_BY_CONTEXT(sequence_checker_);
// The observers to notify of accelerometer updates. // The observers to notify of accelerometer updates.
scoped_refptr<base::ObserverListThreadSafe<AccelerometerReader::Observer>> base::ObserverList<AccelerometerReader::Observer>::Unchecked observers_;
observers_;
// The task runner to use for blocking tasks. // The task runner to use for blocking tasks.
scoped_refptr<base::SequencedTaskRunner> task_runner_; scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
// The task runner of the UI thread.
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
// The last seen accelerometer data. SEQUENCE_CHECKER(sequence_checker_);
scoped_refptr<AccelerometerUpdate> update_;
}; };
} // namespace ash } // namespace ash
......
...@@ -25,8 +25,15 @@ enum class ECLidAngleDriverStatus { UNKNOWN, SUPPORTED, NOT_SUPPORTED }; ...@@ -25,8 +25,15 @@ enum class ECLidAngleDriverStatus { UNKNOWN, SUPPORTED, NOT_SUPPORTED };
class AccelerometerProviderInterface; class AccelerometerProviderInterface;
// Reads an accelerometer device and reports data back to an // AccelerometerReader should only be used on the UI thread.
// AccelerometerDelegate. // It notifies observers if EC Lid Angle Driver is supported, and provides
// accelerometers' (lid and base) samples.
// The current usages of accelerometers' samples are for calculating the angle
// between the lid and the base, which can be substituted by EC Lid Angle
// Driver, if it exists, and the auto rotation, which only needs
// lid-accelerometer's data.
// Therefore, if EC Lid Angle Driver is present, base-accelerometer's samples
// may be ignored and not sent to the observers.
class ASH_EXPORT AccelerometerReader { class ASH_EXPORT AccelerometerReader {
public: public:
// An interface to receive data from the AccelerometerReader. // An interface to receive data from the AccelerometerReader.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment