diff --git a/base/Android.bp b/base/Android.bp index e2604120d..88d8ad111 100644 --- a/base/Android.bp +++ b/base/Android.bp @@ -39,7 +39,10 @@ cc_library { shared_libs: ["liblog"], target: { android: { - srcs: ["errors_unix.cpp"], + srcs: [ + "errors_unix.cpp", + "properties.cpp", + ], cppflags: ["-Wexit-time-destructors"], }, darwin: { @@ -78,6 +81,9 @@ cc_test { "test_main.cpp", ], target: { + android: { + srcs: ["properties_test.cpp"], + }, windows: { srcs: ["utf8_test.cpp"], enabled: true, diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h new file mode 100644 index 000000000..95d1b6abb --- /dev/null +++ b/base/include/android-base/properties.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_BASE_PROPERTIES_H +#define ANDROID_BASE_PROPERTIES_H + +#include + +#if !defined(__BIONIC__) +#error Only bionic supports system properties. +#endif + +#include +#include + +namespace android { +namespace base { + +// Returns the current value of the system property `key`, +// or `default_value` if the property is empty or doesn't exist. +std::string GetProperty(const std::string& key, const std::string& default_value); + +// Returns true if the system property `key` has the value "1", "y", "yes", "on", or "true", +// false for "0", "n", "no", "off", or "false", or `default_value` otherwise. +bool GetBoolProperty(const std::string& key, bool default_value); + +// Returns the signed integer corresponding to the system property `key`. +// If the property is empty, doesn't exist, doesn't have an integer value, or is outside +// the optional bounds, returns `default_value`. +template T GetIntProperty(const std::string& key, + T default_value, + T min = std::numeric_limits::min(), + T max = std::numeric_limits::max()); + +// Returns the unsigned integer corresponding to the system property `key`. +// If the property is empty, doesn't exist, doesn't have an integer value, or is outside +// the optional bound, returns `default_value`. +template T GetUintProperty(const std::string& key, + T default_value, + T max = std::numeric_limits::max()); + +// Sets the system property `key` to `value`. +// Note that system property setting is inherently asynchronous so a return value of `true` +// isn't particularly meaningful, and immediately reading back the value won't necessarily +// tell you whether or not your call succeeded. A `false` return value definitely means failure. +bool SetProperty(const std::string& key, const std::string& value); + +} // namespace base +} // namespace android + +#endif // ANDROID_BASE_MEMORY_H diff --git a/base/properties.cpp b/base/properties.cpp new file mode 100644 index 000000000..fab300557 --- /dev/null +++ b/base/properties.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/properties.h" + +#include + +#include + +#include + +namespace android { +namespace base { + +std::string GetProperty(const std::string& key, const std::string& default_value) { + const prop_info* pi = __system_property_find(key.c_str()); + if (pi == nullptr) return default_value; + + char buf[PROP_VALUE_MAX]; + if (__system_property_read(pi, nullptr, buf) > 0) return buf; + + // If the property exists but is empty, also return the default value. + // Since we can't remove system properties, "empty" is traditionally + // the same as "missing" (this was true for cutils' property_get). + return default_value; +} + +bool GetBoolProperty(const std::string& key, bool default_value) { + std::string value = GetProperty(key, ""); + if (value == "1" || value == "y" || value == "yes" || value == "on" || value == "true") { + return true; + } else if (value == "0" || value == "n" || value == "no" || value == "off" || value == "false") { + return false; + } + return default_value; +} + +template +T GetIntProperty(const std::string& key, T default_value, T min, T max) { + T result; + std::string value = GetProperty(key, ""); + if (!value.empty() && android::base::ParseInt(value.c_str(), &result, min, max)) return result; + return default_value; +} + +template +T GetUintProperty(const std::string& key, T default_value, T max) { + T result; + std::string value = GetProperty(key, ""); + if (!value.empty() && android::base::ParseUint(value.c_str(), &result, max)) return result; + return default_value; +} + +template int8_t GetIntProperty(const std::string&, int8_t, int8_t, int8_t); +template int16_t GetIntProperty(const std::string&, int16_t, int16_t, int16_t); +template int32_t GetIntProperty(const std::string&, int32_t, int32_t, int32_t); +template int64_t GetIntProperty(const std::string&, int64_t, int64_t, int64_t); + +template uint8_t GetUintProperty(const std::string&, uint8_t, uint8_t); +template uint16_t GetUintProperty(const std::string&, uint16_t, uint16_t); +template uint32_t GetUintProperty(const std::string&, uint32_t, uint32_t); +template uint64_t GetUintProperty(const std::string&, uint64_t, uint64_t); + +bool SetProperty(const std::string& key, const std::string& value) { + return (__system_property_set(key.c_str(), value.c_str()) == 0); +} + +} // namespace base +} // namespace android diff --git a/base/properties_test.cpp b/base/properties_test.cpp new file mode 100644 index 000000000..da89ec5a9 --- /dev/null +++ b/base/properties_test.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "android-base/properties.h" + +#include + +#include + +TEST(properties, smoke) { + android::base::SetProperty("debug.libbase.property_test", "hello"); + + std::string s = android::base::GetProperty("debug.libbase.property_test", ""); + ASSERT_EQ("hello", s); + + android::base::SetProperty("debug.libbase.property_test", "world"); + s = android::base::GetProperty("debug.libbase.property_test", ""); + ASSERT_EQ("world", s); + + s = android::base::GetProperty("this.property.does.not.exist", ""); + ASSERT_EQ("", s); + + s = android::base::GetProperty("this.property.does.not.exist", "default"); + ASSERT_EQ("default", s); +} + +TEST(properties, empty) { + // Because you can't delete a property, people "delete" them by + // setting them to the empty string. In that case we'd want to + // keep the default value (like cutils' property_get did). + android::base::SetProperty("debug.libbase.property_test", ""); + std::string s = android::base::GetProperty("debug.libbase.property_test", "default"); + ASSERT_EQ("default", s); +} + +static void CheckGetBoolProperty(bool expected, const std::string& value, bool default_value) { + android::base::SetProperty("debug.libbase.property_test", value.c_str()); + ASSERT_EQ(expected, android::base::GetBoolProperty("debug.libbase.property_test", default_value)); +} + +TEST(properties, GetBoolProperty_true) { + CheckGetBoolProperty(true, "1", false); + CheckGetBoolProperty(true, "y", false); + CheckGetBoolProperty(true, "yes", false); + CheckGetBoolProperty(true, "on", false); + CheckGetBoolProperty(true, "true", false); +} + +TEST(properties, GetBoolProperty_false) { + CheckGetBoolProperty(false, "0", true); + CheckGetBoolProperty(false, "n", true); + CheckGetBoolProperty(false, "no", true); + CheckGetBoolProperty(false, "off", true); + CheckGetBoolProperty(false, "false", true); +} + +TEST(properties, GetBoolProperty_default) { + CheckGetBoolProperty(true, "burp", true); + CheckGetBoolProperty(false, "burp", false); +} + +template void CheckGetIntProperty() { + // Positive and negative. + android::base::SetProperty("debug.libbase.property_test", "-12"); + EXPECT_EQ(T(-12), android::base::GetIntProperty("debug.libbase.property_test", 45)); + android::base::SetProperty("debug.libbase.property_test", "12"); + EXPECT_EQ(T(12), android::base::GetIntProperty("debug.libbase.property_test", 45)); + + // Default value. + android::base::SetProperty("debug.libbase.property_test", ""); + EXPECT_EQ(T(45), android::base::GetIntProperty("debug.libbase.property_test", 45)); + + // Bounds checks. + android::base::SetProperty("debug.libbase.property_test", "0"); + EXPECT_EQ(T(45), android::base::GetIntProperty("debug.libbase.property_test", 45, 1, 2)); + android::base::SetProperty("debug.libbase.property_test", "1"); + EXPECT_EQ(T(1), android::base::GetIntProperty("debug.libbase.property_test", 45, 1, 2)); + android::base::SetProperty("debug.libbase.property_test", "2"); + EXPECT_EQ(T(2), android::base::GetIntProperty("debug.libbase.property_test", 45, 1, 2)); + android::base::SetProperty("debug.libbase.property_test", "3"); + EXPECT_EQ(T(45), android::base::GetIntProperty("debug.libbase.property_test", 45, 1, 2)); +} + +template void CheckGetUintProperty() { + // Positive. + android::base::SetProperty("debug.libbase.property_test", "12"); + EXPECT_EQ(T(12), android::base::GetUintProperty("debug.libbase.property_test", 45)); + + // Default value. + android::base::SetProperty("debug.libbase.property_test", ""); + EXPECT_EQ(T(45), android::base::GetUintProperty("debug.libbase.property_test", 45)); + + // Bounds checks. + android::base::SetProperty("debug.libbase.property_test", "12"); + EXPECT_EQ(T(12), android::base::GetUintProperty("debug.libbase.property_test", 33, 22)); + android::base::SetProperty("debug.libbase.property_test", "12"); + EXPECT_EQ(T(5), android::base::GetUintProperty("debug.libbase.property_test", 5, 10)); +} + +TEST(properties, GetIntProperty_int8_t) { CheckGetIntProperty(); } +TEST(properties, GetIntProperty_int16_t) { CheckGetIntProperty(); } +TEST(properties, GetIntProperty_int32_t) { CheckGetIntProperty(); } +TEST(properties, GetIntProperty_int64_t) { CheckGetIntProperty(); } + +TEST(properties, GetUintProperty_uint8_t) { CheckGetUintProperty(); } +TEST(properties, GetUintProperty_uint16_t) { CheckGetUintProperty(); } +TEST(properties, GetUintProperty_uint32_t) { CheckGetUintProperty(); } +TEST(properties, GetUintProperty_uint64_t) { CheckGetUintProperty(); }