| // 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/ranges/algorithm.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/time/time.h" |
| #include "chromeos/components/sensors/ash/sensor_hal_dispatcher.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Delay of the reconnection to Sensor Hal Dispatcher. |
| constexpr base::TimeDelta kDelayReconnect = |
| base::TimeDelta::FromMilliseconds(1000); |
| |
| // Timeout for the late-present devices: 10 seconds. |
| constexpr base::TimeDelta kNewDevicesTimeout = |
| base::TimeDelta::FromMilliseconds(10000); |
| |
| } // namespace |
| |
| AccelerometerProviderMojo::AccelerometerProviderMojo() = default; |
| |
| void AccelerometerProviderMojo::PrepareAndInitialize() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| RegisterSensorClient(); |
| } |
| |
| void AccelerometerProviderMojo::TriggerRead() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::SUPPORTED) |
| EnableAccelerometerReading(); |
| } |
| |
| void AccelerometerProviderMojo::CancelRead() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::SUPPORTED) |
| DisableAccelerometerReading(); |
| } |
| |
| 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)); |
| sensor_service_remote_.set_disconnect_handler(base::BindOnce( |
| &AccelerometerProviderMojo::OnSensorServiceDisconnect, this)); |
| SetNewDevicesObserver(); |
| |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::UNKNOWN) { |
| sensor_service_remote_->GetDeviceIds( |
| chromeos::sensors::mojom::DeviceType::ANGL, |
| base::BindOnce(&AccelerometerProviderMojo::GetLidAngleIdsCallback, |
| this)); |
| } |
| |
| sensor_service_remote_->GetDeviceIds( |
| chromeos::sensors::mojom::DeviceType::ACCEL, |
| base::BindOnce(&AccelerometerProviderMojo::GetAccelerometerIdsCallback, |
| this)); |
| } |
| |
| void AccelerometerProviderMojo::OnNewDeviceAdded( |
| int32_t iio_device_id, |
| const std::vector<chromeos::sensors::mojom::DeviceType>& types) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_NE(initialization_state_, MojoState::ANGL_LID); |
| |
| for (const auto& type : types) { |
| if (type == chromeos::sensors::mojom::DeviceType::ACCEL) { |
| if (initialization_state_ == MojoState::LID_BASE) { |
| // Don't need a new accelerometer. |
| continue; |
| } |
| |
| if (accelerometers_.find(iio_device_id) != accelerometers_.end()) |
| continue; |
| |
| RegisterAccelerometerWithId(iio_device_id); |
| } else if (type == chromeos::sensors::mojom::DeviceType::ANGL) { |
| SetECLidAngleDriverSupported(); |
| } |
| } |
| } |
| |
| MojoState AccelerometerProviderMojo::GetInitializationStateForTesting() const { |
| return initialization_state_; |
| } |
| |
| bool AccelerometerProviderMojo::ShouldDelayOnTabletPhysicalStateChanged() { |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::UNKNOWN) { |
| pending_on_tablet_physical_state_changed_ = true; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| AccelerometerProviderMojo::AccelerometerData::AccelerometerData() = default; |
| AccelerometerProviderMojo::AccelerometerData::~AccelerometerData() = default; |
| |
| AccelerometerProviderMojo::~AccelerometerProviderMojo() = default; |
| |
| void AccelerometerProviderMojo::RegisterSensorClient() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto* dispatcher = chromeos::sensors::SensorHalDispatcher::GetInstance(); |
| if (!dispatcher) { |
| // In unit tests, SensorHalDispatcher is not initialized. |
| return; |
| } |
| |
| dispatcher->RegisterClient(sensor_hal_client_.BindNewPipeAndPassRemote()); |
| |
| sensor_hal_client_.set_disconnect_handler(base::BindOnce( |
| &AccelerometerProviderMojo::OnSensorHalClientFailure, this)); |
| } |
| |
| void AccelerometerProviderMojo::OnSensorHalClientFailure() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| LOG(ERROR) << "OnSensorHalClientFailure"; |
| |
| ResetSensorService(); |
| sensor_hal_client_.reset(); |
| |
| ui_task_runner_->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&AccelerometerProviderMojo::RegisterSensorClient, 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(); |
| } |
| |
| new_devices_observer_.reset(); |
| sensor_service_remote_.reset(); |
| } |
| |
| void AccelerometerProviderMojo::SetECLidAngleDriverSupported() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::SUPPORTED) |
| return; |
| |
| DCHECK_NE(initialization_state_, MojoState::ANGL); |
| DCHECK_NE(initialization_state_, MojoState::ANGL_LID); |
| |
| if (GetECLidAngleDriverStatus() == ECLidAngleDriverStatus::NOT_SUPPORTED) { |
| // |GetECLidAngleDriverStatus()| will be set to NOT_SUPPORTED when waiting |
| // for new devices is timed out. However, this function may still be called |
| // after the timeout and when that happens, we'll need to overwrite the |
| // status and revert some changes. |
| LOG(WARNING) << "Overwriting ECLidAngleDriverStatus from NOT_SUPPORTED " |
| "to SUPPORTED"; |
| |
| // Restarts to listen to TabletPhysicalStateChanged from |
| // TabletModeController. Allows the enabled samples when setting |
| // ECLidAngleDriverStatus to SUPPORTED. |
| StartListenToTabletModeController(); |
| } |
| |
| SetECLidAngleDriverStatus(ECLidAngleDriverStatus::SUPPORTED); |
| |
| if (pending_on_tablet_physical_state_changed_) |
| OnTabletPhysicalStateChanged(); |
| |
| UpdateStateWithECLidAngleDriverSupported(); |
| } |
| |
| void AccelerometerProviderMojo::UpdateStateWithECLidAngleDriverSupported() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| switch (initialization_state_) { |
| case MojoState::INITIALIZING: |
| initialization_state_ = MojoState::ANGL; |
| break; |
| |
| case MojoState::BASE: { |
| initialization_state_ = MojoState::ANGL; |
| |
| // Ignores the base-accelerometer as it's no longer needed for the only |
| // use case: calculating the angle between the lid and the base, which is |
| // substituted by the driver. |
| auto it = location_to_accelerometer_id_.find( |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| DCHECK(it != location_to_accelerometer_id_.end()); |
| IgnoreAccelerometer(it->second); |
| break; |
| } |
| |
| case MojoState::LID: |
| initialization_state_ = MojoState::ANGL_LID; |
| break; |
| |
| case MojoState::LID_BASE: { |
| initialization_state_ = MojoState::ANGL_LID; |
| |
| // Ignores the base-accelerometer as it's no longer needed for the only |
| // use case: calculating the angle between the lid and the base, which is |
| // substituted by the driver. |
| auto it = location_to_accelerometer_id_.find( |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| DCHECK(it != location_to_accelerometer_id_.end()); |
| IgnoreAccelerometer(it->second); |
| break; |
| } |
| |
| default: |
| LOG(FATAL) << "Unexpected state: " |
| << static_cast<int32_t>(initialization_state_); |
| break; |
| } |
| |
| if (initialization_state_ == MojoState::ANGL_LID) |
| new_devices_observer_.reset(); |
| } |
| |
| void AccelerometerProviderMojo::UpdateStateWithLidAccelerometer() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| switch (initialization_state_) { |
| case MojoState::INITIALIZING: |
| initialization_state_ = MojoState::LID; |
| break; |
| |
| case MojoState::BASE: { |
| initialization_state_ = MojoState::LID_BASE; |
| |
| auto it = location_to_accelerometer_id_.find( |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| DCHECK(it != location_to_accelerometer_id_.end()); |
| if (accelerometers_[it->second].samples_observer.get()) |
| accelerometers_[it->second].samples_observer->SetEnabled(true); |
| |
| break; |
| } |
| |
| case MojoState::ANGL: |
| initialization_state_ = MojoState::ANGL_LID; |
| new_devices_observer_.reset(); |
| break; |
| |
| default: |
| LOG(FATAL) << "Unexpected state: " |
| << static_cast<int32_t>(initialization_state_); |
| break; |
| } |
| } |
| |
| void AccelerometerProviderMojo::UpdateStateWithBaseAccelerometer() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| switch (initialization_state_) { |
| case MojoState::INITIALIZING: |
| initialization_state_ = MojoState::BASE; |
| break; |
| |
| case MojoState::LID: |
| initialization_state_ = MojoState::LID_BASE; |
| break; |
| |
| case MojoState::ANGL: |
| case MojoState::ANGL_LID: { |
| // Ignores the base-accelerometer as it's no longer needed for the only |
| // use case: calculating the angle between the lid and the base, which is |
| // substituted by the driver. |
| auto it = location_to_accelerometer_id_.find( |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| DCHECK(it != location_to_accelerometer_id_.end()); |
| IgnoreAccelerometer(it->second); |
| break; |
| } |
| |
| default: |
| LOG(FATAL) << "Unexpected state: " |
| << static_cast<int32_t>(initialization_state_); |
| break; |
| } |
| } |
| |
| void AccelerometerProviderMojo::SetNewDevicesObserver() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(sensor_service_remote_.is_bound()); |
| DCHECK(!new_devices_observer_.is_bound()); |
| |
| if (initialization_state_ == MojoState::ANGL_LID) { |
| // Don't need any further devices. |
| return; |
| } |
| |
| sensor_service_remote_->RegisterNewDevicesObserver( |
| new_devices_observer_.BindNewPipeAndPassRemote()); |
| new_devices_observer_.set_disconnect_handler(base::BindOnce( |
| &AccelerometerProviderMojo::OnNewDevicesObserverDisconnect, this)); |
| |
| ui_task_runner_->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&AccelerometerProviderMojo::OnNewDevicesTimeout, this), |
| kNewDevicesTimeout); |
| } |
| |
| void AccelerometerProviderMojo::AccelerometerProviderMojo:: |
| OnNewDevicesObserverDisconnect() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| LOG(ERROR) |
| << "OnNewDevicesObserverDisconnect, resetting SensorService as IIO " |
| "Service should be destructed and waiting for the relaunch of it."; |
| ResetSensorService(); |
| } |
| |
| void AccelerometerProviderMojo::OnNewDevicesTimeout() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!sensor_service_remote_.is_bound()) { |
| // Skips and waits for the next timeout for the case that IIO Service |
| // disconnects after the first connection. |
| return; |
| } |
| |
| if (GetECLidAngleDriverStatus() != ECLidAngleDriverStatus::UNKNOWN) |
| return; |
| |
| if (initialization_state_ == MojoState::INITIALIZING || |
| initialization_state_ == MojoState::BASE || |
| initialization_state_ == MojoState::ANGL) { |
| LOG(ERROR) << "Unfinished initialization after timeout: " |
| << static_cast<int32_t>(initialization_state_); |
| } |
| |
| SetECLidAngleDriverStatus(ECLidAngleDriverStatus::NOT_SUPPORTED); |
| EnableAccelerometerReading(); |
| |
| if (pending_on_tablet_physical_state_changed_) |
| OnTabletPhysicalStateChanged(); |
| } |
| |
| void AccelerometerProviderMojo::GetLidAngleIdsCallback( |
| const std::vector<int32_t>& lid_angle_ids) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (!lid_angle_ids.empty()) |
| SetECLidAngleDriverSupported(); |
| } |
| |
| void AccelerometerProviderMojo::GetAccelerometerIdsCallback( |
| const std::vector<int32_t>& accelerometer_ids) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| 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, 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()) { |
| // Create the observer directly if the attributes have already been |
| // retrieved. |
| CreateAccelerometerSamplesObserver(id); |
| |
| return; |
| } |
| |
| if (initialization_state_ == MojoState::ANGL_LID || |
| initialization_state_ == MojoState::LID_BASE) { |
| // No need of new accelerometers. |
| return; |
| } |
| |
| accelerometer.remote->GetAttributes( |
| attr_names, |
| base::BindOnce(&AccelerometerProviderMojo::GetAttributesCallback, this, |
| id)); |
| } |
| |
| void AccelerometerProviderMojo::OnAccelerometerRemoteDisconnect(int32_t id) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| LOG(ERROR) |
| << "OnAccelerometerRemoteDisconnect: " << id |
| << ", resetting SensorService as IIO Service should be destructed and " |
| "waiting for the relaunch of it."; |
| ResetSensorService(); |
| } |
| |
| void AccelerometerProviderMojo::GetAttributesCallback( |
| int32_t id, |
| const std::vector<absl::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(WARNING) << "Duplicated location source " << source |
| << " of accel id: " << id << ", and accel id: " |
| << location_to_accelerometer_id_[source]; |
| IgnoreAccelerometer(id); |
| 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; |
| } |
| |
| if (accelerometer.location == ACCELEROMETER_SOURCE_SCREEN) { |
| UpdateStateWithLidAccelerometer(); |
| } else { |
| DCHECK_EQ(accelerometer.location.value(), |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| UpdateStateWithBaseAccelerometer(); |
| } |
| |
| if (accelerometer.ignored) { |
| // base-accelerometer is not needed if EC Lid Angle Driver is supported. |
| return; |
| } |
| |
| 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(); |
| accelerometer.samples_observer.reset(); |
| } |
| |
| 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 && |
| GetECLidAngleDriverStatus() == 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, this)); |
| |
| if (initialization_state_ == MojoState::BASE) { |
| DCHECK_EQ(accelerometer.location.value(), |
| ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD); |
| // Don't need base-accelerometer's samples without lid-accelerometer. |
| return; |
| } |
| |
| if (accelerometer_read_on_) |
| accelerometer.samples_observer->SetEnabled(true); |
| } |
| |
| void AccelerometerProviderMojo::EnableAccelerometerReading() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK_NE(GetECLidAngleDriverStatus(), ECLidAngleDriverStatus::UNKNOWN); |
| if (accelerometer_read_on_) |
| return; |
| |
| accelerometer_read_on_ = true; |
| |
| if (initialization_state_ == MojoState::BASE) { |
| // Don't need base-accelerometer's samples without lid-accelerometer. |
| return; |
| } |
| |
| 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(GetECLidAngleDriverStatus(), ECLidAngleDriverStatus::SUPPORTED); |
| if (!accelerometer_read_on_) |
| return; |
| |
| accelerometer_read_on_ = false; |
| |
| 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()); |
| DCHECK(accelerometer.location == ACCELEROMETER_SOURCE_SCREEN || |
| initialization_state_ == MojoState::LID_BASE); |
| |
| if (!accelerometer_read_on_) { |
| // This sample is not needed. |
| return; |
| } |
| |
| update_.Set(accelerometers_[iio_device_id].location.value(), sample[0], |
| sample[1], sample[2]); |
| |
| if (initialization_state_ == MojoState::LID_BASE && |
| (!update_.has(ACCELEROMETER_SOURCE_SCREEN) || |
| !update_.has(ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD))) { |
| // Wait for the other accelerometer to be updated. |
| return; |
| } |
| |
| NotifyAccelerometerUpdated(update_); |
| update_.Reset(); |
| } |
| |
| } // namespace ash |