Sync to upstream AOSP (Android 15)

This commit is contained in:
topjohnwu 2024-06-05 15:41:29 -07:00
parent 38c90f4baa
commit 906dfd9139
20 changed files with 405 additions and 185 deletions

View File

@ -49,17 +49,11 @@ bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) {
return true;
}
char filename[PROP_FILENAME_MAX];
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
if (len < 0 || len >= PROP_FILENAME_MAX) {
lock_.unlock();
return false;
}
PropertiesFilename filename(filename_, context_);
if (access_rw) {
pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed);
pa_ = prop_area::map_prop_area_rw(filename.c_str(), context_, fsetxattr_failed);
} else {
pa_ = prop_area::map_prop_area(filename);
pa_ = prop_area::map_prop_area(filename.c_str());
}
lock_.unlock();
return pa_;
@ -84,13 +78,8 @@ void ContextNode::ResetAccess() {
}
bool ContextNode::CheckAccess() {
char filename[PROP_FILENAME_MAX];
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
if (len < 0 || len >= PROP_FILENAME_MAX) {
return false;
}
return access(filename, R_OK) == 0;
PropertiesFilename filename(filename_, context_);
return access(filename.c_str(), R_OK) == 0;
}
void ContextNode::Unmap() {

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,31 +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) {
char filename[PROP_FILENAME_MAX];
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
if (len < 0 || len >= PROP_FILENAME_MAX) {
serial_prop_area_ = nullptr;
return false;
}
if (access_rw) {
serial_prop_area_ =
prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
serial_prop_area_ = prop_area::map_prop_area_rw(
serial_filename_.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
} else {
serial_prop_area_ = prop_area::map_prop_area(filename);
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;
}
@ -95,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

@ -192,18 +192,12 @@ static int read_spec_entries(char* line_buf, int num_args, ...) {
}
bool ContextsSplit::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) {
char filename[PROP_FILENAME_MAX];
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
if (len < 0 || len >= PROP_FILENAME_MAX) {
serial_prop_area_ = nullptr;
return false;
}
PropertiesFilename filename(filename_, "properties_serial");
if (access_rw) {
serial_prop_area_ =
prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
serial_prop_area_ = prop_area::map_prop_area_rw(
filename.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
} else {
serial_prop_area_ = prop_area::map_prop_area(filename);
serial_prop_area_ = prop_area::map_prop_area(filename.c_str());
}
return serial_prop_area_;
}
@ -287,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

@ -41,7 +41,8 @@
__BEGIN_DECLS
#define PROP_SERVICE_NAME "property_service"
#define PROP_FILENAME "/dev/__properties__"
#define PROP_SERVICE_FOR_SYSTEM_NAME "property_service_for_system"
#define PROP_DIRNAME "/dev/__properties__"
#define PROP_MSG_SETPROP 1
#define PROP_MSG_SETPROP2 0x00020001
@ -61,7 +62,7 @@ __BEGIN_DECLS
** This was previously for testing, but now that SystemProperties is its own testable class,
** there is never a reason to call this function and its implementation simply returns -1.
*/
int __system_property_set_filename(const char* __filename);
int __system_property_set_filename(const char* __unused __filename);
/*
** Initialize the area to be used to store properties. Can
@ -102,7 +103,7 @@ uint32_t __system_property_area_serial(void);
**
** Returns 0 on success, -1 if the property area is full.
*/
int __system_property_add(const char* __name, unsigned int __name_length, const char* __value, unsigned int __value_length);
int __system_property_add(const char* _Nonnull __name, unsigned int __name_length, const char* _Nonnull __value, unsigned int __value_length);
/* Update the value of a system property returned by
** __system_property_find. Can only be done by a single process
@ -112,14 +113,14 @@ int __system_property_add(const char* __name, unsigned int __name_length, const
**
** Returns 0 on success, -1 if the parameters are incorrect.
*/
int __system_property_update(prop_info* __pi, const char* __value, unsigned int __value_length);
int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __value, unsigned int __value_length);
/* Read the serial number of a system property returned by
** __system_property_find.
**
** Returns the serial number on success, -1 on error.
*/
uint32_t __system_property_serial(const prop_info* __pi);
uint32_t __system_property_serial(const prop_info* _Nonnull __pi);
/* Initialize the system properties area in read only mode.
* Should be done by all processes that need to read system
@ -129,6 +130,18 @@ uint32_t __system_property_serial(const prop_info* __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

@ -26,8 +26,12 @@
* SUCH DAMAGE.
*/
#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H
#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
#pragma once
/**
* @file system_properties.h
* @brief System properties.
*/
#include <sys/cdefs.h>
#include <stdbool.h>
@ -36,43 +40,56 @@
__BEGIN_DECLS
/** An opaque structure representing a system property. */
typedef struct prop_info prop_info;
/**
* The limit on the length of a property value.
* (See PROP_NAME_MAX for property names.)
*/
#define PROP_VALUE_MAX 92
/*
/**
* Sets system property `name` to `value`, creating the system property if it doesn't already exist.
*/
int __system_property_set(const char* __name, const char* __value);
/*
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
* Use __system_property_read_callback to query the current value.
*
* Property lookup is expensive, so it can be useful to cache the result of this function.
* Returns 0 on success, or -1 on failure.
*/
const prop_info* __system_property_find(const char* __name);
int __system_property_set(const char* _Nonnull __name, const char* _Nonnull __value);
/*
* Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
/**
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
* Use __system_property_read_callback() to query the current value.
*
* Property lookup is expensive, so it can be useful to cache the result of this
* function rather than using __system_property_get().
*/
void __system_property_read_callback(const prop_info* __pi,
void (*__callback)(void* __cookie, const char* __name, const char* __value, uint32_t __serial),
void* __cookie) __INTRODUCED_IN(26);
const prop_info* _Nullable __system_property_find(const char* _Nonnull __name);
/*
/**
* Calls `callback` with a consistent trio of name, value, and serial number
* for property `pi`.
*
* Available since API level 26.
*/
void __system_property_read_callback(const prop_info* _Nonnull __pi,
void (* _Nonnull __callback)(void* _Nullable __cookie, const char* _Nonnull __name, const char* _Nonnull __value, uint32_t __serial),
void* _Nullable __cookie) __INTRODUCED_IN(26);
/**
* Passes a `prop_info` for each system property to the provided
* callback. Use __system_property_read_callback() to read the value.
* callback. Use __system_property_read_callback() to read the value of
* any of the properties.
*
* This method is for inspecting and debugging the property system, and not generally useful.
*
* Returns 0 on success, or -1 on failure.
*/
int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __cookie), void* __cookie)
__INTRODUCED_IN(19);
int __system_property_foreach(void (* _Nonnull __callback)(const prop_info* _Nonnull __pi, void* _Nullable __cookie), void* _Nullable __cookie);
/*
/**
* Waits for the specific system property identified by `pi` to be updated
* past `old_serial`. Waits no longer than `relative_timeout`, or forever
* if `relaive_timeout` is null.
* if `relative_timeout` is null.
*
* If `pi` is null, waits for the global serial number instead.
*
@ -80,20 +97,24 @@ int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __
*
* Returns true and updates `*new_serial_ptr` on success, or false if the call
* timed out.
*
* Available since API level 26.
*/
struct timespec;
bool __system_property_wait(const prop_info* __pi, uint32_t __old_serial, uint32_t* __new_serial_ptr, const struct timespec* __relative_timeout)
bool __system_property_wait(const prop_info* _Nullable __pi, uint32_t __old_serial, uint32_t* _Nonnull __new_serial_ptr, const struct timespec* _Nullable __relative_timeout)
__INTRODUCED_IN(26);
/* Deprecated. In Android O and above, there's no limit on property name length. */
/**
* Deprecated: there's no limit on the length of a property name since
* API level 26, though the limit on property values (PROP_VALUE_MAX) remains.
*/
#define PROP_NAME_MAX 32
/* Deprecated. Use __system_property_read_callback instead. */
int __system_property_read(const prop_info* __pi, char* __name, char* __value);
/* Deprecated. Use __system_property_read_callback instead. */
int __system_property_get(const char* __name, char* __value);
/* Deprecated. Use __system_property_foreach instead. */
const prop_info* __system_property_find_nth(unsigned __n);
/** Deprecated. Use __system_property_read_callback() instead. */
int __system_property_read(const prop_info* _Nonnull __pi, char* _Nullable __name, char* _Nonnull __value);
/** Deprecated. Use __system_property_read_callback() instead. */
int __system_property_get(const char* _Nonnull __name, char* _Nonnull __value);
/** Deprecated. Use __system_property_foreach() instead. */
const prop_info* _Nullable __system_property_find_nth(unsigned __n);
__END_DECLS
#endif

