blob: 4b621361068a6876a736cf529d0f138ad8434f80 [file] [log] [blame]
// Copyright 2020 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 "ash/accelerometer/accelerometer_provider_mojo.h"
#include <iterator>
#include <utility>
#include "ash/accelerometer/accelerometer_constants.h"
#include "ash/shell.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "base/bind.h"
#include "base/observer_list_threadsafe.h"
#include "base/ranges/algorithm.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/time/time.h"
#include "chromeos/components/sensors/sensor_hal_dispatcher.h"
namespace ash {
namespace {
// Delay of the reconnection to Sensor Hal Dispatcher.
constexpr base::TimeDelta kDelayReconnect =
base::TimeDelta::FromMilliseconds(1000);
} // namespace
AccelerometerProviderMojo::AccelerometerProviderMojo()
: sensor_hal_client_(this),
observers_(
new base::ObserverListThreadSafe<AccelerometerReader::Observer>()),
update_(new AccelerometerUpdate()) {}
void AccelerometerProviderMojo::PrepareAndInitialize() {
// This function should only be called once.
DCHECK(!task_runner_);
task_runner_ = base::SequencedTaskRunnerHandle::Get();
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AccelerometerProviderMojo::RegisterSensorClient,
base::Unretained(this)));
}
void AccelerometerProviderMojo::AddObserver(
AccelerometerReader::Observer* observer) {
DCHECK(task_runner_);
// Do not move this into AddObserverOnThread, as the current task runner will
// be used in ObserverListThreadSafe::Notify.
observers_->AddObserver(observer);
task_runner_->PostNonNestableTask(
FROM_HERE, base::BindOnce(&AccelerometerProviderMojo::AddObserverOnThread,
base::Unretained(this), observer));
}
void AccelerometerProviderMojo::RemoveObserver(
AccelerometerReader::Observer* observer) {
DCHECK(task_runner_);
observers_->RemoveObserver(observer);
}
void AccelerometerProviderMojo::StartListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->AddObserver(this);
}
void AccelerometerProviderMojo::StopListenToTabletModeController() {
Shell::Get()->tablet_mode_controller()->RemoveObserver(this);
}
void AccelerometerProviderMojo::SetEmitEvents(bool emit_events) {
task_runner_->PostNonNestableTask(
FROM_HERE,
base::BindOnce(&AccelerometerProviderMojo::SetEmitEventsOnThread, this,
emit_events));
}
void AccelerometerProviderMojo::OnTabletPhysicalStateChanged() {
// Wait until the existence of the driver is determined.
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::UNKNOWN) {
pending_on_tablet_physical_state_changed_ = true;
return;
}
// When CrOS EC lid angle driver is not present, accelerometer read is always
// ON and can't be tuned. Thus AccelerometerProviderMojo 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();
task_runner_->PostNonNestableTask(
FROM_HERE,
is_auto_rotation_on
? base::BindOnce(&AccelerometerProviderMojo::TriggerRead, this)
: base::BindOnce(&AccelerometerProviderMojo::CancelRead, this));
}
void AccelerometerProviderMojo::SetUpChannel(
mojo::PendingRemote<chromeos::sensors::mojom::SensorService>
pending_remote) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (sensor_service_remote_.is_bound()) {
LOG(ERROR) << "Ignoring the second Remote<SensorService>";
return;
}
sensor_service_remote_.Bind(std::move(pending_remote), task_runner_);
sensor_service_remote_.set_disconnect_handler(
base::BindOnce(&AccelerometerProviderMojo::OnSensorServiceDisconnect,
base::Unretained(this)));
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::UNKNOWN) {
sensor_service_remote_->GetDeviceIds(
chromeos::sensors::mojom::DeviceType::ANGL,
base::BindOnce(&AccelerometerProviderMojo::GetLidAngleIdsCallback,
base::Unretained(this)));
}
sensor_service_remote_->GetDeviceIds(
chromeos::sensors::mojom::DeviceType::ACCEL,
base::BindOnce(&AccelerometerProviderMojo::GetAccelerometerIdsCallback,
base::Unretained(this)));
}
void AccelerometerProviderMojo::TriggerRead() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED)
EnableAccelerometerReading();
}
void AccelerometerProviderMojo::CancelRead() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED)
DisableAccelerometerReading();
}
State AccelerometerProviderMojo::GetInitializationStateForTesting() const {
return initialization_state_;
}
AccelerometerProviderMojo::AccelerometerData::AccelerometerData() = default;
AccelerometerProviderMojo::AccelerometerData::~AccelerometerData() = default;
AccelerometerProviderMojo::~AccelerometerProviderMojo() = default;
void AccelerometerProviderMojo::RegisterSensorClient() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
chromeos::sensors::SensorHalDispatcher::GetInstance()->RegisterClient(
sensor_hal_client_.BindNewPipeAndPassRemote());
sensor_hal_client_.set_disconnect_handler(
base::BindOnce(&AccelerometerProviderMojo::OnSensorHalClientFailure,
base::Unretained(this)));
}
void AccelerometerProviderMojo::OnSensorHalClientFailure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "OnSensorHalClientFailure";
ResetSensorService();
sensor_hal_client_.reset();
task_runner_->PostDelayedTask(
FROM_HERE,
base::BindOnce(&AccelerometerProviderMojo::RegisterSensorClient,
base::Unretained(this)),
kDelayReconnect);
}
void AccelerometerProviderMojo::OnSensorServiceDisconnect() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
LOG(ERROR) << "OnSensorServiceDisconnect";
ResetSensorService();
}
void AccelerometerProviderMojo::ResetSensorService() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
for (auto& accelerometer : accelerometers_) {
accelerometer.second.remote.reset();
accelerometer.second.samples_observer.reset();
}
sensor_service_remote_.reset();
}
void AccelerometerProviderMojo::GetLidAngleIdsCallback(
const std::vector<int32_t>& lid_angle_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(ec_lid_angle_driver_status_, ECLidAngleDriverStatus::UNKNOWN);
if (!lid_angle_ids.empty()) {
ec_lid_angle_driver_status_ = ECLidAngleDriverStatus::SUPPORTED;
} else {
ec_lid_angle_driver_status_ = ECLidAngleDriverStatus::NOT_SUPPORTED;
EnableAccelerometerReading();
}
if (pending_on_tablet_physical_state_changed_)
OnTabletPhysicalStateChanged();
}
void AccelerometerProviderMojo::GetAccelerometerIdsCallback(
const std::vector<int32_t>& accelerometer_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (accelerometer_ids.empty()) {
FailedToInitialize();
return;
}
for (int32_t id : accelerometer_ids)
RegisterAccelerometerWithId(id);
}
void AccelerometerProviderMojo::RegisterAccelerometerWithId(int32_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& accelerometer = accelerometers_[id];
if (accelerometer.ignored) {
// Something went wrong in the previous initialization. Ignoring this accel.
return;
}
DCHECK(!accelerometer.remote.is_bound());
DCHECK(!accelerometer.samples_observer.get());
if (!sensor_service_remote_.is_bound()) {
// Something went wrong. Skipping here.
return;
}
accelerometer.remote.reset();
sensor_service_remote_->GetDevice(
id, accelerometer.remote.BindNewPipeAndPassReceiver());
accelerometer.remote.set_disconnect_handler(base::BindOnce(
&AccelerometerProviderMojo::OnAccelerometerRemoteDisconnect,
base::Unretained(this), id));
std::vector<std::string> attr_names;
if (!accelerometer.location.has_value())
attr_names.push_back(chromeos::sensors::mojom::kLocation);
if (!accelerometer.scale.has_value())
attr_names.push_back(chromeos::sensors::mojom::kScale);
if (!attr_names.empty()) {
accelerometer.remote->GetAttributes(
attr_names,
base::BindOnce(&AccelerometerProviderMojo::GetAttributesCallback,
base::Unretained(this), id));
} else {
// Create the observer directly if the attributes have already been
// retrieved.
CreateAccelerometerSamplesObserver(id);
}
}
void AccelerometerProviderMojo::OnAccelerometerRemoteDisconnect(int32_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& accelerometer = accelerometers_[id];
LOG(ERROR) << "Accelerometer with id: " << id << " disconnected";
accelerometer.remote.reset();
accelerometer.samples_observer.reset();
if (!sensor_service_remote_.is_bound())
return;
RegisterAccelerometerWithId(id);
}
void AccelerometerProviderMojo::GetAttributesCallback(
int32_t id,
const std::vector<base::Optional<std::string>>& values) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& accelerometer = accelerometers_[id];
DCHECK(accelerometer.remote.is_bound());
size_t index = 0;
if (!accelerometer.location.has_value()) {
if (index >= values.size()) {
LOG(ERROR) << "values doesn't contain location attribute.";
IgnoreAccelerometer(id);
return;
}
if (!values[index].has_value()) {
LOG(WARNING) << "No location attribute for accel with id: " << id;
IgnoreAccelerometer(id);
return;
}
auto* it = base::ranges::find(kLocationStrings, values[index]);
if (it == std::end(kLocationStrings)) {
LOG(WARNING) << "Unrecognized location: " << values[index].value()
<< " for device with id: ";
IgnoreAccelerometer(id);
return;
}
AccelerometerSource source = static_cast<AccelerometerSource>(
std::distance(std::begin(kLocationStrings), it));
accelerometer.location = source;
if (location_to_accelerometer_id_.find(source) !=
location_to_accelerometer_id_.end()) {
LOG(ERROR) << "Duplicated location source " << source
<< " of accel id: " << id
<< ", and accel id: " << location_to_accelerometer_id_[source];
FailedToInitialize();
return;
}
location_to_accelerometer_id_[source] = id;
++index;
}
if (!accelerometer.scale.has_value()) {
if (index >= values.size()) {
LOG(ERROR) << "values doesn't contain scale attribute.";
IgnoreAccelerometer(id);
return;
}
double scale = 0.0;
if (!values[index].has_value() ||
!base::StringToDouble(values[index].value(), &scale)) {
LOG(ERROR) << "Invalid scale: " << values[index].value_or("")
<< ", for accel with id: " << id;
IgnoreAccelerometer(id);
return;
}
accelerometer.scale = scale;
++index;
}
CheckInitialization();
CreateAccelerometerSamplesObserver(id);
}
void AccelerometerProviderMojo::IgnoreAccelerometer(int32_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& accelerometer = accelerometers_[id];
LOG(WARNING) << "Ignoring accel with id: " << id;
accelerometer.ignored = true;
accelerometer.remote.reset();
CheckInitialization();
}
void AccelerometerProviderMojo::CheckInitialization() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(ec_lid_angle_driver_status_, ECLidAngleDriverStatus::UNKNOWN);
if (initialization_state_ != State::INITIALIZING)
return;
bool has_accelerometer_lid = false;
for (const auto& accelerometer : accelerometers_) {
if (accelerometer.second.ignored) {
if (!accelerometer.second.location.has_value())
continue;
if (accelerometer.second.location == ACCELEROMETER_SOURCE_SCREEN ||
ec_lid_angle_driver_status_ ==
ECLidAngleDriverStatus::NOT_SUPPORTED) {
// This ignored accelerometer is essential.
FailedToInitialize();
return;
}
continue;
}
if (!accelerometer.second.scale.has_value() ||
!accelerometer.second.location.has_value())
return;
if (accelerometer.second.location == ACCELEROMETER_SOURCE_SCREEN)
has_accelerometer_lid = true;
else
has_accelerometer_base_ = true;
}
if (has_accelerometer_lid) {
if (!has_accelerometer_base_) {
LOG(WARNING)
<< "Initialization succeeded without an accelerometer on the base";
}
initialization_state_ = State::SUCCESS;
} else {
FailedToInitialize();
}
}
void AccelerometerProviderMojo::CreateAccelerometerSamplesObserver(int32_t id) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto& accelerometer = accelerometers_[id];
DCHECK(accelerometer.remote.is_bound());
DCHECK(!accelerometer.ignored);
DCHECK(accelerometer.scale.has_value() && accelerometer.location.has_value());
if (accelerometer.location == ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD &&
ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
// Skipping as it's only needed if lid-angle is not supported.
// |GetLidAngleIdsCallback| will call this function with this |id| again if
// it's found not supported.
return;
}
accelerometer.samples_observer =
std::make_unique<AccelerometerSamplesObserver>(
id, std::move(accelerometer.remote), accelerometer.scale.value(),
base::BindRepeating(
&AccelerometerProviderMojo::OnSampleUpdatedCallback,
base::Unretained(this)));
if (accelerometer_read_on_ || one_time_read_)
accelerometer.samples_observer->SetEnabled(true);
}
void AccelerometerProviderMojo::EnableAccelerometerReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_NE(ec_lid_angle_driver_status_, ECLidAngleDriverStatus::UNKNOWN);
if (accelerometer_read_on_)
return;
accelerometer_read_on_ = true;
for (auto& accelerometer : accelerometers_) {
if (!accelerometer.second.samples_observer.get())
continue;
accelerometer.second.samples_observer->SetEnabled(true);
}
}
void AccelerometerProviderMojo::DisableAccelerometerReading() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(ec_lid_angle_driver_status_, ECLidAngleDriverStatus::SUPPORTED);
if (!accelerometer_read_on_)
return;
accelerometer_read_on_ = false;
// Allow one more read and let |OnSampleUpdatedCallback| disable the
// observers.
if (one_time_read_)
return;
for (auto& accelerometer : accelerometers_) {
if (!accelerometer.second.samples_observer.get())
continue;
accelerometer.second.samples_observer->SetEnabled(false);
}
}
void AccelerometerProviderMojo::OnSampleUpdatedCallback(
int iio_device_id,
std::vector<float> sample) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(sample.size(), kNumberOfAxes);
auto& accelerometer = accelerometers_[iio_device_id];
DCHECK(accelerometer.location.has_value());
bool need_two_accelerometers =
(ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::NOT_SUPPORTED &&
has_accelerometer_base_);
if (!one_time_read_ && !accelerometer_read_on_) {
// This sample is not needed.
return;
}
if (!emit_events_)
return;
update_->Set(accelerometers_[iio_device_id].location.value(), sample[0],
sample[1], sample[2]);
if (need_two_accelerometers &&
(!update_->has(ACCELEROMETER_SOURCE_SCREEN) ||
!update_->has(ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD))) {
// Wait for the other accel to be updated.
return;
}
observers_->Notify(FROM_HERE,
&AccelerometerReader::Observer::OnAccelerometerUpdated,
update_);
update_ = new AccelerometerUpdate();
one_time_read_ = false;
if (accelerometer_read_on_)
return;
// This was a one time read. Disable observers.
for (auto& accelerometer : accelerometers_) {
if (!accelerometer.second.samples_observer.get())
continue;
accelerometer.second.samples_observer->SetEnabled(false);
}
}
void AccelerometerProviderMojo::SetEmitEventsOnThread(bool emit_events) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
emit_events_ = emit_events;
}
void AccelerometerProviderMojo::FailedToInitialize() {
DCHECK_NE(initialization_state_, State::SUCCESS);
LOG(ERROR) << "Failed to initialize for accelerometer read.";
initialization_state_ = State::FAILED;
accelerometers_.clear();
ResetSensorService();
sensor_hal_client_.reset();
}
void AccelerometerProviderMojo::AddObserverOnThread(
AccelerometerReader::Observer* observer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
one_time_read_ = true;
if (accelerometer_read_on_)
return;
for (auto& accelerometer : accelerometers_) {
if (!accelerometer.second.samples_observer.get())
continue;
accelerometer.second.samples_observer->SetEnabled(true);
}
}
} // namespace ash