text: // Copyright 2020 The Chromium OS Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #ifndef TYPECD_UDEV_MONITOR_H_
  #define TYPECD_UDEV_MONITOR_H_
  
  #include <libudev.h>
  
  #include <map>
  #include <memory>
  #include <string>
  #include <utility>
  
  #include <base/files/file_descriptor_watcher_posix.h>
  #include <base/files/file_path.h>
  #include <base/observer_list.h>
  #include <base/observer_list_types.h>
  #include <brillo/udev/mock_udev.h>
  #include <gtest/gtest_prod.h>
  
  namespace typecd {
  
  constexpr char kTypeCSubsystem[] = "typec";
  constexpr char kUdevMonitorName[] = "udev";
  
  // Class to monitor udev events on the Type C subsystem and inform other
  // objects / classes of these events.
  class UdevMonitor {
   public:
    UdevMonitor() = default;
  
    // Create a Udev device for enumeration and monitoring.
    bool InitUdev();
  
    // Enumerate all existing events/devices, and send the appropriate
    // notifications to other classes.
    bool ScanDevices();
  
    // Start monitoring udev for typec events.
    bool BeginMonitoring();
  
    class Observer : public base::CheckedObserver {
     public:
      virtual ~Observer() {}
      // Callback that is executed when a port is connected or disconnected.
      //
      // The |path| argument refers to the sysfs device path of the port.
      // The |port_num| argmnet refers to the port's index number.
      // The |added| argument is set to true if the port was added, and false
      // otherwise.
      virtual void OnPortAddedOrRemoved(const base::FilePath& path,
                                        int port_num,
                                        bool added) = 0;
  
      // Callback that is executed when a port partner is connected or
      // disconnected.
      //
      // The |path| argument refers to the sysfs device path of the port partner.
      // The |port_num| argument refers to the port's index number.
      // The |added| argument is set to true if the partner was added, and false
      // otherwise.
      virtual void OnPartnerAddedOrRemoved(const base::FilePath& path,
                                           int port_num,
                                           bool added) = 0;
  
      // Callback that is executed when a port partner alt mode is registered or
      // removed.
      //
      // The |path| argument refers to the sysfs device path of the partner alt
      // mode. The |port_num| argmnet refers to the port's index number. The
      // |added| argument is set to true if the alt mode was added, and false
      // otherwise.
      virtual void OnPartnerAltModeAddedOrRemoved(const base::FilePath& path,
                                                  int port_num,
                                                  bool added) = 0;
  
      // Callback that is executed when a port cable is connected or
      // disconnected.
      //
      // The |path| argument refers to the sysfs device path of the port cable.
      // The |port_num| argument refers to the port's index number.
      // The |added| argument is set to true if the cable was added, and false
      // otherwise.
      virtual void OnCableAddedOrRemoved(const base::FilePath& path,
                                         int port_num,
                                         bool added) = 0;
  
      // Callback that is executed when a cable plug (SOP') device is registered.
      //
      // The |path| argument refers to the sysfs device path of the cable plug
      // (SOP'). The |port_num| argument refers to the port's index number.
      virtual void OnCablePlugAdded(const base::FilePath& path, int port_num) = 0;
  
      // Callback that is executed when a cable (SOP') alternate mode is
      // registered.
      //
      // The |path| argument refers to the sysfs device path of the cable (SOP')
      // alternate mode. The |port_num| argument refers to the port's index
      // number.
      virtual void OnCableAltModeAdded(const base::FilePath& path,
                                       int port_num) = 0;
  
      // Callback that is executed when a partner "change" event is received.
      //
      // The |port_num| argument refers to the port's index number.
      virtual void OnPartnerChanged(int port_num) = 0;
    };
  
    void AddObserver(Observer* obs);
    void RemoveObserver(Observer* obs);
  
   private:
    friend class UdevMonitorTest;
    FRIEND_TEST(UdevMonitorTest, TestBasic);
    FRIEND_TEST(UdevMonitorTest, TestHotplug);
    FRIEND_TEST(UdevMonitorTest, TestInvalidPortSyspath);
    FRIEND_TEST(UdevMonitorTest, TestCableAndAltModeAddition);
    FRIEND_TEST(UdevMonitorTest, TestPartnerChanged);
  
    // Set the |udev_| pointer to a MockUdev device. *Only* used by unit tests.
    void SetUdev(std::unique_ptr<brillo::MockUdev> udev) {
      udev_ = std::move(udev);
    }
  
    // Handle a udev event which causes a Type C device to be added/removed.
    bool HandleDeviceAddedRemoved(const base::FilePath& path, bool added);
  
    // Handle a udev "change" event for a Type C device.
    void HandleDeviceChange(const base::FilePath& path);
  
    // Handle Udev events emanating from |udev_monitor_watcher_|.
    void HandleUdevEvent();
  
    std::unique_ptr<brillo::Udev> udev_;
    std::unique_ptr<brillo::UdevMonitor> udev_monitor_;
    std::unique_ptr<base::FileDescriptorWatcher::Controller>
        udev_monitor_watcher_;
    base::ObserverList<Observer> observer_list_;
  };
  
  }  // namespace typecd
  
  #endif  // TYPECD_UDEV_MONITOR_H_
  