View File

@ -40,12 +40,12 @@ static constexpr uintptr_t align_up(uintptr_t p, size_t align) {
}
template <typename T>
static inline T* align_down(T* p, size_t align) {
static inline T* _Nonnull align_down(T* _Nonnull p, size_t align) {
return reinterpret_cast<T*>(align_down(reinterpret_cast<uintptr_t>(p), align));
}
template <typename T>
static inline T* align_up(T* p, size_t align) {
static inline T* _Nonnull align_up(T* _Nonnull p, size_t align) {
return reinterpret_cast<T*>(align_up(reinterpret_cast<uintptr_t>(p), align));
}
@ -94,6 +94,6 @@ static inline uintptr_t untag_address(uintptr_t p) {
}
template <typename T>
static inline T* untag_address(T* p) {
static inline T* _Nonnull untag_address(T* _Nonnull p) {
return reinterpret_cast<T*>(untag_address(reinterpret_cast<uintptr_t>(p)));
}

View File

@ -20,6 +20,8 @@
#include <stdint.h>
#include <stdlib.h>
static constexpr char PROP_TREE_FILE[] = "/dev/__properties__/property_info";
namespace android {
namespace properties {

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

@ -53,14 +53,14 @@
// +-----+ +-----+ +-----+ +===========+
// Represents a node in the trie.
struct prop_bt {
struct prop_trie_node {
uint32_t namelen;
// The property trie is updated only by the init process (single threaded) which provides
// property service. And it can be read by multiple threads at the same time.
// As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
// left, right, children "pointers" in the trie node. To make sure readers who see the
// change of "pointers" can also notice the change of prop_bt structure contents pointed by
// change of "pointers" can also notice the change of prop_trie_node structure contents pointed by
// the "pointers", we always use release-consume ordering pair when accessing these "pointers".
// prop "points" to prop_info structure if there is a propery associated with the trie node.
@ -79,14 +79,14 @@ struct prop_bt {
char name[0];
prop_bt(const char* name, const uint32_t name_length) {
prop_trie_node(const char* name, const uint32_t name_length) {
this->namelen = name_length;
memcpy(this->name, name, name_length);
this->name[name_length] = '\0';
}
private:
BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_bt);
BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_trie_node);
};
class prop_area {
@ -105,7 +105,7 @@ class prop_area {
atomic_init(&serial_, 0u);
memset(reserved_, 0, sizeof(reserved_));
// Allocate enough space for the root node.
bytes_used_ = sizeof(prop_bt);
bytes_used_ = sizeof(prop_trie_node);
// To make property reads wait-free, we reserve a
// PROP_VALUE_MAX-sized block of memory, the "dirty backup area",
// just after the root node. When we're about to modify a
@ -136,30 +136,29 @@ class prop_area {
uint32_t version() const {
return version_;
}
char* dirty_backup_area() {
return data_ + sizeof (prop_bt);
}
char* dirty_backup_area() { return data_ + sizeof(prop_trie_node); }
private:
static prop_area* map_fd_ro(const int fd);
void* allocate_obj(const size_t size, uint_least32_t* const off);
prop_bt* new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off);
prop_trie_node* new_prop_trie_node(const char* name, uint32_t namelen, uint_least32_t* const off);
prop_info* new_prop_info(const char* name, uint32_t namelen, const char* value, uint32_t valuelen,
uint_least32_t* const off);
void* to_prop_obj(uint_least32_t off);
prop_bt* to_prop_bt(atomic_uint_least32_t* off_p);
prop_trie_node* to_prop_trie_node(atomic_uint_least32_t* off_p);
prop_info* to_prop_info(atomic_uint_least32_t* off_p);
prop_bt* root_node();
prop_trie_node* root_node();
prop_bt* find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen, bool alloc_if_needed);
prop_trie_node* find_prop_trie_node(prop_trie_node* const trie, const char* name,
uint32_t namelen, bool alloc_if_needed);
const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen,
const prop_info* find_property(prop_trie_node* const trie, const char* name, uint32_t namelen,
const char* value, uint32_t valuelen, bool alloc_if_needed);
bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie),
void* cookie);
bool foreach_property(prop_trie_node* const trie,
void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
// The original design doesn't include pa_size or pa_data_size in the prop_area struct itself.
// Since we'll need to be backwards compatible with that design, we don't gain much by adding it

View File

@ -45,14 +45,14 @@ struct prop_info {
// Read only properties will not set anything but the bottom most bit of serial and the top byte.
// We borrow the 2nd from the top byte for extra flags, and use the bottom most bit of that for
// our first user, kLongFlag.
constexpr static uint32_t kLongFlag = 1 << 16;
static constexpr uint32_t kLongFlag = 1 << 16;
// The error message fits in part of a union with the previous 92 char property value so there
// must be room left over after the error message for the offset to the new longer property value
// and future expansion fields if needed. Note that this value cannot ever increase. The offset
// to the new longer property value appears immediately after it, so an increase of this size will
// break compatibility.
constexpr static size_t kLongLegacyErrorBufferSize = 56;
static constexpr size_t kLongLegacyErrorBufferSize = 56;
public:
atomic_uint_least32_t serial;

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,8 +36,6 @@
#include "contexts_serialized.h"
#include "contexts_split.h"
constexpr int PROP_FILENAME_MAX = 1024;
class SystemProperties {
public:
friend struct LocalPropertyTestState;
@ -55,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);
@ -83,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_;
char property_filename_[PROP_FILENAME_MAX];
PropertiesFilename properties_filename_;
PropertiesFilename appcompat_filename_;
};

View File

@ -154,16 +154,15 @@ void* prop_area::allocate_obj(const size_t size, uint_least32_t* const off) {
return data_ + *off;
}
prop_bt* prop_area::new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off) {
prop_trie_node* prop_area::new_prop_trie_node(const char* name, uint32_t namelen,
uint_least32_t* const off) {
uint_least32_t new_offset;
void* const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
if (p != nullptr) {
prop_bt* bt = new (p) prop_bt(name, namelen);
*off = new_offset;
return bt;
}
void* const p = allocate_obj(sizeof(prop_trie_node) + namelen + 1, &new_offset);
if (p == nullptr) return nullptr;
return nullptr;
prop_trie_node* node = new (p) prop_trie_node(name, namelen);
*off = new_offset;
return node;
}
prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value,
@ -200,9 +199,9 @@ void* prop_area::to_prop_obj(uint_least32_t off) {
return (data_ + off);
}
inline prop_bt* prop_area::to_prop_bt(atomic_uint_least32_t* off_p) {
inline prop_trie_node* prop_area::to_prop_trie_node(atomic_uint_least32_t* off_p) {
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
return reinterpret_cast<prop_bt*>(to_prop_obj(off));
return reinterpret_cast<prop_trie_node*>(to_prop_obj(off));
}
inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
@ -210,8 +209,8 @@ inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
return reinterpret_cast<prop_info*>(to_prop_obj(off));
}
inline prop_bt* prop_area::root_node() {
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
inline prop_trie_node* prop_area::root_node() {
return reinterpret_cast<prop_trie_node*>(to_prop_obj(0));
}
static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uint32_t two_len) {
@ -223,9 +222,9 @@ static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uin
return strncmp(one, two, one_len);
}
prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen,
bool alloc_if_needed) {
prop_bt* current = bt;
prop_trie_node* prop_area::find_prop_trie_node(prop_trie_node* const trie, const char* name,
uint32_t namelen, bool alloc_if_needed) {
prop_trie_node* current = trie;
while (true) {
if (!current) {
return nullptr;
@ -239,46 +238,46 @@ prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t n
if (ret < 0) {
uint_least32_t left_offset = atomic_load_explicit(&current->left, memory_order_relaxed);
if (left_offset != 0) {
current = to_prop_bt(&current->left);
current = to_prop_trie_node(&current->left);
} else {
if (!alloc_if_needed) {
return nullptr;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
prop_trie_node* new_node = new_prop_trie_node(name, namelen, &new_offset);
if (new_node) {
atomic_store_explicit(&current->left, new_offset, memory_order_release);
}
return new_bt;
return new_node;
}
} else {
uint_least32_t right_offset = atomic_load_explicit(&current->right, memory_order_relaxed);
if (right_offset != 0) {
current = to_prop_bt(&current->right);
current = to_prop_trie_node(&current->right);
} else {
if (!alloc_if_needed) {
return nullptr;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
prop_trie_node* new_node = new_prop_trie_node(name, namelen, &new_offset);
if (new_node) {
atomic_store_explicit(&current->right, new_offset, memory_order_release);
}
return new_bt;
return new_node;
}
}
}
}
const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen,
const char* value, uint32_t valuelen,
const prop_info* prop_area::find_property(prop_trie_node* const trie, const char* name,
uint32_t namelen, const char* value, uint32_t valuelen,
bool alloc_if_needed) {
if (!trie) return nullptr;
const char* remaining_name = name;
prop_bt* current = trie;
prop_trie_node* current = trie;
while (true) {
const char* sep = strchr(remaining_name, '.');
const bool want_subtree = (sep != nullptr);
@ -288,13 +287,13 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
return nullptr;
}
prop_bt* root = nullptr;
prop_trie_node* root = nullptr;
uint_least32_t children_offset = atomic_load_explicit(&current->children, memory_order_relaxed);
if (children_offset != 0) {
root = to_prop_bt(&current->children);
root = to_prop_trie_node(&current->children);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
root = new_prop_bt(remaining_name, substr_size, &new_offset);
root = new_prop_trie_node(remaining_name, substr_size, &new_offset);
if (root) {
atomic_store_explicit(&current->children, new_offset, memory_order_release);
}
@ -304,7 +303,7 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
return nullptr;
}
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
current = find_prop_trie_node(root, remaining_name, substr_size, alloc_if_needed);
if (!current) {
return nullptr;
}
@ -330,13 +329,13 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
}
}
bool prop_area::foreach_property(prop_bt* const trie,
bool prop_area::foreach_property(prop_trie_node* const trie,
void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
if (!trie) return false;
uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
if (left_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
const int err = foreach_property(to_prop_trie_node(&trie->left), propfn, cookie);
if (err < 0) return false;
}
uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
@ -347,12 +346,12 @@ bool prop_area::foreach_property(prop_bt* const trie,
}
uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
if (children_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
const int err = foreach_property(to_prop_trie_node(&trie->children), propfn, cookie);
if (err < 0) return false;
}
uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
if (right_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
const int err = foreach_property(to_prop_trie_node(&trie->right), propfn, cookie);
if (err < 0) return false;
}

View File

@ -30,7 +30,7 @@
#include <string.h>
constexpr static const char kLongLegacyError[] =
static constexpr const char kLongLegacyError[] =
"Must use __system_property_read_callback() to read";
static_assert(sizeof(kLongLegacyError) < prop_info::kLongLegacyErrorBufferSize,
"Error message for long properties read by legacy libc must fit within 56 chars");

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;
@ -67,47 +70,81 @@ bool SystemProperties::Init(const char* filename) {
return true;
}
if (strlen(filename) >= PROP_FILENAME_MAX) {
properties_filename_ = filename;
if (!InitContexts(false)) {
return false;
}
strcpy(property_filename_, filename);
if (is_dir(property_filename_)) {
if (access("/dev/__properties__/property_info", R_OK) == 0) {
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
initialized_ = true;
return true;
}
bool SystemProperties::InitContexts(bool load_default_path) {
if (is_dir(properties_filename_.c_str())) {
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 {
contexts_ = new (contexts_data_) ContextsSplit();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
return false;
}
}
} else {
contexts_ = new (contexts_data_) ContextsPreSplit();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
return false;
}
}
initialized_ = true;
return true;
}
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
if (strlen(filename) >= PROP_FILENAME_MAX) {
return false;
}
strcpy(property_filename_, filename);
return AreaInit(filename, fsetxattr_failed, false);
}
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
// 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;
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;
}
appcompat_filename_ = PropertiesFilename(properties_filename_.c_str(), "appcompat_override");
appcompat_override_contexts_ = nullptr;
if (access(appcompat_filename_.c_str(), F_OK) != -1) {
auto* appcompat_contexts = new (appcompat_override_contexts_data_) ContextsSerialized();
if (!appcompat_contexts->Initialize(true, appcompat_filename_.c_str(), fsetxattr_failed,
load_default_path)) {
// The appcompat folder exists, but initializing it failed
return false;
} else {
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;
@ -136,6 +173,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;
}
@ -234,16 +275,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);
@ -253,18 +302,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;
@ -272,34 +337,73 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
if (namelen < 1) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: name length 0");
return -1;
}
if (namelen < 1) {
if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: \"%s\" value too long: %d >= PROP_VALUE_MAX",
name, valuelen);
return -1;
}
if (!initialized_) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: properties not initialized");
return -1;
}
prop_area* serial_pa = contexts_->GetSerialPropArea();
if (serial_pa == nullptr) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: property area not found");
return -1;
}
prop_area* pa = contexts_->GetPropAreaForName(name);
if (!pa) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: access denied for \"%s\"", name);
return -1;
}
bool ret = pa->add(name, namelen, value, valuelen);
if (!ret) {
if (!pa->add(name, namelen, value, valuelen)) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"__system_property_add failed: add failed for \"%s\"", name);
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

@ -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

@ -49,21 +49,34 @@
#include "private/ScopedFd.h"
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char property_service_for_system_socket[] =
"/dev/socket/" PROP_SERVICE_FOR_SYSTEM_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";
class PropertyServiceConnection {
public:
PropertyServiceConnection() : last_error_(0) {
PropertyServiceConnection(const char* name) : last_error_(0) {
socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
if (socket_.get() == -1) {
last_error_ = errno;
return;
}
const size_t namelen = strlen(property_service_socket);
// If we're trying to set "sys.powerctl" from a privileged process, use the special
// socket. Because this socket is only accessible to privileged processes, it can't
// be DoSed directly by malicious apps. (The shell user should be able to reboot,
// though, so we don't just always use the special socket for "sys.powerctl".)
// See b/262237198 for context
const char* socket = property_service_socket;
if (strcmp(name, "sys.powerctl") == 0 &&
access(property_service_for_system_socket, W_OK) == 0) {
socket = property_service_for_system_socket;
}
const size_t namelen = strlen(socket);
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
strlcpy(addr.sun_path, socket, sizeof(addr.sun_path));
addr.sun_family = AF_LOCAL;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
@ -176,7 +189,7 @@ struct prop_msg {
};
static int send_prop_msg(const prop_msg* msg) {
PropertyServiceConnection connection;
PropertyServiceConnection connection(msg->name);
if (!connection.IsValid()) {
return connection.GetLastError();
}
@ -244,6 +257,21 @@ static void detect_protocol_version() {
}
}
static const char* __prop_error_to_string(int error) {
switch (error) {
case PROP_ERROR_READ_CMD: return "PROP_ERROR_READ_CMD";
case PROP_ERROR_READ_DATA: return "PROP_ERROR_READ_DATA";
case PROP_ERROR_READ_ONLY_PROPERTY: return "PROP_ERROR_READ_ONLY_PROPERTY";
case PROP_ERROR_INVALID_NAME: return "PROP_ERROR_INVALID_NAME";
case PROP_ERROR_INVALID_VALUE: return "PROP_ERROR_INVALID_VALUE";
case PROP_ERROR_PERMISSION_DENIED: return "PROP_ERROR_PERMISSION_DENIED";
case PROP_ERROR_INVALID_CMD: return "PROP_ERROR_INVALID_CMD";
case PROP_ERROR_HANDLE_CONTROL_MESSAGE: return "PROP_ERROR_HANDLE_CONTROL_MESSAGE";
case PROP_ERROR_SET_FAILED: return "PROP_ERROR_SET_FAILED";
}
return "<unknown>";
}
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_set(const char* key, const char* value) {
if (key == nullptr) return -1;
@ -269,13 +297,12 @@ int __system_property_set(const char* key, const char* value) {
// New protocol only allows long values for ro. properties only.
if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
// Use proper protocol
PropertyServiceConnection connection;
PropertyServiceConnection connection(key);
if (!connection.IsValid()) {
errno = connection.GetLastError();
async_safe_format_log(
ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
errno, strerror(errno));
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": connection failed: %m", key,
value);
return -1;
}
@ -283,8 +310,8 @@ int __system_property_set(const char* key, const char* value) {
if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
errno = connection.GetLastError();
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
key, value, errno, strerror(errno));
"Unable to set property \"%s\" to \"%s\": write failed: %m", key,
value);
return -1;
}
@ -292,15 +319,14 @@ int __system_property_set(const char* key, const char* value) {
if (!connection.RecvInt32(&result)) {
errno = connection.GetLastError();
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
key, value, errno, strerror(errno));
"Unable to set property \"%s\" to \"%s\": recv failed: %m", key, value);
return -1;
}
if (result != PROP_SUCCESS) {
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
result);
"Unable to set property \"%s\" to \"%s\": %s (0x%x)", key, value,
__prop_error_to_string(result), result);
return -1;
}