Merge changes from topic "appcompat_override" into main

* changes:
  Move __system_properties_reload to LIBC from LIBC_PLATFORM
  Write appcompat_override system properties
This commit is contained in:
Treehugger Robot 2023-10-31 02:29:57 +00:00 committed by Gerrit Code Review
commit 0207a6a186
16 changed files with 392 additions and 55 deletions

View file

@ -29,6 +29,7 @@
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
#include <async_safe/CHECK.h>
#include <system_properties/prop_area.h>
#include <system_properties/system_properties.h>
@ -45,7 +46,7 @@ prop_area* __system_property_area__ = nullptr;
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_properties_init() {
return system_properties.Init(PROP_FILENAME) ? 0 : -1;
return system_properties.Init(PROP_DIRNAME) ? 0 : -1;
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
@ -55,8 +56,8 @@ int __system_property_set_filename(const char*) {
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() {
bool fsetxattr_failed = false;
return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
bool fsetxattr_fail = false;
return system_properties.AreaInit(PROP_DIRNAME, &fsetxattr_fail) && !fsetxattr_fail ? 0 : -1;
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
@ -129,3 +130,9 @@ __BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
return system_properties.Foreach(propfn, cookie);
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_properties_zygote_reload(void) {
CHECK(getpid() == gettid());
return system_properties.Reload(false) ? 0 : -1;
}

View file

@ -41,7 +41,7 @@
__BEGIN_DECLS
#define PROP_SERVICE_NAME "property_service"
#define PROP_FILENAME "/dev/__properties__"
#define PROP_DIRNAME "/dev/__properties__"
#define PROP_MSG_SETPROP 1
#define PROP_MSG_SETPROP2 0x00020001
@ -129,6 +129,18 @@ uint32_t __system_property_serial(const prop_info* _Nonnull __pi);
*/
int __system_properties_init(void);
/*
* Reloads the system properties from disk.
* Not intended for use by any apps except the Zygote. Should only be called from the main thread.
*
* NOTE: Any pointers received from methods such as __system_property_find should be assumed to be
* invalid after this method is called.
*
* Returns 0 on success, -1 if the system properties failed to re-initialize (same conditions as
* __system properties_init)
*/
int __system_properties_zygote_reload(void); __INTRODUCED_IN(__ANDROID_API_V__)
/* Deprecated: use __system_property_wait instead. */
uint32_t __system_property_wait_any(uint32_t __old_serial);

View file

@ -1600,6 +1600,7 @@ LIBC_V { # introduced=VanillaIceCream
tzalloc;
tzfree;
wcsrtombs_l;
__system_properties_zygote_reload; # apex
} LIBC_U;
LIBC_PRIVATE {

View file

@ -38,6 +38,7 @@
#include <new>
#include <async_safe/log.h>
#include <private/android_filesystem_config.h>
#include "system_properties/system_properties.h"
@ -59,25 +60,28 @@ bool ContextsSerialized::InitializeContextNodes() {
context_nodes_mmap_size_ = context_nodes_mmap_size;
for (size_t i = 0; i < num_context_nodes; ++i) {
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), dirname_);
}
return true;
}
bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) {
PropertiesFilename filename(filename_, "properties_serial");
if (access_rw) {
serial_prop_area_ = prop_area::map_prop_area_rw(
filename.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
serial_filename_.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
} else {
serial_prop_area_ = prop_area::map_prop_area(filename.c_str());
serial_prop_area_ = prop_area::map_prop_area(serial_filename_.c_str());
}
return serial_prop_area_;
}
bool ContextsSerialized::InitializeProperties() {
if (!property_info_area_file_.LoadDefaultPath()) {
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
bool ContextsSerialized::InitializeProperties(bool load_default_path) {
if (load_default_path && !property_info_area_file_.LoadDefaultPath()) {
return false;
} else if (!load_default_path && !property_info_area_file_.LoadPath(tree_filename_.c_str())) {
return false;
}
@ -89,14 +93,20 @@ bool ContextsSerialized::InitializeProperties() {
return true;
}
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
filename_ = filename;
if (!InitializeProperties()) {
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
bool ContextsSerialized::Initialize(bool writable, const char* dirname, bool* fsetxattr_failed,
bool load_default_path) {
dirname_ = dirname;
tree_filename_ = PropertiesFilename(dirname, "property_info");
serial_filename_ = PropertiesFilename(dirname, "properties_serial");
if (!InitializeProperties(load_default_path)) {
return false;
}
if (writable) {
mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
mkdir(dirname_, S_IRWXU | S_IXGRP | S_IXOTH);
bool open_failed = false;
if (fsetxattr_failed) {
*fsetxattr_failed = false;

View file

@ -281,7 +281,7 @@ bool ContextsSplit::InitializeProperties() {
return true;
}
bool ContextsSplit::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
bool ContextsSplit::Initialize(bool writable, const char* filename, bool* fsetxattr_failed, bool) {
filename_ = filename;
if (!InitializeProperties()) {
return false;

View file

@ -36,7 +36,8 @@ class Contexts {
virtual ~Contexts() {
}
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) = 0;
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed,
bool load_default_path = false) = 0;
virtual prop_area* GetPropAreaForName(const char* name) = 0;
virtual prop_area* GetSerialPropArea() = 0;
virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) = 0;

View file

@ -38,7 +38,7 @@ class ContextsPreSplit : public Contexts {
}
// We'll never initialize this legacy option as writable, so don't even check the arg.
virtual bool Initialize(bool, const char* filename, bool*) override {
virtual bool Initialize(bool, const char* filename, bool*, bool) override {
pre_split_prop_area_ = prop_area::map_prop_area(filename);
return pre_split_prop_area_ != nullptr;
}

View file

@ -32,13 +32,15 @@
#include "context_node.h"
#include "contexts.h"
#include "properties_filename.h"
class ContextsSerialized : public Contexts {
public:
virtual ~ContextsSerialized() override {
}
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) override;
virtual bool Initialize(bool writable, const char* dirname, bool* fsetxattr_failed,
bool load_default_path) override;
virtual prop_area* GetPropAreaForName(const char* name) override;
virtual prop_area* GetSerialPropArea() override {
return serial_prop_area_;
@ -49,10 +51,12 @@ class ContextsSerialized : public Contexts {
private:
bool InitializeContextNodes();
bool InitializeProperties();
bool InitializeProperties(bool load_default_path);
bool MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed);
const char* filename_;
const char* dirname_;
PropertiesFilename tree_filename_;
PropertiesFilename serial_filename_;
android::properties::PropertyInfoAreaFile property_info_area_file_;
ContextNode* context_nodes_ = nullptr;
size_t num_context_nodes_ = 0;

View file

@ -38,7 +38,8 @@ class ContextsSplit : public Contexts {
virtual ~ContextsSplit() override {
}
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) override;
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed,
bool) override;
virtual prop_area* GetPropAreaForName(const char* name) override;
virtual prop_area* GetSerialPropArea() override {
return serial_prop_area_;

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#pragma once
#include <stdint.h>
class PropertiesFilename {
public:
PropertiesFilename() = default;
PropertiesFilename(const char* dir, const char* file) {
if (snprintf(filename_, sizeof(filename_), "%s/%s", dir, file) >=
static_cast<int>(sizeof(filename_))) {
abort();
}
}
void operator=(const char* value) {
if (strlen(value) >= sizeof(filename_)) abort();
strcpy(filename_, value);
}
const char* c_str() { return filename_; }
private:
// Typically something like "/dev/__properties__/properties_serial".
char filename_[128];
};

View file

@ -28,7 +28,6 @@
#pragma once
#include <stdint.h>
#include <sys/param.h>
#include <sys/system_properties.h>
@ -37,26 +36,6 @@
#include "contexts_serialized.h"
#include "contexts_split.h"
class PropertiesFilename {
public:
PropertiesFilename() = default;
PropertiesFilename(const char* dir, const char* file) {
if (snprintf(filename_, sizeof(filename_), "%s/%s", dir, file) >=
static_cast<int>(sizeof(filename_))) {
abort();
}
}
void operator=(const char* value) {
if (strlen(value) >= sizeof(filename_)) abort();
strcpy(filename_, value);
}
const char* c_str() { return filename_; }
private:
// Typically something like "/dev/__properties__/properties_serial".
char filename_[128];
};
class SystemProperties {
public:
friend struct LocalPropertyTestState;
@ -73,7 +52,9 @@ class SystemProperties {
BIONIC_DISALLOW_COPY_AND_ASSIGN(SystemProperties);
bool Init(const char* filename);
bool Reload(bool load_default_path);
bool AreaInit(const char* filename, bool* fsetxattr_failed);
bool AreaInit(const char* filename, bool* fsetxattr_failed, bool load_default_path);
uint32_t AreaSerial();
const prop_info* Find(const char* name);
int Read(const prop_info* pi, char* name, char* value);
@ -101,8 +82,14 @@ class SystemProperties {
static constexpr size_t kMaxContextsSize =
MAX(sizeof(ContextsSerialized), MAX(sizeof(ContextsSplit), sizeof(ContextsPreSplit)));
alignas(kMaxContextsAlign) char contexts_data_[kMaxContextsSize];
alignas(kMaxContextsAlign) char appcompat_override_contexts_data_[kMaxContextsSize];
Contexts* contexts_;
// See http://b/291816546#comment#3 for more explanation of appcompat_override
Contexts* appcompat_override_contexts_;
bool InitContexts(bool load_default_path);
bool initialized_;
PropertiesFilename properties_filename_;
PropertiesFilename appcompat_filename_;
};

View file

@ -29,6 +29,7 @@
#include "system_properties/system_properties.h"
#include <errno.h>
#include <private/android_filesystem_config.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
@ -38,6 +39,7 @@
#include <new>
#include <async_safe/CHECK.h>
#include <async_safe/log.h>
#include "private/ErrnoRestorer.h"
@ -49,6 +51,7 @@
#define SERIAL_DIRTY(serial) ((serial)&1)
#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
#define APPCOMPAT_PREFIX "ro.appcompat_override."
static bool is_dir(const char* pathname) {
struct stat info;
@ -69,10 +72,21 @@ bool SystemProperties::Init(const char* filename) {
properties_filename_ = filename;
if (!InitContexts(false)) {
return false;
}
initialized_ = true;
return true;
}
bool SystemProperties::InitContexts(bool load_default_path) {
if (is_dir(properties_filename_.c_str())) {
if (access("/dev/__properties__/property_info", R_OK) == 0) {
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
if (access(PROP_TREE_FILE, R_OK) == 0) {
auto serial_contexts = new (contexts_data_) ContextsSerialized();
contexts_ = serial_contexts;
if (!serial_contexts->Initialize(false, properties_filename_.c_str(), nullptr,
load_default_path)) {
return false;
}
} else {
@ -87,20 +101,46 @@ bool SystemProperties::Init(const char* filename) {
return false;
}
}
initialized_ = true;
return true;
}
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
return AreaInit(filename, fsetxattr_failed, false);
}
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed,
bool load_default_path) {
properties_filename_ = filename;
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(true, properties_filename_.c_str(), fsetxattr_failed)) {
auto serial_contexts = new (contexts_data_) ContextsSerialized();
contexts_ = serial_contexts;
if (!serial_contexts->Initialize(true, properties_filename_.c_str(), fsetxattr_failed,
load_default_path)) {
return false;
}
auto* appcompat_contexts = new (appcompat_override_contexts_data_) ContextsSerialized();
appcompat_filename_ = PropertiesFilename(properties_filename_.c_str(), "appcompat_override");
if (!appcompat_contexts->Initialize(true, appcompat_filename_.c_str(), fsetxattr_failed,
load_default_path)) {
appcompat_override_contexts_ = nullptr;
return false;
}
appcompat_override_contexts_ = appcompat_contexts;
initialized_ = true;
return true;
}
bool SystemProperties::Reload(bool load_default_path) {
if (!initialized_) {
return true;
}
return InitContexts(load_default_path);
}
uint32_t SystemProperties::AreaSerial() {
if (!initialized_) {
return -1;
@ -129,6 +169,10 @@ const prop_info* SystemProperties::Find(const char* name) {
return pa->find(name);
}
static bool is_appcompat_override(const char* name) {
return strncmp(name, APPCOMPAT_PREFIX, strlen(APPCOMPAT_PREFIX)) == 0;
}
static bool is_read_only(const char* name) {
return strncmp(name, "ro.", 3) == 0;
}
@ -227,16 +271,24 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
if (!initialized_) {
return -1;
}
bool have_override = appcompat_override_contexts_ != nullptr;
prop_area* serial_pa = contexts_->GetSerialPropArea();
prop_area* override_serial_pa =
have_override ? appcompat_override_contexts_->GetSerialPropArea() : nullptr;
if (!serial_pa) {
return -1;
}
prop_area* pa = contexts_->GetPropAreaForName(pi->name);
prop_area* override_pa =
have_override ? appcompat_override_contexts_->GetPropAreaForName(pi->name) : nullptr;
if (__predict_false(!pa)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find area for \"%s\"", pi->name);
return -1;
}
CHECK(!have_override || (override_pa && override_serial_pa));
auto* override_pi = const_cast<prop_info*>(have_override ? override_pa->find(pi->name) : nullptr);
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
unsigned int old_len = SERIAL_VALUE_LEN(serial);
@ -246,18 +298,34 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
// that we publish our dirty area update before allowing readers to see a
// dirty serial.
memcpy(pa->dirty_backup_area(), pi->value, old_len + 1);
if (have_override) {
memcpy(override_pa->dirty_backup_area(), override_pi->value, old_len + 1);
}
atomic_thread_fence(memory_order_release);
serial |= 1;
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
strlcpy(pi->value, value, len + 1);
if (have_override) {
atomic_store_explicit(&override_pi->serial, serial, memory_order_relaxed);
strlcpy(override_pi->value, value, len + 1);
}
// Now the primary value property area is up-to-date. Let readers know that they should
// look at the property value instead of the backup area.
atomic_thread_fence(memory_order_release);
atomic_store_explicit(&pi->serial, (len << 24) | ((serial + 1) & 0xffffff), memory_order_relaxed);
int new_serial = (len << 24) | ((serial + 1) & 0xffffff);
atomic_store_explicit(&pi->serial, new_serial, memory_order_relaxed);
if (have_override) {
atomic_store_explicit(&override_pi->serial, new_serial, memory_order_relaxed);
}
__futex_wake(&pi->serial, INT32_MAX); // Fence by side effect
atomic_store_explicit(serial_pa->serial(),
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
if (have_override) {
atomic_store_explicit(override_serial_pa->serial(),
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
}
__futex_wake(serial_pa->serial(), INT32_MAX);
return 0;
@ -293,6 +361,34 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va
return -1;
}
if (appcompat_override_contexts_ != nullptr) {
bool is_override = is_appcompat_override(name);
const char* override_name = name;
if (is_override) override_name += strlen(APPCOMPAT_PREFIX);
prop_area* other_pa = appcompat_override_contexts_->GetPropAreaForName(override_name);
prop_area* other_serial_pa = appcompat_override_contexts_->GetSerialPropArea();
CHECK(other_pa && other_serial_pa);
// We may write a property twice to overrides, once for the ro.*, and again for the
// ro.appcompat_override.ro.* property. If we've already written, then we should essentially
// perform an Update, not an Add.
auto other_pi = const_cast<prop_info*>(other_pa->find(override_name));
if (!other_pi) {
if (other_pa->add(override_name, strlen(override_name), value, valuelen)) {
atomic_store_explicit(
other_serial_pa->serial(),
atomic_load_explicit(other_serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
}
} else if (is_override) {
// We already wrote the ro.*, but appcompat_override.ro.* should override that. We don't
// need to do the usual dirty bit setting, as this only happens during the init process,
// before any readers are started. Check that only init or root can write appcompat props.
CHECK(getpid() == 1 || getuid() == 0);
atomic_thread_fence(memory_order_release);
strlcpy(other_pi->value, value, valuelen + 1);
}
}
// There is only a single mutator, but we want to make sure that
// updates are visible to a reader waiting for the update.
atomic_store_explicit(serial_pa->serial(),

View file

@ -25,31 +25,53 @@
#include <android-base/file.h>
#include <android-base/silent_death_test.h>
#include <android-base/stringprintf.h>
#include "utils.h"
using namespace std::literals;
#if defined(__BIONIC__)
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <stdlib.h>
#include <sys/_system_properties.h>
#include <sys/mount.h>
#include <system_properties/system_properties.h>
class SystemPropertiesTest : public SystemProperties {
public:
SystemPropertiesTest() : SystemProperties(false) {
valid_ = AreaInit(dir_.path, nullptr);
appcompat_path = android::base::StringPrintf("%s/appcompat_override", dir_.path);
mount_path = android::base::StringPrintf("%s/__properties__", dir_.path);
mkdir(appcompat_path.c_str(), S_IRWXU | S_IXGRP | S_IXOTH);
valid_ = AreaInit(dir_.path, nullptr, true);
}
~SystemPropertiesTest() {
if (valid_) {
contexts_->FreeAndUnmap();
}
umount2(dir_.path, MNT_DETACH);
umount2(real_sysprop_dir.c_str(), MNT_DETACH);
}
bool valid() const {
return valid_;
}
const char* get_path() const { return dir_.path; }
const char* get_appcompat_path() const { return appcompat_path.c_str(); }
const char* get_mount_path() const { return mount_path.c_str(); }
const char* get_real_sysprop_dir() const { return real_sysprop_dir.c_str(); }
std::string appcompat_path;
std::string mount_path;
std::string real_sysprop_dir = "/dev/__properties__";
private:
TemporaryDir dir_;
bool valid_;
@ -125,6 +147,58 @@ TEST(properties, __system_property_add) {
#endif // __BIONIC__
}
TEST(properties, __system_property_add_appcompat) {
#if defined(__BIONIC__)
if (getuid() != 0) GTEST_SKIP() << "test requires root";
SystemPropertiesTest system_properties;
ASSERT_TRUE(system_properties.valid());
char name[] = "ro.property";
char override_name[] = "ro.appcompat_override.ro.property";
char name_not_written[] = "ro.property_other";
char override_with_no_real[] = "ro.appcompat_override.ro.property_other";
ASSERT_EQ(0, system_properties.Add(name, strlen(name), "value1", 6));
ASSERT_EQ(0, system_properties.Add(override_name, strlen(override_name), "value2", 6));
ASSERT_EQ(0, system_properties.Add(override_with_no_real, strlen(override_with_no_real),
"value3", 6));
char propvalue[PROP_VALUE_MAX];
ASSERT_EQ(6, system_properties.Get(name, propvalue));
ASSERT_STREQ(propvalue, "value1");
ASSERT_EQ(6, system_properties.Get(override_name, propvalue));
ASSERT_STREQ(propvalue, "value2");
ASSERT_EQ(0, system_properties.Get(name_not_written, propvalue));
ASSERT_STREQ(propvalue, "");
ASSERT_EQ(6, system_properties.Get(override_with_no_real, propvalue));
ASSERT_STREQ(propvalue, "value3");
int ret = mount(system_properties.get_appcompat_path(), system_properties.get_path(), nullptr,
MS_BIND | MS_REC, nullptr);
if (ret != 0) {
ASSERT_ERRNO(0);
}
system_properties.Reload(true);
ASSERT_EQ(6, system_properties.Get(name, propvalue));
ASSERT_STREQ(propvalue, "value2");
ASSERT_EQ(0, system_properties.Get(override_name, propvalue));
ASSERT_STREQ(propvalue, "");
ASSERT_EQ(6, system_properties.Get(name_not_written, propvalue));
ASSERT_STREQ(propvalue, "value3");
ASSERT_EQ(0, system_properties.Get(override_with_no_real, propvalue));
ASSERT_STREQ(propvalue, "");
#else // __BIONIC__
GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}
TEST(properties, __system_property_update) {
#if defined(__BIONIC__)
SystemPropertiesTest system_properties;
@ -432,7 +506,7 @@ TEST_F(properties_DeathTest, read_only) {
// This test only makes sense if we're talking to the real system property service.
struct stat sb;
ASSERT_FALSE(stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT);
ASSERT_FALSE(stat(PROP_DIRNAME, &sb) == -1 && errno == ENOENT);
ASSERT_EXIT(__system_property_add("property", 8, "value", 5), KilledByFault(), "");
#else // __BIONIC__
@ -526,3 +600,93 @@ TEST(properties, __system_property_extra_long_read_only_too_long) {
GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}
// Note that this test affects global state of the system
// this tests tries to mitigate this by using utime+pid
// prefix for the property name. It is still results in
// pollution of property service since properties cannot
// be removed.
//
// Note that there is also possibility to run into "out-of-memory"
// if this test if it is executed often enough without reboot.
TEST(properties, __system_property_reload_no_op) {
#if defined(__BIONIC__)
std::string property_name =
android::base::StringPrintf("debug.test.%d.%" PRId64 ".property", getpid(), NanoTime());
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "test value"));
ASSERT_EQ(0, __system_properties_zygote_reload());
const prop_info* readptr = __system_property_find(property_name.c_str());
std::string expected_name = property_name;
__system_property_read_callback(
readptr,
[](void*, const char*, const char* value, unsigned) { ASSERT_STREQ("test value", value); },
&expected_name);
#else // __BIONIC__
GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}
TEST(properties, __system_property_reload_invalid) {
#if defined(__BIONIC__)
if (getuid() != 0) GTEST_SKIP() << "test requires root";
SystemPropertiesTest system_properties;
// Create an invalid property_info file, so the system will attempt to initialize a
// ContextSerialized
std::string property_info_file =
android::base::StringPrintf("%s/property_info", system_properties.get_path());
fclose(fopen(property_info_file.c_str(), "w"));
int ret = mount(system_properties.get_path(), system_properties.get_real_sysprop_dir(), nullptr,
MS_BIND | MS_REC, nullptr);
if (ret != 0) {
ASSERT_ERRNO(0);
}
ASSERT_EQ(-1, __system_properties_zygote_reload());
#else // __BIONIC__
GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}
// Note that this test affects global state of the system
// this tests tries to mitigate this by using utime+pid
// prefix for the property name. It is still results in
// pollution of property service since properties cannot
// be removed.
//
// Note that there is also possibility to run into "out-of-memory"
// if this test if it is executed often enough without reboot.
TEST(properties, __system_property_reload_valid) {
#if defined(__BIONIC__)
if (getuid() != 0) GTEST_SKIP() << "test requires root";
SystemPropertiesTest system_properties;
// Copy the system properties files into the temp directory
std::string shell_cmd = android::base::StringPrintf(
"cp -r %s %s", system_properties.get_real_sysprop_dir(), system_properties.get_path());
system(shell_cmd.c_str());
// Write a system property to the current set of system properties
std::string property_name =
android::base::StringPrintf("debug.test.%d.%" PRId64 ".property", getpid(), NanoTime());
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "test value"));
// Mount the temp directory (which doesn't have the property we just wrote) in place of the
// real one
int ret = mount(system_properties.get_mount_path(), system_properties.get_real_sysprop_dir(),
nullptr, MS_BIND | MS_REC, nullptr);
if (ret != 0) {
ASSERT_ERRNO(0);
}
// reload system properties in the new dir, and verify the property we wrote after we copied the
// files isn't there
ASSERT_EQ(0, __system_properties_zygote_reload());
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
#else // __BIONIC__
GTEST_SKIP() << "bionic-only test";
#endif // __BIONIC__
}

View file

@ -28,10 +28,6 @@
#if defined(__BIONIC__)
#include <sys/system_properties.h>
int64_t NanoTime() {
auto t = std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now());
return t.time_since_epoch().count();
}
#endif
// Note that this test affects global state of the system

View file

@ -89,6 +89,11 @@ void PrintTo(const Errno& e, std::ostream* os) {
}
}
int64_t NanoTime() {
auto t = std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now());
return t.time_since_epoch().count();
}
bool operator==(const Errno& lhs, const Errno& rhs) {
return lhs.errno_ == rhs.errno_;
}

View file

@ -316,6 +316,8 @@ static inline bool running_with_mte() {
bool IsLowRamDevice();
int64_t NanoTime();
class Errno {
public:
Errno(int e) : errno_(e) {}