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

accelerometer: Add OnECLidAngleDriverStatusChanged

This commit adds OnECLidAngleDriverStatusChanged in
AccelerometerReader::Observer, which simplifies implementations of
AccelerometerProviderInterface by removing the one-time-read.
This commit also implements AddObserver, RemoveObserver,
StartListenToTabletModeController, and StopListenToTabletModeController
in AccelerometerProviderInterface instead of the derived classes.

BUG=b:171446270, b:172414227
TEST=unit tests and run on kohaku(with iioservice) and helios(without
iioservice)

Change-Id: Ic0d37edc8df22cbd6facf54fe5fb0d11425be2b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2538855
Commit-Queue: Cheng-Hao Yang <chenghaoyang@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarXiaoqian Dai <xdai@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841313}
parent 9481083f
......@@ -20,7 +20,6 @@
#include "base/location.h"
#include "base/memory/singleton.h"
#include "base/numerics/math_constants.h"
#include "base/observer_list_threadsafe.h"
#include "base/sequenced_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
......@@ -142,8 +141,7 @@ bool ReadFileToDouble(const base::FilePath& path, double* value) {
} // namespace
AccelerometerFileReader::AccelerometerFileReader()
: ui_task_runner_(base::SequencedTaskRunnerHandle::Get()) {}
AccelerometerFileReader::AccelerometerFileReader() = default;
void AccelerometerFileReader::PrepareAndInitialize() {
DCHECK(base::CurrentUIThread::IsSet());
......@@ -165,62 +163,11 @@ void AccelerometerFileReader::PrepareAndInitialize() {
TryScheduleInitialize();
}
void AccelerometerFileReader::AddObserver(
AccelerometerReader::Observer* observer) {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK(blocking_task_runner_);
observers_.AddObserver(observer);
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) {
if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::SUPPORTED) {
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::EnableAccelerometerReading,
......@@ -242,7 +189,7 @@ void AccelerometerFileReader::TriggerRead() {
void AccelerometerFileReader::CancelRead() {
DCHECK(base::CurrentUIThread::IsSet());
if (initialization_state_ == State::SUCCESS &&
ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::SUPPORTED) {
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::DisableAccelerometerReading,
......@@ -250,30 +197,6 @@ void AccelerometerFileReader::CancelRead() {
}
}
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();
}
AccelerometerFileReader::InitializationResult::InitializationResult()
: initialization_state(State::INITIALIZING),
ec_lid_angle_driver_status(ECLidAngleDriverStatus::UNKNOWN) {}
......@@ -465,9 +388,9 @@ void AccelerometerFileReader::SetStatesWithInitializationResult(
case State::SUCCESS:
DCHECK_NE(result.ec_lid_angle_driver_status,
ECLidAngleDriverStatus::UNKNOWN);
ec_lid_angle_driver_status_ = result.ec_lid_angle_driver_status;
SetECLidAngleDriverStatus(result.ec_lid_angle_driver_status);
if (ec_lid_angle_driver_status_ ==
if (GetECLidAngleDriverStatus() ==
ECLidAngleDriverStatus::NOT_SUPPORTED) {
// If ChromeOS lid angle driver is not present, start accelerometer read
// and read is always on.
......@@ -593,6 +516,20 @@ bool AccelerometerFileReader::InitializeLegacyAccelerometers(
return true;
}
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_);
read_refresh_timer_.Stop();
}
void AccelerometerFileReader::ReadSample() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
......@@ -640,19 +577,8 @@ void AccelerometerFileReader::ReadSample() {
ui_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AccelerometerFileReader::NotifyObserversWithUpdate, this,
base::BindOnce(&AccelerometerFileReader::NotifyAccelerometerUpdated, this,
update));
}
void AccelerometerFileReader::NotifyObserversWithUpdate(
const AccelerometerUpdate& update) {
DCHECK(base::CurrentUIThread::IsSet());
if (!emit_events_)
return;
for (auto& observer : observers_)
observer.OnAccelerometerUpdated(update);
}
} // namespace ash
......@@ -9,9 +9,7 @@
#include <vector>
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/public/cpp/tablet_mode_observer.h"
#include "base/files/file_util.h"
#include "base/observer_list.h"
#include "base/sequence_checker.h"
#include "base/timer/timer.h"
......@@ -20,8 +18,7 @@ namespace ash {
// Work that runs on a base::TaskRunner. It determines the accelerometer
// configuration, and reads the data. Upon a successful read it will notify
// all observers.
class AccelerometerFileReader : public AccelerometerProviderInterface,
public TabletModeObserver {
class AccelerometerFileReader : public AccelerometerProviderInterface {
public:
AccelerometerFileReader();
AccelerometerFileReader(const AccelerometerFileReader&) = delete;
......@@ -29,24 +26,8 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
// AccelerometerProviderInterface:
void PrepareAndInitialize() override;
void AddObserver(AccelerometerReader::Observer* observer) override;
void RemoveObserver(AccelerometerReader::Observer* observer) override;
void StartListenToTabletModeController() override;
void StopListenToTabletModeController() override;
void SetEmitEvents(bool emit_events) override;
// Controls accelerometer reading.
void EnableAccelerometerReading();
void DisableAccelerometerReading();
// 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
// mode), cancelled otherwise.
void TriggerRead();
void CancelRead();
// TabletModeObserver:
void OnTabletPhysicalStateChanged() override;
void TriggerRead() override;
void CancelRead() override;
private:
struct InitializationResult {
......@@ -97,11 +78,11 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
~AccelerometerFileReader() override;
// Post a task to initialize on |task_runner_| and process the result on the
// UI thread. May be called multiple times in the retries.
// Post a task to initialize on |blocking_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_|.
// Detects the accelerometer configuration in |blocking_task_runner_|.
// If an accelerometer is available, it triggers reads.
// This function MAY be called more than once.
// This function contains the actual initialization code to be run by the
......@@ -130,12 +111,14 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
bool InitializeLegacyAccelerometers(const base::FilePath& iio_path,
const base::FilePath& name);
// Attempts to read the accelerometer data in |task_runner_|. Upon a success,
// converts the raw reading to an AccelerometerUpdate and notifies observers.
void ReadSample();
void NotifyObserversWithUpdate(const AccelerometerUpdate& update);
// Controls accelerometer reading.
void EnableAccelerometerReading();
void DisableAccelerometerReading();
bool emit_events_ = true;
// Attempts to read the accelerometer data in |blocking_task_runner_|. Upon a
// success, converts the raw reading to an AccelerometerUpdate and notifies
// observers.
void ReadSample();
// The time at which initialization re-tries should stop.
base::TimeTicks initialization_timeout_;
......@@ -149,15 +132,9 @@ class AccelerometerFileReader : public AccelerometerProviderInterface,
base::RepeatingTimer read_refresh_timer_
GUARDED_BY_CONTEXT(sequence_checker_);
// The observers to notify of accelerometer updates.
base::ObserverList<AccelerometerReader::Observer>::Unchecked observers_;
// The task runner to use for blocking tasks.
scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
// The task runner of the UI thread.
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
SEQUENCE_CHECKER(sequence_checker_);
};
......
......@@ -15,11 +15,8 @@
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_samples_observer.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/tablet_mode_observer.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
......@@ -31,7 +28,6 @@ namespace ash {
// observers.
class ASH_EXPORT AccelerometerProviderMojo
: public AccelerometerProviderInterface,
public TabletModeObserver,
public chromeos::sensors::mojom::SensorHalClient {
public:
AccelerometerProviderMojo();
......@@ -41,27 +37,19 @@ class ASH_EXPORT AccelerometerProviderMojo
// AccelerometerProviderInterface:
void PrepareAndInitialize() override;
void AddObserver(AccelerometerReader::Observer* observer) override;
void RemoveObserver(AccelerometerReader::Observer* observer) override;
void StartListenToTabletModeController() override;
void StopListenToTabletModeController() override;
void SetEmitEvents(bool emit_events) override;
// TabletModeObserver:
void OnTabletPhysicalStateChanged() override;
void TriggerRead() override;
void CancelRead() override;
// chromeos::sensors::mojom::SensorHalClient:
void SetUpChannel(mojo::PendingRemote<chromeos::sensors::mojom::SensorService>
pending_remote) override;
// 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
// mode), cancelled otherwise.
void TriggerRead();
void CancelRead();
State GetInitializationStateForTesting() const;
protected:
// AccelerometerProviderInterface:
bool ShouldDelayOnTabletPhysicalStateChanged() override;
private:
struct AccelerometerData {
AccelerometerData();
......@@ -117,15 +105,13 @@ class ASH_EXPORT AccelerometerProviderMojo
void EnableAccelerometerReading();
void DisableAccelerometerReading();
// Called by |observers_|, containing a sample of the accelerometer.
// Called by |AccelerometerData::samples_observer| stored in the
// |accelerometers_| map, containing a sample of the accelerometer.
void OnSampleUpdatedCallback(int iio_device_id, std::vector<float> sample);
// Sets FAILED to |initialization_state_| due to an error.
void FailedToInitialize();
// The task runner to use for blocking tasks.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// The Mojo channel connecting to Sensor Hal Dispatcher.
mojo::Receiver<chromeos::sensors::mojom::SensorHalClient> sensor_hal_client_{
this};
......@@ -148,20 +134,9 @@ class ASH_EXPORT AccelerometerProviderMojo
// |ec_lid_angle_driver_status_| is set.
bool pending_on_tablet_physical_state_changed_ = false;
// One time read upon |AddObserver|.
// Some observers need to know ECLidAngleDriverStatus, and it's guaranteed to
// be set before reading samples. When adding an observer, trigger at least
// one sample to notify observers that ECLidAngleDriverStatus has been set.
bool one_time_read_ = false;
// True if periodical accelerometer read is on.
bool accelerometer_read_on_ = false;
bool emit_events_ = true;
// The observers to notify of accelerometer updates.
base::ObserverList<AccelerometerReader::Observer>::Unchecked observers_;
// The last seen accelerometer data.
AccelerometerUpdate update_;
......
......@@ -32,6 +32,10 @@ constexpr int64_t kFakeSampleData[] = {1, 2, 3};
class FakeObserver : public AccelerometerReader::Observer {
public:
void OnECLidAngleDriverStatusChanged(bool is_supported) override {
CHECK(!is_supported_.has_value());
is_supported_ = is_supported;
}
void OnAccelerometerUpdated(const AccelerometerUpdate& update) override {
for (uint32_t index = 0; index < ACCELEROMETER_SOURCE_COUNT; ++index) {
auto source = static_cast<AccelerometerSource>(index);
......@@ -46,6 +50,7 @@ class FakeObserver : public AccelerometerReader::Observer {
update_ = update;
}
base::Optional<bool> is_supported_;
AccelerometerUpdate update_;
};
......@@ -103,7 +108,8 @@ class AccelerometerProviderMojoTest : public ::testing::Test {
std::unique_ptr<chromeos::sensors::FakeSensorHalServer> sensor_hal_server_;
scoped_refptr<AccelerometerProviderMojo> provider_;
base::test::SingleThreadTaskEnvironment task_environment;
base::test::SingleThreadTaskEnvironment task_environment{
base::test::TaskEnvironment::MainThreadType::UI};
};
TEST_F(AccelerometerProviderMojoTest, CheckNoScale) {
......@@ -117,6 +123,8 @@ TEST_F(AccelerometerProviderMojoTest, CheckNoScale) {
// Wait until initialization failed.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_.is_supported_.has_value());
EXPECT_FALSE(observer_.is_supported_.value());
EXPECT_EQ(provider_->GetInitializationStateForTesting(), State::FAILED);
}
......@@ -131,6 +139,8 @@ TEST_F(AccelerometerProviderMojoTest, CheckNoLocation) {
// Wait until initialization failed.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_.is_supported_.has_value());
EXPECT_FALSE(observer_.is_supported_.value());
EXPECT_EQ(provider_->GetInitializationStateForTesting(), State::SUCCESS);
}
......@@ -141,6 +151,8 @@ TEST_F(AccelerometerProviderMojoTest, GetSamplesOfOneAccel) {
// Wait until a sample is received.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_.is_supported_.has_value());
EXPECT_FALSE(observer_.is_supported_.value());
EXPECT_EQ(provider_->GetInitializationStateForTesting(), State::SUCCESS);
EXPECT_TRUE(observer_.update_.has(ACCELEROMETER_SOURCE_SCREEN));
......@@ -159,6 +171,8 @@ TEST_F(AccelerometerProviderMojoTest, GetSamplesWithNoLidAngle) {
// Wait until samples are received.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_.is_supported_.has_value());
EXPECT_FALSE(observer_.is_supported_.value());
EXPECT_EQ(provider_->GetInitializationStateForTesting(), State::SUCCESS);
EXPECT_TRUE(observer_.update_.has(ACCELEROMETER_SOURCE_SCREEN));
......@@ -190,11 +204,13 @@ TEST_F(AccelerometerProviderMojoTest, GetSamplesWithLidAngle) {
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterServer(
sensor_hal_server_->PassRemote());
// Wait until all setups are finished and the one time read is done.
// Wait until all setups are finished and no samples updated.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(observer_.is_supported_.has_value());
EXPECT_TRUE(observer_.is_supported_.value());
EXPECT_EQ(provider_->GetInitializationStateForTesting(), State::SUCCESS);
EXPECT_TRUE(observer_.update_.has(ACCELEROMETER_SOURCE_SCREEN));
EXPECT_FALSE(observer_.update_.has(ACCELEROMETER_SOURCE_SCREEN));
EXPECT_FALSE(observer_.update_.has(ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD));
observer_.update_.Reset();
......
......@@ -8,9 +8,12 @@
#include "ash/accelerometer/accelerometer_file_reader.h"
#include "ash/accelerometer/accelerometer_provider_mojo.h"
#include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/no_destructor.h"
#include "base/posix/eintr_wrapper.h"
#include "base/sequenced_task_runner.h"
#include "base/task/current_thread.h"
#include "base/threading/sequenced_task_runner_handle.h"
namespace ash {
......@@ -52,10 +55,6 @@ void AccelerometerReader::SetEnabled(bool enabled) {
accelerometer_provider_->SetEmitEvents(enabled);
}
ECLidAngleDriverStatus AccelerometerReader::GetECLidAngleDriverStatus() const {
return accelerometer_provider_->GetECLidAngleDriverStatus();
}
void AccelerometerReader::SetECLidAngleDriverStatusForTesting(
ECLidAngleDriverStatus ec_lid_angle_driver_status) {
accelerometer_provider_->SetECLidAngleDriverStatusForTesting( // IN-TEST
......@@ -78,14 +77,110 @@ AccelerometerReader::AccelerometerReader() {
AccelerometerReader::~AccelerometerReader() = default;
void AccelerometerProviderInterface::OnTabletPhysicalStateChanged() {
DCHECK(base::CurrentUIThread::IsSet());
if (ShouldDelayOnTabletPhysicalStateChanged())
return;
// When CrOS EC lid angle driver is not present, accelerometer read is always
// ON and can't be tuned. Thus this object 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).
if (tablet_mode_controller->is_in_tablet_physical_state())
TriggerRead();
else
CancelRead();
}
void AccelerometerProviderInterface::AddObserver(
AccelerometerReader::Observer* observer) {
DCHECK(base::CurrentUIThread::IsSet());
if (ec_lid_angle_driver_status_ != ECLidAngleDriverStatus::UNKNOWN) {
observer->OnECLidAngleDriverStatusChanged(
ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED);
}
observers_.AddObserver(observer);
}
void AccelerometerProviderInterface::RemoveObserver(
AccelerometerReader::Observer* observer) {
DCHECK(base::CurrentUIThread::IsSet());
observers_.RemoveObserver(observer);
}
void AccelerometerProviderInterface::StartListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
void AccelerometerProviderInterface::StopListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void AccelerometerProviderInterface::SetEmitEvents(bool emit_events) {
DCHECK(base::CurrentUIThread::IsSet());
emit_events_ = emit_events;
}
void AccelerometerProviderInterface::SetECLidAngleDriverStatusForTesting(
ECLidAngleDriverStatus status) {
SetECLidAngleDriverStatus(status);
}
AccelerometerProviderInterface::AccelerometerProviderInterface()
: ui_task_runner_(base::SequencedTaskRunnerHandle::Get()) {
DCHECK(base::CurrentUIThread::IsSet());
}
AccelerometerProviderInterface::~AccelerometerProviderInterface() = default;
bool AccelerometerProviderInterface::ShouldDelayOnTabletPhysicalStateChanged() {
return false;
}
void AccelerometerProviderInterface::SetECLidAngleDriverStatus(
ECLidAngleDriverStatus status) {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK_NE(status, ECLidAngleDriverStatus::UNKNOWN);
if (status == ec_lid_angle_driver_status_)
return;
ec_lid_angle_driver_status_ = status;
for (auto& observer : observers_) {
observer.OnECLidAngleDriverStatusChanged(ec_lid_angle_driver_status_ ==
ECLidAngleDriverStatus::SUPPORTED);
}
}
ECLidAngleDriverStatus
AccelerometerProviderInterface::GetECLidAngleDriverStatus() const {
DCHECK(base::CurrentUIThread::IsSet());
return ec_lid_angle_driver_status_;
}
void AccelerometerProviderInterface::SetECLidAngleDriverStatusForTesting(
ECLidAngleDriverStatus ec_lid_angle_driver_status) {
ec_lid_angle_driver_status_ = ec_lid_angle_driver_status;
void AccelerometerProviderInterface::NotifyAccelerometerUpdated(
const AccelerometerUpdate& update) {
DCHECK(base::CurrentUIThread::IsSet());
DCHECK_NE(ec_lid_angle_driver_status_, ECLidAngleDriverStatus::UNKNOWN);
if (!emit_events_)
return;
for (auto& observer : observers_)
observer.OnAccelerometerUpdated(update);
}
} // namespace ash
......@@ -7,8 +7,10 @@
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/ash_export.h"
#include "ash/public/cpp/tablet_mode_observer.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
namespace base {
template <typename T>
......@@ -39,6 +41,11 @@ class ASH_EXPORT AccelerometerReader {
// An interface to receive data from the AccelerometerReader.
class Observer {
public:
// Called only once, when
// |AcceleromterProviderInterface::ec_lid_angle_driver_status_| is set to
// either SUPPORTED or NOT_SUPPORTED.
// It's also guaranteed to be called before |OnAccelerometerUpdated|.
virtual void OnECLidAngleDriverStatusChanged(bool is_supported) = 0;
virtual void OnAccelerometerUpdated(const AccelerometerUpdate& update) = 0;
protected:
......@@ -62,9 +69,6 @@ class ASH_EXPORT AccelerometerReader {
// be able to control the accelerometer feature.
void SetEnabled(bool enabled);
// Return the state of the driver being supported or not.
ECLidAngleDriverStatus GetECLidAngleDriverStatus() const;
void SetECLidAngleDriverStatusForTesting(
ECLidAngleDriverStatus ec_lid_angle_driver_status);
......@@ -83,41 +87,71 @@ class ASH_EXPORT AccelerometerReader {
scoped_refptr<AccelerometerProviderInterface> accelerometer_provider_;
};
class AccelerometerProviderInterface
: public base::RefCountedThreadSafe<AccelerometerProviderInterface> {
class ASH_EXPORT AccelerometerProviderInterface
: public base::RefCountedThreadSafe<AccelerometerProviderInterface>,
public TabletModeObserver {
public:
// Prepare and start async initialization.
virtual void PrepareAndInitialize() = 0;
// 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
// mode), cancelled otherwise.
virtual void TriggerRead() = 0;
virtual void CancelRead() = 0;
// TabletModeObserver:
void OnTabletPhysicalStateChanged() override;
// Add/Remove observers.
virtual void AddObserver(AccelerometerReader::Observer* observer) = 0;
virtual void RemoveObserver(AccelerometerReader::Observer* observer) = 0;
void AddObserver(AccelerometerReader::Observer* observer);
void RemoveObserver(AccelerometerReader::Observer* observer);
// Start/Stop listening to tablet mode controller.
virtual void StartListenToTabletModeController() = 0;
virtual void StopListenToTabletModeController() = 0;
void StartListenToTabletModeController();
void StopListenToTabletModeController();
// Set emitting events (samples) to observers or not.
virtual void SetEmitEvents(bool emit_events) = 0;
void SetEmitEvents(bool emit_events);
void SetECLidAngleDriverStatusForTesting(ECLidAngleDriverStatus status);
// Return the state of the driver being supported or not.
protected:
AccelerometerProviderInterface();
~AccelerometerProviderInterface() override;
// Used in |OnTabletPhysicalStateChanged()|. As there might be
// initialization steps, each implementation can override this function to
// determine if this class is ready to process the state changed.
// If returns true, |OnTabletPhysicalStateChanged()| will be skipped, and it's
// the implementation's responsibility to call it again when the class is
// ready. If returns false, |OnTabletPhysicalStateChanged()| will be processed
// as usual.
// Default to return false.
virtual bool ShouldDelayOnTabletPhysicalStateChanged();
void SetECLidAngleDriverStatus(ECLidAngleDriverStatus status);
ECLidAngleDriverStatus GetECLidAngleDriverStatus() const;
void SetECLidAngleDriverStatusForTesting(
ECLidAngleDriverStatus ec_lid_angle_driver_status);
void NotifyAccelerometerUpdated(const AccelerometerUpdate& update);
protected:
virtual ~AccelerometerProviderInterface() = default;
// Set in the constructor.
scoped_refptr<base::SequencedTaskRunner> ui_task_runner_;
// The current initialization state of reader.
State initialization_state_ = State::INITIALIZING;
private:
// State of ChromeOS EC lid angle driver, if SUPPORTED, it means EC can handle
// lid angle calculation.
ECLidAngleDriverStatus ec_lid_angle_driver_status_ =
ECLidAngleDriverStatus::UNKNOWN;
private:
bool emit_events_ = true;
// The observers to notify of accelerometer updates.
// Bound to the UI thread.
base::ObserverList<AccelerometerReader::Observer>::Unchecked observers_;
friend class base::RefCountedThreadSafe<AccelerometerProviderInterface>;
};
......
......@@ -152,6 +152,7 @@ class ASH_EXPORT ScreenOrientationController
void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
// AccelerometerReader::Observer:
void OnECLidAngleDriverStatusChanged(bool is_supported) override {}
void OnAccelerometerUpdated(const AccelerometerUpdate& update) override;
// WindowTreeHostManager::Observer:
......
......@@ -138,6 +138,7 @@ class ASH_EXPORT PowerButtonController
// TODO(minch): Remove this if/when all applicable devices expose a tablet
// mode switch: https://crbug.com/798646.
// AccelerometerReader::Observer:
void OnECLidAngleDriverStatusChanged(bool is_supported) override {}
void OnAccelerometerUpdated(const AccelerometerUpdate& update) override;
// BacklightsForcedOffSetter::Observer:
......
......@@ -627,27 +627,27 @@ void TabletModeController::OnChromeTerminating() {
}
}
void TabletModeController::OnAccelerometerUpdated(
const AccelerometerUpdate& update) {
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::UNKNOWN) {
ec_lid_angle_driver_status_ =
AccelerometerReader::GetInstance()->GetECLidAngleDriverStatus();
}
void TabletModeController::OnECLidAngleDriverStatusChanged(bool is_supported) {
is_ec_lid_angle_driver_supported_ = is_supported;
// When ChromeOS EC lid angle driver is present, EC can handle lid angle
// calculation, thus Chrome side lid angle calculation is disabled. In this
// case, TabletModeController no longer listens to accelerometer events.
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
// Reset lid angle that might be calculated before lid angle driver is
// read.
lid_angle_ = 0.f;
can_detect_lid_angle_ = false;
if (record_lid_angle_timer_.IsRunning())
record_lid_angle_timer_.Stop();
AccelerometerReader::GetInstance()->RemoveObserver(this);
if (!is_supported)
return;
}
// When ChromeOS EC lid angle driver is supported, EC can handle lid angle
// calculation, thus Chrome side lid angle calculation is disabled. In this
// case, TabletModeController no longer listens to accelerometer samples.
// Reset lid angle that might be calculated before lid angle driver is
// read.
lid_angle_ = 0.f;
can_detect_lid_angle_ = false;
if (record_lid_angle_timer_.IsRunning())
record_lid_angle_timer_.Stop();
AccelerometerReader::GetInstance()->RemoveObserver(this);
}
void TabletModeController::OnAccelerometerUpdated(
const AccelerometerUpdate& update) {
have_seen_accelerometer_data_ = true;
can_detect_lid_angle_ = update.has(ACCELEROMETER_SOURCE_SCREEN) &&
update.has(ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD);
......@@ -1300,7 +1300,7 @@ bool TabletModeController::ShouldUiBeInTabletMode() const {
const bool can_enter_tablet_mode =
IsBoardTypeMarkedAsTabletCapable() && HasActiveInternalDisplay() &&
(ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED ||
(is_ec_lid_angle_driver_supported_.value_or(false) ||
have_seen_accelerometer_data_);
return !has_internal_pointing_device_ && can_enter_tablet_mode &&
......
......@@ -147,6 +147,7 @@ class ASH_EXPORT TabletModeController
void OnChromeTerminating() override;
// AccelerometerReader::Observer:
void OnECLidAngleDriverStatusChanged(bool is_supported) override;
void OnAccelerometerUpdated(const AccelerometerUpdate& update) override;
// chromeos::PowerManagerClient::Observer:
......@@ -351,15 +352,17 @@ class ASH_EXPORT TabletModeController
// internal keyboard and touchpad.
std::unique_ptr<InternalInputDevicesEventBlocker> event_blocker_;
// Whether we have ever seen accelerometer data. When ChromeOS EC lid angle is
// present, convertible device cannot see accelerometer data.
// Whether we have ever seen accelerometer data. When ChromeOS EC lid angle
// driver is supported, convertible device cannot see accelerometer data.
bool have_seen_accelerometer_data_ = false;
// If ECLidAngleDriverStatus is supported, Chrome does not calculate lid angle
// itself, but will reply on the tablet-mode flag that EC sends to decide if
// itself, but will rely on the tablet-mode flag that EC sends to decide if
// the device should in tablet mode.
ECLidAngleDriverStatus ec_lid_angle_driver_status_ =
ECLidAngleDriverStatus::UNKNOWN;
// As it's set in |OnECLidAngleDriverStatusChanged|, which is a callback by
// AccelerometerReader, we make it optional to indicate a lack of value until
// the accelerometer reader is initialized.
base::Optional<bool> is_ec_lid_angle_driver_supported_;
// Whether the lid angle can be detected by browser. If it's true, the device
// is a convertible device (both screen acclerometer and keyboard acclerometer
......
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