chromium/ash/sensor_info/sensor_provider.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_SENSOR_INFO_SENSOR_PROVIDER_H_
#define ASH_SENSOR_INFO_SENSOR_PROVIDER_H_

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "ash/accelerometer/accel_gyro_samples_observer.h"
#include "ash/accelerometer/accelerometer_constants.h"
#include "ash/ash_export.h"
#include "ash/sensor_info/sensor_types.h"
#include "base/gtest_prod_util.h"
#include "base/observer_list.h"
#include "chromeos/components/sensors/mojom/cros_sensor_service.mojom.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace ash {

class SensorProviderTest;

// SensorProvider communicates with IIO Service, and provide information from
// three types of sensors: accelerometers, gyroscopes, lid_angle sensors.
// SensorProvider determines sensors' configuration, and waits for the
// sensors' samples. Upon receiving a sample, it will notify all
// observers.
class ASH_EXPORT SensorProvider
    : public chromeos::sensors::mojom::SensorHalClient,
      public chromeos::sensors::mojom::SensorServiceNewDevicesObserver {
 public:
  // Class tracking existence of sensors.
  class DeviceState {
   public:
    DeviceState();
    ~DeviceState();

    DeviceState(const DeviceState&) = delete;
    DeviceState& operator=(const DeviceState&) = delete;

    // Returns true if all kinds of sensors available.
    bool AllSensorsFound() const;

    void Reset();

    // Returns true if all present sensors have update.
    bool CompareUpdate(SensorUpdate update) const;

    // Adds a type of sensor.
    void AddSource(SensorType source);

    // Removes a type of sensor.
    void RemoveSource(SensorType source);

    // Gets the existence of `source`.
    bool GetSource(SensorType source) const;

    // Gets DeviceState.present_ while testing.
    std::vector<bool> GetStatesForTesting() const;

   private:
    // Vector storing the existence of types of sensors.
    std::vector<bool> present_;
  };

  // Struct tracking all sensor info.
  struct SensorData {
    SensorData();
    ~SensorData();

    // Sensor marked ignored will not be used anymore.
    bool ignored = false;
    mojo::Remote<chromeos::sensors::mojom::SensorDevice> remote;
    std::optional<SensorLocation> location;
    // Data scale of this sensor (Used for initializing Samples_Observer,
    // range:(0, 10)). Will be 1.0 for most of the cases.
    std::optional<float> scale;

    // SamplesObserver of this sensor.
    std::unique_ptr<ash::AccelGyroSamplesObserver> samples_observer;
  };

  SensorProvider();

  SensorProvider(const SensorProvider&) = delete;
  SensorProvider& operator=(const SensorProvider&) = delete;

  ~SensorProvider() override;

  // chromeos::sensors::mojom::SensorHalClient:
  void SetUpChannel(mojo::PendingRemote<chromeos::sensors::mojom::SensorService>
                        pending_remote) override;

  // chromeos::sensors::mojom::SensorServiceNewDevicesObserver:
  void OnNewDeviceAdded(
      int32_t iio_device_id,
      const std::vector<chromeos::sensors::mojom::DeviceType>& types) override;

  // Adds/Removes SensorObservers.
  void AddObserver(SensorObserver* observer);
  void RemoveObserver(SensorObserver* observer);

  // Starts/Stops sensor reading.
  // Changes 'sensor_read_on_' and call EnableSensorReadingInternal.
  void EnableSensorReading();
  void StopSensorReading();

  // Gets DeviceState.state while testing.
  std::vector<bool> GetStateForTesting();

 private:
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, CheckNoScale);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, CheckNoLocation);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, GetSamplesOfLidAccel);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, GetSamplesOfLidAngleAndLidAccel);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, GetSamplesOfBaseGyroscope);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest,
                           GetSamplesOfBaseGyroscopeAndBaseAccel);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, AddSensorsWhileSampling);
  FRIEND_TEST_ALL_PREFIXES(SensorProviderTest, GetSamplesOfAccelGyroDevices);

  // Gets all existing sensor devices then initialize sensor devices.
  void QueryDevices();

  // Callback of GetDeviceIds(DeviceType::ANGL), containing the lid_angle
  // device's id if it exists.
  void OnLidAngleIds(const std::vector<int32_t>& lid_angle_ids);

  // Callback of GetDeviceIds(DeviceType::ACCEL), containing all iio_device_ids
  // of accelerometers.
  void OnAccelerometerIds(const std::vector<int32_t>& accelerometer_ids);

  // Callback of GetDeviceIds(DeviceType::ANGLVEL), containing all
  // iio_device_ids of gyroscopes.
  void OnGyroscopeIds(const std::vector<int32_t>& gyroscope_ids);

  // Registers `chromeos::sensors::mojom::SensorHalClient` to Sensor Hal
  // Dispatcher, waiting for the Mojo connection to IIO Service.
  void RegisterSensorClient();
  void OnSensorServiceDisconnect();
  void OnSensorHalClientFailure();
  void ResetSensorService();
  void ResetStates();

  // Waits for new devices from IIO Service.
  void SetNewDevicesObserver();
  void OnNewDevicesObserverDisconnect();

  // Creates the Mojo channel for the sensor, and requests the
  // sensor's location and scale attributes before creating the
  // SensorSamplesObserver of it.
  void RegisterSensor(chromeos::sensors::mojom::DeviceType device_type,
                      int32_t id);

  // Ignores the sensor with id, device_type as the attributes are not expected.
  void IgnoreSensor(chromeos::sensors::mojom::DeviceType device_type,
                    int32_t id);

  // Enables all SamplesObservers and sets frequency for SamplesObservers.
  void EnableSensorReadingInternal();

  void OnSensorRemoteDisconnect(
      chromeos::sensors::mojom::DeviceType device_type,
      int32_t id,
      uint32_t custom_reason_code,
      const std::string& description);

  // Callback for getting lid angle value.
  void OnLidAngleValue(const std::vector<std::optional<std::string>>& values);

  // Callback for getting sensor's location and scale attributes.
  void OnAttributes(chromeos::sensors::mojom::DeviceType device_type,
                    int32_t id,
                    const std::vector<std::optional<std::string>>& values);

  // Creates the SamplesObserver for the sensor with 'id'.
  void CreateSensorSamplesObserver(
      chromeos::sensors::mojom::DeviceType device_type,
      int32_t id);

  // Called by `SensorData::samples_observer` stored in the
  // `sensors_` map, containing a sample of the sensor.
  void OnSampleUpdatedCallback(chromeos::sensors::mojom::DeviceType device_type,
                               int iio_device_id,
                               std::vector<float> sample);

  // Notifies external observer updates.
  void NotifySensorUpdated(SensorUpdate update);

  // Returns true if all stored SensorData (excluding SensorData for lid_angle
  // and SensorData for ignored devices) have a SamplesObserver.
  bool CheckSensorSamplesObserver();

  // Calls GetChannelsAttributesCallback to get lid_angle update.
  void GetLidAngleUpdate();

  // Stores <SensorType, device_id> pairs.
  std::map<SensorType, int32_t> type_to_sensor_id_;

  // Map storing <device_id, <chromeos::sensors::mojom::DeviceType,
  // SensorData>>.
  std::map<int32_t, std::map<chromeos::sensors::mojom::DeviceType, SensorData>>
      sensors_;

  DeviceState current_state_;

  // The Mojo channel connecting to Sensor Hal Dispatcher. SensorHalDispatcher
  // will receive registrations of SensorService.
  mojo::Receiver<chromeos::sensors::mojom::SensorHalClient> sensor_hal_client_{
      this};

  // The Mojo channel to query and request for devices.
  mojo::Remote<chromeos::sensors::mojom::SensorService> sensor_service_remote_;

  // The Mojo channel to get notified when new devices are added to IIO Service.
  mojo::Receiver<chromeos::sensors::mojom::SensorServiceNewDevicesObserver>
      new_devices_observer_{this};

  // The last seen sensor data.
  SensorUpdate update_;

  // Whether send updates to external observers. Value is only changed by
  // external calls of EnableSensorReading() and StopSensorReading().
  bool sensor_read_on_ = false;

  // List of all external observers.
  base::ObserverList<SensorObserver> observers_;

  base::WeakPtrFactory<SensorProvider> weak_ptr_factory_{this};
};

}  // namespace ash
#endif  // ASH_SENSOR_INFO_SENSOR_PROVIDER_H_