text: // Copyright 2014 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_reader.h"
  
  #include "ash/shell.h"
  #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  #include "base/no_destructor.h"
  #include "base/sequenced_task_runner.h"
  #include "base/task/current_thread.h"
  #include "base/threading/sequenced_task_runner_handle.h"
  #include "chromeos/components/sensors/buildflags.h"
  #if BUILDFLAG(USE_IIOSERVICE)
  #include "ash/accelerometer/accelerometer_provider_mojo.h"
  #else  // !BUILDFLAG(USE_IIOSERVICE)
  #include "ash/accelerometer/accelerometer_file_reader.h"
  #endif  // BUILDFLAG(USE_IIOSERVICE)
  
  namespace ash {
  
  // static
  AccelerometerReader* AccelerometerReader::GetInstance() {
    static base::NoDestructor<AccelerometerReader> accelerometer_reader;
    return accelerometer_reader.get();
  }
  
  void AccelerometerReader::Initialize() {
    accelerometer_provider_->PrepareAndInitialize();
  }
  
  void AccelerometerReader::AddObserver(Observer* observer) {
    accelerometer_provider_->AddObserver(observer);
  }
  
  void AccelerometerReader::RemoveObserver(Observer* observer) {
    accelerometer_provider_->RemoveObserver(observer);
  }
  
  void AccelerometerReader::StartListenToTabletModeController() {
    accelerometer_provider_->StartListenToTabletModeController();
  }
  
  void AccelerometerReader::StopListenToTabletModeController() {
    accelerometer_provider_->StopListenToTabletModeController();
  }
  
  void AccelerometerReader::SetEnabled(bool enabled) {
    accelerometer_provider_->SetEmitEvents(enabled);
  }
  
  void AccelerometerReader::SetECLidAngleDriverStatusForTesting(
      ECLidAngleDriverStatus ec_lid_angle_driver_status) {
    accelerometer_provider_->SetECLidAngleDriverStatusForTesting(  // IN-TEST
        ec_lid_angle_driver_status);
  }
  
  AccelerometerReader::AccelerometerReader() {
  #if BUILDFLAG(USE_IIOSERVICE)
    accelerometer_provider_ = new AccelerometerProviderMojo();
  #else   // !BUILDFLAG(USE_IIOSERVICE)
    accelerometer_provider_ = new AccelerometerFileReader();
  #endif  // BUILDFLAG(USE_IIOSERVICE)
  }
  
  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::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
  