1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "property_monitor.h"
18 
19 #include <atomic>
20 #include <functional>
21 #include <optional>
22 #include <string>
23 #include <thread>
24 #include <utility>
25 #include <vector>
26 
27 #include <android-base/logging.h>
28 
29 static uint32_t WaitForSerialChange(uint32_t current_serial) {
30     uint32_t result;
31     __system_property_wait(nullptr, current_serial, &result, nullptr);
32     return result;
33 }
34 
35 static bool FindProperty(const std::string& property_name, PropertyMonitorData* data) {
36     const prop_info* p = __system_property_find(property_name.c_str());
37     if (!p) {
38         return false;
39     }
40 
41     data->prop_info = p;
42     return true;
43 }
44 
45 // Read a property and return its value if it's been changed, while updating our cached serial.
46 static std::optional<std::string> ReadProperty(PropertyMonitorData* data) {
47     struct ReadData {
48         std::string value;
49         uint32_t serial;
50     };
51 
52     ReadData result;
53     __system_property_read_callback(
54             data->prop_info,
55             [](void* cookie, const char* name, const char* value, uint32_t serial) {
56                 ReadData* result = static_cast<ReadData*>(cookie);
57                 result->value = value;
58                 result->serial = serial;
59             },
60             &result);
61 
62     if (result.serial <= data->serial) {
63         return {};
64     }
65 
66     data->serial = result.serial;
67     return result.value;
68 }
69 
70 void PropertyMonitor::Add(std::string property, std::function<PropertyMonitorCallback> callback) {
71     PropertyMonitorData data = {
72             .callback = std::move(callback),
73             .prop_info = nullptr,
74             .serial = 0,
75     };
76 
77     if (FindProperty(property, &data)) {
78         data.callback(ReadProperty(&data).value());
79     } else {
80         data.callback(std::string());
81     }
82 
83     properties_.emplace(std::move(property), std::move(data));
84 }
85 
86 void PropertyMonitor::Run() {
87     bool result = true;
88     while (result) {
89         uint32_t current_serial = WaitForSerialChange(last_serial_);
90         for (auto& [property_name, data] : properties_) {
91             if (!data.prop_info) {
92                 if (FindProperty(property_name, &data)) {
93                     result &= data.callback(ReadProperty(&data).value());
94                 }
95             } else {
96                 if (auto value = ReadProperty(&data); value) {
97                     result &= data.callback(value.value());
98                 }
99             }
100         }
101 
102         last_serial_ = current_serial;
103     }
104 }
105