1 /* 2 * Copyright (C) 2016 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 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ 18 19 #include "android-base/properties.h" 20 21 #include <sys/system_properties.h> 22 #include <sys/_system_properties.h> 23 24 #include <algorithm> 25 #include <chrono> 26 #include <limits> 27 #include <string> 28 29 #include <android-base/parseint.h> 30 31 namespace android { 32 namespace base { 33 34 std::string GetProperty(const std::string& key, const std::string& default_value) { 35 const prop_info* pi = __system_property_find(key.c_str()); 36 if (pi == nullptr) return default_value; 37 38 std::string property_value; 39 __system_property_read_callback(pi, 40 [](void* cookie, const char*, const char* value, unsigned) { 41 auto property_value = reinterpret_cast<std::string*>(cookie); 42 *property_value = value; 43 }, 44 &property_value); 45 46 // If the property exists but is empty, also return the default value. 47 // Since we can't remove system properties, "empty" is traditionally 48 // the same as "missing" (this was true for cutils' property_get). 49 return property_value.empty() ? default_value : property_value; 50 } 51 52 bool GetBoolProperty(const std::string& key, bool default_value) { 53 std::string value = GetProperty(key, ""); 54 if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") { 55 return true; 56 } else if (value == "0" || value == "n" || value == "no" || value == "off" || value == "false") { 57 return false; 58 } 59 return default_value; 60 } 61 62 template <typename T> 63 T GetIntProperty(const std::string& key, T default_value, T min, T max) { 64 T result; 65 std::string value = GetProperty(key, ""); 66 if (!value.empty() && android::base::ParseInt(value, &result, min, max)) return result; 67 return default_value; 68 } 69 70 template <typename T> 71 T GetUintProperty(const std::string& key, T default_value, T max) { 72 T result; 73 std::string value = GetProperty(key, ""); 74 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result; 75 return default_value; 76 } 77 78 template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t); 79 template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t); 80 template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t); 81 template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t); 82 83 template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t); 84 template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t); 85 template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t); 86 template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t); 87 88 bool SetProperty(const std::string& key, const std::string& value) { 89 return (__system_property_set(key.c_str(), value.c_str()) == 0); 90 } 91 92 struct WaitForPropertyData { 93 bool done; 94 const std::string* expected_value; 95 unsigned last_read_serial; 96 }; 97 98 static void WaitForPropertyCallback(void* data_ptr, const char*, const char* value, unsigned serial) { 99 WaitForPropertyData* data = reinterpret_cast<WaitForPropertyData*>(data_ptr); 100 if (*data->expected_value == value) { 101 data->done = true; 102 } else { 103 data->last_read_serial = serial; 104 } 105 } 106 107 // TODO: chrono_utils? 108 static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) { 109 auto s = std::chrono::duration_cast<std::chrono::seconds>(d); 110 auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s); 111 ts.tv_sec = std::min<std::chrono::seconds::rep>(s.count(), std::numeric_limits<time_t>::max()); 112 ts.tv_nsec = ns.count(); 113 } 114 115 // TODO: boot_clock? 116 using AbsTime = std::chrono::time_point<std::chrono::steady_clock>; 117 118 static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout, 119 const AbsTime& start_time) { 120 auto now = std::chrono::steady_clock::now(); 121 auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time); 122 if (time_elapsed >= relative_timeout) { 123 ts = { 0, 0 }; 124 } else { 125 auto remaining_timeout = relative_timeout - time_elapsed; 126 DurationToTimeSpec(ts, remaining_timeout); 127 } 128 } 129 130 // Waits for the system property `key` to be created. 131 // Times out after `relative_timeout`. 132 // Sets absolute_timeout which represents absolute time for the timeout. 133 // Returns nullptr on timeout. 134 static const prop_info* WaitForPropertyCreation(const std::string& key, 135 const std::chrono::milliseconds& relative_timeout, 136 const AbsTime& start_time) { 137 // Find the property's prop_info*. 138 const prop_info* pi; 139 unsigned global_serial = 0; 140 while ((pi = __system_property_find(key.c_str())) == nullptr) { 141 // The property doesn't even exist yet. 142 // Wait for a global change and then look again. 143 timespec ts; 144 UpdateTimeSpec(ts, relative_timeout, start_time); 145 if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr; 146 } 147 return pi; 148 } 149 150 bool WaitForProperty(const std::string& key, const std::string& expected_value, 151 std::chrono::milliseconds relative_timeout) { 152 auto start_time = std::chrono::steady_clock::now(); 153 const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time); 154 if (pi == nullptr) return false; 155 156 WaitForPropertyData data; 157 data.expected_value = &expected_value; 158 data.done = false; 159 while (true) { 160 timespec ts; 161 // Check whether the property has the value we're looking for? 162 __system_property_read_callback(pi, WaitForPropertyCallback, &data); 163 if (data.done) return true; 164 165 // It didn't, so wait for the property to change before checking again. 166 UpdateTimeSpec(ts, relative_timeout, start_time); 167 uint32_t unused; 168 if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false; 169 } 170 } 171 172 bool WaitForPropertyCreation(const std::string& key, 173 std::chrono::milliseconds relative_timeout) { 174 auto start_time = std::chrono::steady_clock::now(); 175 return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr); 176 } 177 178 } // namespace base 179 } // namespace android 180