Revert "Revert "Remove limit of system property name length""
am: 16b2a4de14
Change-Id: I585a55769cdca1d7f9edee479b5ec94d8821b673
This commit is contained in:
commit
d91e7766ec
14 changed files with 393 additions and 73 deletions
|
@ -59,8 +59,10 @@
|
|||
#include "private/bionic_sdk_version.h"
|
||||
#include "private/libc_logging.h"
|
||||
|
||||
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
|
||||
#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
|
||||
|
||||
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
|
||||
static const char* kServiceVersionPropertyName = "ro.property_service.version";
|
||||
|
||||
/*
|
||||
* Properties are stored in a hybrid trie/binary tree structure.
|
||||
|
@ -81,8 +83,7 @@ static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
|
|||
|
||||
// Represents a node in the trie.
|
||||
struct prop_bt {
|
||||
uint8_t namelen;
|
||||
uint8_t reserved[3];
|
||||
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.
|
||||
|
@ -107,7 +108,7 @@ struct prop_bt {
|
|||
|
||||
char name[0];
|
||||
|
||||
prop_bt(const char *name, const uint8_t name_length) {
|
||||
prop_bt(const char *name, const uint32_t name_length) {
|
||||
this->namelen = name_length;
|
||||
memcpy(this->name, name, name_length);
|
||||
this->name[name_length] = '\0';
|
||||
|
@ -140,9 +141,9 @@ public:
|
|||
|
||||
private:
|
||||
void *allocate_obj(const size_t size, uint_least32_t *const off);
|
||||
prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
|
||||
prop_info *new_prop_info(const char *name, uint8_t namelen,
|
||||
const char *value, uint8_t valuelen,
|
||||
prop_bt *new_prop_bt(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);
|
||||
|
@ -151,11 +152,11 @@ private:
|
|||
prop_bt *root_node();
|
||||
|
||||
prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
|
||||
uint8_t namelen, bool alloc_if_needed);
|
||||
uint32_t namelen, bool alloc_if_needed);
|
||||
|
||||
const prop_info *find_property(prop_bt *const trie, const char *name,
|
||||
uint8_t namelen, const char *value,
|
||||
uint8_t valuelen, bool alloc_if_needed);
|
||||
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),
|
||||
|
@ -173,19 +174,20 @@ private:
|
|||
|
||||
struct prop_info {
|
||||
atomic_uint_least32_t serial;
|
||||
// we need to keep this buffer around because the property
|
||||
// value can be modified whereas name is constant.
|
||||
char value[PROP_VALUE_MAX];
|
||||
char name[0];
|
||||
|
||||
prop_info(const char *name, const uint8_t namelen, const char *value,
|
||||
const uint8_t valuelen) {
|
||||
prop_info(const char *name, uint32_t namelen, const char *value, uint32_t valuelen) {
|
||||
memcpy(this->name, name, namelen);
|
||||
this->name[namelen] = '\0';
|
||||
atomic_init(&this->serial, valuelen << 24);
|
||||
memcpy(this->value, value, valuelen);
|
||||
this->value[valuelen] = '\0';
|
||||
}
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(prop_info);
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(prop_info);
|
||||
};
|
||||
|
||||
struct find_nth_cookie {
|
||||
|
@ -358,7 +360,7 @@ 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, uint8_t namelen, uint_least32_t *const off)
|
||||
prop_bt *prop_area::new_prop_bt(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);
|
||||
|
@ -371,8 +373,8 @@ prop_bt *prop_area::new_prop_bt(const char *name, uint8_t namelen, uint_least32_
|
|||
return NULL;
|
||||
}
|
||||
|
||||
prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen,
|
||||
const char *value, uint8_t valuelen, uint_least32_t *const off)
|
||||
prop_info *prop_area::new_prop_info(const char *name, uint32_t namelen,
|
||||
const char *value, uint32_t valuelen, uint_least32_t *const off)
|
||||
{
|
||||
uint_least32_t new_offset;
|
||||
void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
|
||||
|
@ -408,8 +410,8 @@ inline prop_bt *prop_area::root_node()
|
|||
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
|
||||
}
|
||||
|
||||
static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
|
||||
uint8_t two_len)
|
||||
static int cmp_prop_name(const char *one, uint32_t one_len, const char *two,
|
||||
uint32_t two_len)
|
||||
{
|
||||
if (one_len < two_len)
|
||||
return -1;
|
||||
|
@ -420,7 +422,7 @@ static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
|
|||
}
|
||||
|
||||
prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
|
||||
uint8_t namelen, bool alloc_if_needed)
|
||||
uint32_t namelen, bool alloc_if_needed)
|
||||
{
|
||||
|
||||
prop_bt* current = bt;
|
||||
|
@ -471,7 +473,7 @@ prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
|
|||
}
|
||||
|
||||
const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
|
||||
uint8_t namelen, const char *value, uint8_t valuelen,
|
||||
uint32_t namelen, const char *value, uint32_t valuelen,
|
||||
bool alloc_if_needed)
|
||||
{
|
||||
if (!trie) return NULL;
|
||||
|
@ -481,7 +483,7 @@ const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
|
|||
while (true) {
|
||||
const char *sep = strchr(remaining_name, '.');
|
||||
const bool want_subtree = (sep != NULL);
|
||||
const uint8_t substr_size = (want_subtree) ?
|
||||
const uint32_t substr_size = (want_subtree) ?
|
||||
sep - remaining_name : strlen(remaining_name);
|
||||
|
||||
if (!substr_size) {
|
||||
|
@ -531,28 +533,93 @@ const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
|
|||
}
|
||||
}
|
||||
|
||||
static int send_prop_msg(const prop_msg *msg)
|
||||
{
|
||||
const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd == -1) {
|
||||
return -1;
|
||||
class PropertyServiceConnection {
|
||||
public:
|
||||
PropertyServiceConnection() : last_error_(0) {
|
||||
fd_ = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd_ == -1) {
|
||||
last_error_ = errno;
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t namelen = strlen(property_service_socket);
|
||||
|
||||
sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
|
||||
addr.sun_family = AF_LOCAL;
|
||||
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
|
||||
if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) {
|
||||
close(fd);
|
||||
return -1;
|
||||
|
||||
if (TEMP_FAILURE_RETRY(connect(fd_, reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
|
||||
close(fd_);
|
||||
fd_ = -1;
|
||||
last_error_ = errno;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsValid() {
|
||||
return fd_ != -1;
|
||||
}
|
||||
|
||||
int GetLastError() {
|
||||
return last_error_;
|
||||
}
|
||||
|
||||
bool SendUint32(uint32_t value) {
|
||||
int result = TEMP_FAILURE_RETRY(send(fd_, &value, sizeof(value), 0));
|
||||
return CheckSendRecvResult(result, sizeof(value));
|
||||
}
|
||||
|
||||
bool SendString(const char* value) {
|
||||
uint32_t valuelen = strlen(value);
|
||||
if (!SendUint32(valuelen)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
|
||||
int result = TEMP_FAILURE_RETRY(send(fd_, value, valuelen, 0));
|
||||
return CheckSendRecvResult(result, valuelen);
|
||||
}
|
||||
|
||||
bool RecvInt32(int32_t* value) {
|
||||
int result = TEMP_FAILURE_RETRY(recv(fd_, value, sizeof(*value), MSG_WAITALL));
|
||||
return CheckSendRecvResult(result, sizeof(*value));
|
||||
}
|
||||
|
||||
int GetFd() {
|
||||
return fd_;
|
||||
}
|
||||
|
||||
~PropertyServiceConnection() {
|
||||
if (fd_ != -1) {
|
||||
close(fd_);
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool CheckSendRecvResult(int result, int expected_len) {
|
||||
if (result == -1) {
|
||||
last_error_ = errno;
|
||||
} else if (result != expected_len) {
|
||||
last_error_ = -1;
|
||||
} else {
|
||||
last_error_ = 0;
|
||||
}
|
||||
|
||||
return last_error_ == 0;
|
||||
}
|
||||
|
||||
int fd_;
|
||||
int last_error_;
|
||||
};
|
||||
|
||||
static int send_prop_msg(const prop_msg* msg) {
|
||||
PropertyServiceConnection connection;
|
||||
if (!connection.IsValid()) {
|
||||
return connection.GetLastError();
|
||||
}
|
||||
|
||||
int result = -1;
|
||||
int fd = connection.GetFd();
|
||||
|
||||
const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
|
||||
if (num_bytes == sizeof(prop_msg)) {
|
||||
// We successfully wrote to the property server but now we
|
||||
// wait for the property server to finish its work. It
|
||||
|
@ -578,11 +645,13 @@ static int send_prop_msg(const prop_msg *msg)
|
|||
// ms so callers who do read-after-write can reliably see
|
||||
// what they've written. Most of the time.
|
||||
// TODO: fix the system properties design.
|
||||
__libc_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Property service has timed out while trying to set \"%s\" to \"%s\"",
|
||||
msg->name, msg->value);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -1124,51 +1193,136 @@ int __system_property_read(const prop_info *pi, char *name, char *value)
|
|||
atomic_thread_fence(memory_order_acquire);
|
||||
if (serial ==
|
||||
load_const_atomic(&(pi->serial), memory_order_relaxed)) {
|
||||
if (name != 0) {
|
||||
strcpy(name, pi->name);
|
||||
if (name != nullptr) {
|
||||
size_t namelen = strlcpy(name, pi->name, PROP_NAME_MAX);
|
||||
if(namelen >= PROP_NAME_MAX) {
|
||||
__libc_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"The property name length for \"%s\" is >= %d;"
|
||||
" please use __system_property_read_callback"
|
||||
" to read this property. (the name is truncated to \"%s\")",
|
||||
pi->name, PROP_NAME_MAX - 1, name);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __system_property_read_callback(const prop_info* pi,
|
||||
void (*callback)(void* cookie, const char* name, const char* value),
|
||||
void* cookie) {
|
||||
// TODO (dimitry): do we need compat mode for this function?
|
||||
if (__predict_false(compat_mode)) {
|
||||
char value_buf[PROP_VALUE_MAX];
|
||||
__system_property_read_compat(pi, nullptr, value_buf);
|
||||
callback(cookie, pi->name, value_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
uint32_t serial = __system_property_serial(pi); // acquire semantics
|
||||
size_t len = SERIAL_VALUE_LEN(serial);
|
||||
char value_buf[len+1];
|
||||
|
||||
memcpy(value_buf, pi->value, len);
|
||||
value_buf[len] = '\0';
|
||||
|
||||
// TODO: see todo in __system_property_read function
|
||||
atomic_thread_fence(memory_order_acquire);
|
||||
if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) {
|
||||
callback(cookie, pi->name, value_buf);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __system_property_get(const char *name, char *value)
|
||||
{
|
||||
const prop_info *pi = __system_property_find(name);
|
||||
|
||||
if (pi != 0) {
|
||||
return __system_property_read(pi, 0, value);
|
||||
return __system_property_read(pi, nullptr, value);
|
||||
} else {
|
||||
value[0] = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int __system_property_set(const char *key, const char *value)
|
||||
{
|
||||
if (key == 0) return -1;
|
||||
if (value == 0) value = "";
|
||||
if (strlen(key) >= PROP_NAME_MAX) return -1;
|
||||
static constexpr uint32_t kProtocolVersion1 = 1;
|
||||
static constexpr uint32_t kProtocolVersion2 = 2; // current
|
||||
|
||||
static atomic_uint_least32_t g_propservice_protocol_version = 0;
|
||||
|
||||
static void detect_protocol_version() {
|
||||
char value[PROP_VALUE_MAX];
|
||||
if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
|
||||
g_propservice_protocol_version = kProtocolVersion1;
|
||||
__libc_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Using old property service protocol (\"%s\" is not set)",
|
||||
kServiceVersionPropertyName);
|
||||
} else {
|
||||
uint32_t version = static_cast<uint32_t>(atoll(value));
|
||||
if (version >= kProtocolVersion2) {
|
||||
g_propservice_protocol_version = kProtocolVersion2;
|
||||
} else {
|
||||
__libc_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Using old property service protocol (\"%s\"=\"%s\")",
|
||||
kServiceVersionPropertyName, value);
|
||||
g_propservice_protocol_version = kProtocolVersion1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int __system_property_set(const char* key, const char* value) {
|
||||
if (key == nullptr) return -1;
|
||||
if (value == nullptr) value = "";
|
||||
if (strlen(value) >= PROP_VALUE_MAX) return -1;
|
||||
|
||||
prop_msg msg;
|
||||
memset(&msg, 0, sizeof msg);
|
||||
msg.cmd = PROP_MSG_SETPROP;
|
||||
strlcpy(msg.name, key, sizeof msg.name);
|
||||
strlcpy(msg.value, value, sizeof msg.value);
|
||||
|
||||
const int err = send_prop_msg(&msg);
|
||||
if (err < 0) {
|
||||
return err;
|
||||
if (g_propservice_protocol_version == 0) {
|
||||
detect_protocol_version();
|
||||
}
|
||||
|
||||
return 0;
|
||||
int result = -1;
|
||||
if (g_propservice_protocol_version == kProtocolVersion1) {
|
||||
// Old protocol does not support long names
|
||||
if (strlen(key) >= PROP_NAME_MAX) return -1;
|
||||
|
||||
prop_msg msg;
|
||||
memset(&msg, 0, sizeof msg);
|
||||
msg.cmd = PROP_MSG_SETPROP;
|
||||
strlcpy(msg.name, key, sizeof msg.name);
|
||||
strlcpy(msg.value, value, sizeof msg.value);
|
||||
|
||||
result = send_prop_msg(&msg);
|
||||
} else {
|
||||
// Use proper protocol
|
||||
PropertyServiceConnection connection;
|
||||
if (connection.IsValid() &&
|
||||
connection.SendUint32(PROP_MSG_SETPROP2) &&
|
||||
connection.SendString(key) &&
|
||||
connection.SendString(value) &&
|
||||
connection.RecvInt32(&result)) {
|
||||
if (result != PROP_SUCCESS) {
|
||||
__libc_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": error code: 0x%x",
|
||||
key, value, result);
|
||||
}
|
||||
} else {
|
||||
result = connection.GetLastError();
|
||||
__libc_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": error code: 0x%x (%s)",
|
||||
key, value, result, strerror(result));
|
||||
}
|
||||
}
|
||||
|
||||
return result != 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
int __system_property_update(prop_info *pi, const char *value, unsigned int len)
|
||||
{
|
||||
if (len >= PROP_VALUE_MAX)
|
||||
if (len >= PROP_VALUE_MAX) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
prop_area* pa = __system_property_area__;
|
||||
|
||||
|
@ -1183,7 +1337,8 @@ int __system_property_update(prop_info *pi, const char *value, unsigned int len)
|
|||
// used memory_order_relaxed atomics, and use the analogous
|
||||
// counterintuitive fence.
|
||||
atomic_thread_fence(memory_order_release);
|
||||
memcpy(pi->value, value, len + 1);
|
||||
strlcpy(pi->value, value, len + 1);
|
||||
|
||||
atomic_store_explicit(
|
||||
&pi->serial,
|
||||
(len << 24) | ((serial + 1) & 0xffffff),
|
||||
|
@ -1202,12 +1357,13 @@ int __system_property_update(prop_info *pi, const char *value, unsigned int len)
|
|||
int __system_property_add(const char *name, unsigned int namelen,
|
||||
const char *value, unsigned int valuelen)
|
||||
{
|
||||
if (namelen >= PROP_NAME_MAX)
|
||||
if (valuelen >= PROP_VALUE_MAX) {
|
||||
return -1;
|
||||
if (valuelen >= PROP_VALUE_MAX)
|
||||
return -1;
|
||||
if (namelen < 1)
|
||||
}
|
||||
|
||||
if (namelen < 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!__system_property_area__) {
|
||||
return -1;
|
||||
|
@ -1221,8 +1377,9 @@ int __system_property_add(const char *name, unsigned int namelen,
|
|||
}
|
||||
|
||||
bool ret = pa->add(name, namelen, value, valuelen);
|
||||
if (!ret)
|
||||
if (!ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// There is only a single mutator, but we want to make sure that
|
||||
// updates are visible to a reader waiting for the update.
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
#define TOC_NAME_LEN(toc) ((toc) >> 24)
|
||||
#define TOC_TO_INFO(area, toc) ((prop_info_compat*) (((char*) area) + ((toc) & 0xFFFFFF)))
|
||||
#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
|
||||
|
||||
struct prop_area_compat {
|
||||
unsigned volatile count;
|
||||
|
|
|
@ -48,7 +48,6 @@ typedef struct prop_msg prop_msg;
|
|||
|
||||
#define PA_SIZE (128 * 1024)
|
||||
|
||||
#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
|
||||
#define SERIAL_DIRTY(serial) ((serial) & 1)
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
@ -61,6 +60,18 @@ struct prop_msg
|
|||
};
|
||||
|
||||
#define PROP_MSG_SETPROP 1
|
||||
#define PROP_MSG_SETPROP2 0x00020001
|
||||
|
||||
#define PROP_SUCCESS 0
|
||||
#define PROP_ERROR_READ_CMD 0x0004
|
||||
#define PROP_ERROR_READ_DATA 0x0008
|
||||
#define PROP_ERROR_READ_ONLY_PROPERTY 0x000B
|
||||
#define PROP_ERROR_INVALID_NAME 0x0010
|
||||
#define PROP_ERROR_INVALID_VALUE 0x0014
|
||||
#define PROP_ERROR_PERMISSION_DENIED 0x0018
|
||||
#define PROP_ERROR_INVALID_CMD 0x001B
|
||||
#define PROP_ERROR_HANDLE_CONTROL_MESSAGE 0x0020
|
||||
#define PROP_ERROR_SET_FAILED 0x0024
|
||||
|
||||
/*
|
||||
** Rules:
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stddef.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
@ -64,13 +65,17 @@ const prop_info *__system_property_find(const char *name);
|
|||
/* Read the value of a system property. Returns the length
|
||||
** of the value. Copies the value and \0 terminator into
|
||||
** the provided value pointer. Total length (including
|
||||
** terminator) will be no greater that PROP_VALUE_MAX.
|
||||
** terminator) will be no greater that PROP_VALUE_MAX for
|
||||
** __system_property_read.
|
||||
**
|
||||
** If name is nonzero, up to PROP_NAME_MAX bytes will be
|
||||
** copied into the provided name pointer. The name will
|
||||
** be \0 terminated.
|
||||
*/
|
||||
int __system_property_read(const prop_info *pi, char *name, char *value);
|
||||
void __system_property_read_callback(const prop_info *pi,
|
||||
void (*)(void* cookie, const char *name, const char *value),
|
||||
void* cookie) __INTRODUCED_IN_FUTURE;
|
||||
|
||||
/* Return a prop_info for the nth system property, or NULL if
|
||||
** there is no nth property. Use __system_property_read() to
|
||||
|
|
|
@ -1265,6 +1265,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
bsd_signal; # arm x86 mips versioned=26
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
|
|
|
@ -1188,6 +1188,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
catopen; # future
|
||||
|
|
|
@ -1290,6 +1290,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
bsd_signal; # arm x86 mips versioned=26
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
|
|
|
@ -1249,6 +1249,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
bsd_signal; # arm x86 mips versioned=26
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
|
|
|
@ -1188,6 +1188,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
catopen; # future
|
||||
|
|
|
@ -1247,6 +1247,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
bsd_signal; # arm x86 mips versioned=26
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
|
|
|
@ -1188,6 +1188,7 @@ LIBC_N { # introduced-arm64=24 introduced-mips=24 introduced-mips64=24 introduce
|
|||
|
||||
LIBC_O {
|
||||
global:
|
||||
__system_property_read_callback; # future
|
||||
catclose; # future
|
||||
catgets; # future
|
||||
catopen; # future
|
||||
|
|
|
@ -132,6 +132,7 @@ cc_test_library {
|
|||
"sys_vfs_test.cpp",
|
||||
"sys_xattr_test.cpp",
|
||||
"system_properties_test.cpp",
|
||||
"system_properties_test2.cpp",
|
||||
"time_test.cpp",
|
||||
"uchar_test.cpp",
|
||||
"unistd_nofortify_test.cpp",
|
||||
|
|
|
@ -69,7 +69,7 @@ private:
|
|||
static void foreach_test_callback(const prop_info *pi, void* cookie) {
|
||||
size_t *count = static_cast<size_t *>(cookie);
|
||||
|
||||
ASSERT_NE((prop_info *)NULL, pi);
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
(*count)++;
|
||||
}
|
||||
|
||||
|
@ -98,16 +98,15 @@ static void hierarchical_test_callback(const prop_info *pi, void *cookie) {
|
|||
ok[name_i][name_j][name_k] = true;
|
||||
}
|
||||
|
||||
static void *PropertyWaitHelperFn(void *arg) {
|
||||
int *flag = (int *)arg;
|
||||
prop_info *pi;
|
||||
pi = (prop_info *)__system_property_find("property");
|
||||
static void* PropertyWaitHelperFn(void* arg) {
|
||||
int* flag = static_cast<int*>(arg);
|
||||
prop_info* pi = const_cast<prop_info*>(__system_property_find("property"));
|
||||
usleep(100000);
|
||||
|
||||
*flag = 1;
|
||||
__system_property_update(pi, "value3", 6);
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif // __BIONIC__
|
||||
|
@ -123,6 +122,16 @@ TEST(properties, add) {
|
|||
ASSERT_EQ(0, __system_property_add("other_property", 14, "value2", 6));
|
||||
ASSERT_EQ(0, __system_property_add("property_other", 14, "value3", 6));
|
||||
|
||||
// check that there is no limit on property name length
|
||||
char name[PROP_NAME_MAX + 11];
|
||||
name[0] = 'p';
|
||||
for (size_t i = 1; i < sizeof(name); i++) {
|
||||
name[i] = 'x';
|
||||
}
|
||||
|
||||
name[sizeof(name)-1] = '\0';
|
||||
ASSERT_EQ(0, __system_property_add(name, strlen(name), "value", 5));
|
||||
|
||||
ASSERT_EQ(6, __system_property_get("property", propvalue));
|
||||
ASSERT_STREQ(propvalue, "value1");
|
||||
|
||||
|
@ -131,6 +140,9 @@ TEST(properties, add) {
|
|||
|
||||
ASSERT_EQ(6, __system_property_get("property_other", propvalue));
|
||||
ASSERT_STREQ(propvalue, "value3");
|
||||
|
||||
ASSERT_EQ(5, __system_property_get(name, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value");
|
||||
#else // __BIONIC__
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif // __BIONIC__
|
||||
|
@ -323,7 +335,6 @@ TEST(properties, errors) {
|
|||
ASSERT_EQ(0, __system_property_find("property1"));
|
||||
ASSERT_EQ(0, __system_property_get("property1", prop_value));
|
||||
|
||||
ASSERT_EQ(-1, __system_property_add("name", PROP_NAME_MAX, "value", 5));
|
||||
ASSERT_EQ(-1, __system_property_add("name", 4, "value", PROP_VALUE_MAX));
|
||||
ASSERT_EQ(-1, __system_property_update(NULL, "value", PROP_VALUE_MAX));
|
||||
#else // __BIONIC__
|
||||
|
@ -359,12 +370,13 @@ TEST(properties, wait) {
|
|||
|
||||
ASSERT_EQ(0, __system_property_add("property", 8, "value1", 6));
|
||||
serial = __system_property_wait_any(0);
|
||||
pi = (prop_info *)__system_property_find("property");
|
||||
ASSERT_NE((prop_info *)NULL, pi);
|
||||
|
||||
pi = const_cast<prop_info*>(__system_property_find("property"));
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
__system_property_update(pi, "value2", 6);
|
||||
serial = __system_property_wait_any(serial);
|
||||
|
||||
ASSERT_EQ(0, pthread_create(&t, NULL, PropertyWaitHelperFn, &flag));
|
||||
ASSERT_EQ(0, pthread_create(&t, nullptr, PropertyWaitHelperFn, &flag));
|
||||
ASSERT_EQ(flag, 0);
|
||||
serial = __system_property_wait_any(serial);
|
||||
ASSERT_EQ(flag, 1);
|
||||
|
@ -396,9 +408,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;
|
||||
if (stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT) {
|
||||
return;
|
||||
}
|
||||
ASSERT_FALSE(stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT);
|
||||
|
||||
ASSERT_EXIT(__system_property_add("property", 8, "value", 5), KilledByFault(), "");
|
||||
#else // __BIONIC__
|
||||
|
|
128
tests/system_properties_test2.cpp
Normal file
128
tests/system_properties_test2.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 <gtest/gtest.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
static uint64_t NanoTime() {
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 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, smoke) {
|
||||
#if defined(__BIONIC__)
|
||||
char propvalue[PROP_VALUE_MAX];
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "debug.test." << getpid() << "." << NanoTime() << ".";
|
||||
const std::string property_prefix = ss.str();
|
||||
const std::string property_name = property_prefix + "property1";
|
||||
|
||||
// Set brand new property
|
||||
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1"));
|
||||
ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
|
||||
ASSERT_STREQ("value1", propvalue);
|
||||
|
||||
std::string long_value = "property-";
|
||||
for (size_t i = 0; i < PROP_VALUE_MAX; i++) {
|
||||
long_value += "y";
|
||||
}
|
||||
|
||||
// Make sure that attempts to set invalid property value fails and preserves
|
||||
// previous value.
|
||||
propvalue[0] = '\0';
|
||||
ASSERT_EQ(-1, __system_property_set(property_name.c_str(), long_value.c_str()));
|
||||
ASSERT_EQ(6, __system_property_get(property_name.c_str(), propvalue));
|
||||
ASSERT_STREQ("value1", propvalue);
|
||||
|
||||
// Update property
|
||||
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "value1-1"));
|
||||
ASSERT_EQ(8, __system_property_get(property_name.c_str(), propvalue));
|
||||
ASSERT_STREQ("value1-1", propvalue);
|
||||
|
||||
|
||||
// check that there is no limit on property name length
|
||||
char suffix[1024];
|
||||
for (size_t i = 0; i < sizeof(suffix); i++) {
|
||||
suffix[i] = 'x';
|
||||
}
|
||||
|
||||
suffix[sizeof(suffix)-1] = '\0';
|
||||
const std::string long_property_name = property_prefix + suffix;
|
||||
|
||||
ASSERT_EQ(0, __system_property_set(long_property_name.c_str(), "value2"));
|
||||
ASSERT_EQ(6, __system_property_get(long_property_name.c_str(), propvalue));
|
||||
ASSERT_STREQ("value2", propvalue);
|
||||
|
||||
// test find and read_callback
|
||||
const prop_info* pi = __system_property_find(property_name.c_str());
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
|
||||
std::string expected_name = property_name;
|
||||
__system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
|
||||
const std::string* expected_name = static_cast<const std::string*>(cookie);
|
||||
ASSERT_EQ(*expected_name, name);
|
||||
ASSERT_STREQ("value1-1", value);
|
||||
}, &expected_name);
|
||||
|
||||
pi = __system_property_find(long_property_name.c_str());
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
|
||||
expected_name = long_property_name;
|
||||
__system_property_read_callback(pi, [](void* cookie, const char *name, const char *value) {
|
||||
const std::string* expected_name = static_cast<const std::string*>(cookie);
|
||||
ASSERT_EQ(*expected_name, name);
|
||||
ASSERT_STREQ("value2", value);
|
||||
}, &expected_name);
|
||||
|
||||
// Check that read() for long names still works but returns truncated version of the name
|
||||
pi = __system_property_find(property_name.c_str());
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
char legacy_name[PROP_NAME_MAX];
|
||||
expected_name = std::string(property_name.c_str(), PROP_NAME_MAX-1);
|
||||
ASSERT_EQ(8, __system_property_read(pi, &legacy_name[0], propvalue));
|
||||
ASSERT_EQ(expected_name, legacy_name);
|
||||
ASSERT_STREQ("value1-1", propvalue);
|
||||
|
||||
const prop_info* pi_long = __system_property_find(long_property_name.c_str());
|
||||
ASSERT_TRUE(pi != nullptr);
|
||||
expected_name = std::string(long_property_name.c_str(), PROP_NAME_MAX-1);
|
||||
ASSERT_EQ(6, __system_property_read(pi_long, &legacy_name[0], propvalue));
|
||||
ASSERT_EQ(expected_name, legacy_name);
|
||||
ASSERT_STREQ("value2", propvalue);
|
||||
#else // __BIONIC__
|
||||
GTEST_LOG_(INFO) << "This test does nothing.\n";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
Loading…
Reference in a new issue