summaryrefslogtreecommitdiff
path: root/common/apex_update_listener/apex_update_listener.h
blob: 195d24c707c56acaf55065058ffa322a2c50e7bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#pragma once

#include <set>
#include <sstream>
#include <string>
#include <string_view>
#include <thread>

class ApexUpdateListener {
 private:
  struct Sigil {};

 public:
  // Information extracted from updated /apex/apex-info-list.xml
  struct Info {
    std::string module_name;
    std::string module_path;
    std::string preinstalled_module_path;
    int64_t version_code;
    std::string version_name;
    bool is_factory;
    bool is_active;

    auto AsTuple() const {
      return std::make_tuple(is_active, is_factory, version_code, version_name,
                             module_path, preinstalled_module_path, module_name);
    }

    bool operator<(const Info& other) const {
      return AsTuple() < other.AsTuple();
    }

    bool operator==(const ApexUpdateListener::Info& other) const {
      return !(other < *this) && !(*this < other);
    }
    bool operator!=(const ApexUpdateListener::Info& other) const {
      return !(*this == other);
    }
    template <typename T>
    friend auto& operator<<(T& stream, const ApexUpdateListener::Info& i) {
      return stream << "{ moduleName: " << i.module_name
                    << ", modulePath: " << i.module_path
                    << ", preinstalledModulePath: " << i.preinstalled_module_path
                    << ", versionCode: " << i.version_code
                    << ", versionName: " << i.version_name
                    << ", i.isFactory: " << i.is_factory
                    << ", i.isActive: " << i.is_active << " }";
    }
    template <typename T>
    friend auto& operator<<(T& stream,
                            const std::set<ApexUpdateListener::Info>& s) {
      stream << "{";
      std::string sep = "";
      for (auto& i : s) {
        stream << sep << i;
        sep = ", ";
      }
      return stream << "}";
    }
  };

  using CallbackFunction =
      std::function<void(const std::set<Info>& last_versions,
                         const std::set<Info>& current_versions)>;

  ApexUpdateListener(Sigil, const std::string& apex_name,
                     const std::string& apex_info_list_file_name,
                     CallbackFunction callback, int fd, int wd,
                     const std::set<Info>& last_info);

  static std::unique_ptr<ApexUpdateListener> Make(
      const std::string& apex_name, CallbackFunction callback,
      bool invoke_with_initial_version = false,
      const std::string& apex_info_list_file_name = "/apex/apex-info-list.xml");

  // We need some cleanup handling
  ~ApexUpdateListener();

 private:
  const std::string apex_name_;
  const std::string apex_info_list_file_name_;
  const CallbackFunction callback_function_;
  std::atomic<bool> running_ = true;
  int file_descriptor_ = -1;
  int watch_descriptor_ = -1;
  std::set<Info> last_info_;
  std::thread thread_;

  void ThreadFunction();
  static std::optional<std::set<Info>> TrySlurpInfo(
      const std::string& apex_name, const std::string& apex_info_list_file_name);
};