Write appcompat_override system properties
Create a second set of system properties, that can be overlaid over the
real ones if necessary, for appcompat purposes.
Bug: 291814949
Ignore-AOSP-First: Aosp -> internal merge conflict
Test: manual, treehugger, system_properties_test
Change-Id: I541d3658cab7753c16970957c6ab4fc8bd68d8f3
Merged-In: I884a78b67679c1f0b90a6c0159b17ab007f8cc60
diff --git a/libc/system_properties/system_properties.cpp b/libc/system_properties/system_properties.cpp
index 049236f..e0b771c 100644
--- a/libc/system_properties/system_properties.cpp
+++ b/libc/system_properties/system_properties.cpp
@@ -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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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(getpid() == 1);
+ 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(),