1 #pragma once
2 
3 #include <set>
4 #include <sstream>
5 #include <string>
6 #include <string_view>
7 #include <thread>
8 
9 class ApexUpdateListener {
10  private:
11   struct Sigil {};
12 
13  public:
14   // Information extracted from updated /apex/apex-info-list.xml
15   struct Info {
16     std::string module_name;
17     std::string module_path;
18     std::string preinstalled_module_path;
19     int64_t version_code;
20     std::string version_name;
21     bool is_factory;
22     bool is_active;
23 
AsTupleInfo24     auto AsTuple() const {
25       return std::make_tuple(is_active, is_factory, version_code, version_name,
26                              module_path, preinstalled_module_path, module_name);
27     }
28 
29     bool operator<(const Info& other) const {
30       return AsTuple() < other.AsTuple();
31     }
32 
33     bool operator==(const ApexUpdateListener::Info& other) const {
34       return !(other < *this) && !(*this < other);
35     }
36     bool operator!=(const ApexUpdateListener::Info& other) const {
37       return !(*this == other);
38     }
39     template <typename T>
40     friend auto& operator<<(T& stream, const ApexUpdateListener::Info& i) {
41       return stream << "{ moduleName: " << i.module_name
42                     << ", modulePath: " << i.module_path
43                     << ", preinstalledModulePath: " << i.preinstalled_module_path
44                     << ", versionCode: " << i.version_code
45                     << ", versionName: " << i.version_name
46                     << ", i.isFactory: " << i.is_factory
47                     << ", i.isActive: " << i.is_active << " }";
48     }
49     template <typename T>
50     friend auto& operator<<(T& stream,
51                             const std::set<ApexUpdateListener::Info>& s) {
52       stream << "{";
53       std::string sep = "";
54       for (auto& i : s) {
55         stream << sep << i;
56         sep = ", ";
57       }
58       return stream << "}";
59     }
60   };
61 
62   using CallbackFunction =
63       std::function<void(const std::set<Info>& last_versions,
64                          const std::set<Info>& current_versions)>;
65 
66   ApexUpdateListener(Sigil, const std::string& apex_name,
67                      const std::string& apex_info_list_file_name,
68                      CallbackFunction callback, int fd, int wd,
69                      std::set<Info> last_info);
70 
71   static std::unique_ptr<ApexUpdateListener> Make(
72       const std::string& apex_name, CallbackFunction callback,
73       bool invoke_with_initial_version = false,
74       const std::string& apex_info_list_file_name = "/apex/apex-info-list.xml");
75 
76   // We need some cleanup handling
77   ~ApexUpdateListener();
78 
79  private:
80   const std::string apex_name_;
81   const std::string apex_info_list_file_name_;
82   const CallbackFunction callback_function_;
83   std::atomic<bool> running_ = true;
84   int file_descriptor_ = -1;
85   int watch_descriptor_ = -1;
86   std::set<Info> last_info_;
87   std::thread thread_;
88 
89   void ThreadFunction();
90   static std::optional<std::set<Info>> TrySlurpInfo(
91       const std::string& apex_name, const std::string& apex_info_list_file_name);
92 };
93