From f4c93b22518f8439f157db78c0c6fd1112a5033d Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 16 Apr 2017 23:05:39 +0800 Subject: [PATCH] Update resetprop for Android O support Updated to upstream https://android.googlesource.com/platform/bionic.git --- jni/resetprop/ErrnoRestorer.h | 43 + jni/resetprop/LICENSE | 339 ----- jni/resetprop/_system_properties.h | 94 +- jni/resetprop/system_properties.cpp | 2165 +++++++++++++++------------ jni/resetprop/system_properties.h | 102 ++ 5 files changed, 1345 insertions(+), 1398 deletions(-) create mode 100644 jni/resetprop/ErrnoRestorer.h delete mode 100644 jni/resetprop/LICENSE create mode 100644 jni/resetprop/system_properties.h diff --git a/jni/resetprop/ErrnoRestorer.h b/jni/resetprop/ErrnoRestorer.h new file mode 100644 index 000000000..f4673936a --- /dev/null +++ b/jni/resetprop/ErrnoRestorer.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ERRNO_RESTORER_H +#define ERRNO_RESTORER_H + +#include + +#include "bionic_macros.h" + +class ErrnoRestorer { + public: + explicit ErrnoRestorer() : saved_errno_(errno) { + } + + ~ErrnoRestorer() { + errno = saved_errno_; + } + + void override(int new_errno) { + saved_errno_ = new_errno; + } + + private: + int saved_errno_; + + DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer); +}; + +#endif // ERRNO_RESTORER_H diff --git a/jni/resetprop/LICENSE b/jni/resetprop/LICENSE deleted file mode 100644 index 23cb79033..000000000 --- a/jni/resetprop/LICENSE +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {description} - Copyright (C) {year} {fullname} - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - {signature of Ty Coon}, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/jni/resetprop/_system_properties.h b/jni/resetprop/_system_properties.h index e82c3c5ca..73baf8729 100644 --- a/jni/resetprop/_system_properties.h +++ b/jni/resetprop/_system_properties.h @@ -29,61 +29,34 @@ #ifndef _INCLUDE_SYS__SYSTEM_PROPERTIES_H #define _INCLUDE_SYS__SYSTEM_PROPERTIES_H +#include +#include + #ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #error you should #include instead -#else -#include +#endif -typedef struct prop_msg prop_msg; - -#define PROP_AREA_MAGIC 0x504f5250 -#define PROP_AREA_VERSION 0xfc6ed0ab -#define PROP_AREA_VERSION_COMPAT 0x45434f76 - -#define PROP_SERVICE_NAME "property_service" -#define PROP_FILENAME_MAX 1024 -#define PROP_FILENAME "/dev/__properties__" - -#define PA_SIZE (128 * 1024) - -#define SERIAL_VALUE_LEN(serial) ((serial) >> 24) -#define SERIAL_DIRTY(serial) ((serial) & 1) +// #include +#include "system_properties.h" __BEGIN_DECLS -struct prop_msg -{ - unsigned cmd; - char name[PROP_NAME_MAX]; - char value[PROP_VALUE_MAX]; -}; +#define PROP_SERVICE_NAME "property_service" +#define PROP_FILENAME "/dev/__properties__" #define PROP_MSG_SETPROP 1 +#define PROP_MSG_SETPROP2 0x00020001 -/* -** Rules: -** -** - there is only one writer, but many readers -** - prop_area.count will never decrease in value -** - once allocated, a prop_info's name will not change -** - once allocated, a prop_info's offset will not change -** - reading a value requires the following steps -** 1. serial = pi->serial -** 2. if SERIAL_DIRTY(serial), wait*, then goto 1 -** 3. memcpy(local, pi->value, SERIAL_VALUE_LEN(serial) + 1) -** 4. if pi->serial != serial, goto 2 -** -** - writing a value requires the following steps -** 1. pi->serial = pi->serial | 1 -** 2. memcpy(pi->value, local_value, value_len) -** 3. pi->serial = (value_len << 24) | ((pi->serial + 1) & 0xffffff) -*/ - -#define PROP_PATH_RAMDISK_DEFAULT "/default.prop" -#define PROP_PATH_SYSTEM_BUILD "/system/build.prop" -#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop" -#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" -#define PROP_PATH_FACTORY "/factory/factory.prop" +#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 /* ** Map the property area from the specified filename. This @@ -120,7 +93,7 @@ int __system_property_area_init(); ** ** Returns the serial number on success, -1 on error. */ -unsigned int __system_property_area_serial(); +uint32_t __system_property_area_serial(); /* Add a new system property. Can only be done by a single ** process that has write access to the property area, and @@ -130,9 +103,12 @@ unsigned int __system_property_area_serial(); ** ** Returns 0 on success, -1 if the property area is full. */ -int __system_property_add(const char *name, unsigned int namelen, - const char *value, unsigned int valuelen); +int __system_property_add(const char *name, unsigned int namelen, const char *value, unsigned int valuelen); +/* Delete a new system property. Added in resetprop +** +** Returns 0 on success, -1 if the property area is full. +*/ int __system_property_del(const char *name); /* Update the value of a system property returned by @@ -150,21 +126,7 @@ int __system_property_update(prop_info *pi, const char *value, unsigned int len) ** ** Returns the serial number on success, -1 on error. */ -unsigned int __system_property_serial(const prop_info *pi); - -/* Wait for any system property to be updated. Caller must pass -** in 0 the first time, and the previous return value on each -** successive call. */ -unsigned int __system_property_wait_any(unsigned int serial); - -/* Compatibility functions to support using an old init with a new libc, - ** mostly for the OTA updater binary. These can be deleted once OTAs from - ** a pre-K release no longer needed to be supported. */ -// const prop_info *__system_property_find_compat(const char *name); -// int __system_property_read_compat(const prop_info *pi, char *name, char *value); -// int __system_property_foreach_compat( -// void (*propfn)(const prop_info *pi, void *cookie), -// void *cookie); +uint32_t __system_property_serial(const prop_info* pi); /* Initialize the system properties area in read only mode. * Should be done by all processes that need to read system @@ -174,7 +136,9 @@ unsigned int __system_property_wait_any(unsigned int serial); */ int __system_properties_init(); +/* Deprecated: use __system_property_wait instead. */ +uint32_t __system_property_wait_any(uint32_t old_serial); + __END_DECLS #endif -#endif diff --git a/jni/resetprop/system_properties.cpp b/jni/resetprop/system_properties.cpp index 03e503a9e..2c26bc5c7 100644 --- a/jni/resetprop/system_properties.cpp +++ b/jni/resetprop/system_properties.cpp @@ -25,6 +25,7 @@ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ + #include #include #include @@ -33,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -46,20 +46,32 @@ #include #include #include +#include #include #include #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_ #include "_system_properties.h" -#include +#include "system_properties.h" +#include "ErrnoRestorer.h" #include "bionic_futex.h" #include "bionic_lock.h" #include "bionic_macros.h" #include "libc_logging.h" -static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME; +static constexpr int PROP_FILENAME_MAX = 1024; +static constexpr uint32_t PROP_AREA_MAGIC = 0x504f5250; +static constexpr uint32_t PROP_AREA_VERSION = 0xfc6ed0ab; + +static constexpr size_t PA_SIZE = 128 * 1024; + +#define SERIAL_DIRTY(serial) ((serial)&1) +#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. @@ -80,718 +92,764 @@ 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. - // 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 - // the "pointers", we always use release-consume ordering pair when accessing these "pointers". + // 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 + // 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. - // Its situation is similar to the left, right, children "pointers". So we use - // atomic_uint_least32_t and release-consume ordering to protect it as well. + // prop "points" to prop_info structure if there is a propery associated with the trie node. + // Its situation is similar to the left, right, children "pointers". So we use + // atomic_uint_least32_t and release-consume ordering to protect it as well. - // We should also avoid rereading these fields redundantly, since not - // all processor implementations ensure that multiple loads from the - // same field are carried out in the right order. - atomic_uint_least32_t prop; + // We should also avoid rereading these fields redundantly, since not + // all processor implementations ensure that multiple loads from the + // same field are carried out in the right order. + atomic_uint_least32_t prop; - atomic_uint_least32_t left; - atomic_uint_least32_t right; + atomic_uint_least32_t left; + atomic_uint_least32_t right; - atomic_uint_least32_t children; + atomic_uint_least32_t children; - char name[0]; + char name[0]; - prop_bt(const char *name, const uint8_t name_length) { - this->namelen = name_length; - memcpy(this->name, name, name_length); - this->name[name_length] = '\0'; - } + 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'; + } -private: - DISALLOW_COPY_AND_ASSIGN(prop_bt); + private: + DISALLOW_COPY_AND_ASSIGN(prop_bt); }; class prop_area { -public: + public: + prop_area(const uint32_t magic, const uint32_t version) : magic_(magic), version_(version) { + atomic_init(&serial_, 0); + memset(reserved_, 0, sizeof(reserved_)); + // Allocate enough space for the root node. + bytes_used_ = sizeof(prop_bt); + } - prop_area(const uint32_t magic, const uint32_t version) : - magic_(magic), version_(version) { - atomic_init(&serial_, 0); - memset(reserved_, 0, sizeof(reserved_)); - // Allocate enough space for the root node. - bytes_used_ = sizeof(prop_bt); - } + const prop_info* find(const char* name); + bool del(const char *name); // resetprop add + bool add(const char* name, unsigned int namelen, const char* value, unsigned int valuelen); - const prop_info *find(const char *name); - bool add(const char *name, unsigned int namelen, - const char *value, unsigned int valuelen); - const prop_info *del(const char *name); + bool foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie); - bool foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie); + atomic_uint_least32_t* serial() { + return &serial_; + } + uint32_t magic() const { + return magic_; + } + uint32_t version() const { + return version_; + } - atomic_uint_least32_t *serial() { return &serial_; } - uint32_t magic() const { return magic_; } - uint32_t version() const { return version_; } + private: + 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_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_info* to_prop_info(atomic_uint_least32_t* off_p); -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, - 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_info *to_prop_info(atomic_uint_least32_t *off_p); + prop_bt* root_node(); - prop_bt *root_node(); + prop_bt* find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen, bool alloc_if_needed); - prop_bt *find_prop_bt(prop_bt *const bt, const char *name, - uint8_t namelen, bool alloc_if_needed); + const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen, + const char* value, uint32_t valuelen, 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); + bool find_property_and_del(prop_bt *const trie, const char *name); // resetprop add - const prop_info *find_property_and_del(prop_bt *const trie, const char *name); + bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie), + void* cookie); - bool foreach_property(prop_bt *const trie, - void (*propfn)(const prop_info *pi, void *cookie), - void *cookie); + uint32_t bytes_used_; + atomic_uint_least32_t serial_; + uint32_t magic_; + uint32_t version_; + uint32_t reserved_[28]; + char data_[0]; - uint32_t bytes_used_; - atomic_uint_least32_t serial_; - uint32_t magic_; - uint32_t version_; - uint32_t reserved_[28]; - char data_[0]; - - DISALLOW_COPY_AND_ASSIGN(prop_area); + DISALLOW_COPY_AND_ASSIGN(prop_area); }; struct prop_info { - atomic_uint_least32_t serial; - char value[PROP_VALUE_MAX]; - char name[0]; + 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) { - 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); + 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_IMPLICIT_CONSTRUCTORS(prop_info); }; struct find_nth_cookie { - uint32_t count; - const uint32_t n; - const prop_info *pi; + uint32_t count; + const uint32_t n; + const prop_info* pi; - find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) { - } + explicit find_nth_cookie(uint32_t n) : count(0), n(n), pi(nullptr) { + } }; +// This is public because it was exposed in the NDK. As of 2017-01, ~60 apps reference this symbol. +prop_area* __system_property_area__ = nullptr; + static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME; -static bool compat_mode = false; static size_t pa_data_size; static size_t pa_size; static bool initialized = false; -// NOTE: This isn't static because system_properties_compat.c -// requires it. -prop_area *__system_property_area__ = NULL; - -static int get_fd_from_env(void) -{ - // This environment variable consistes of two decimal integer - // values separated by a ",". The first value is a file descriptor - // and the second is the size of the system properties area. The - // size is currently unused. - char *env = getenv("ANDROID_PROPERTY_WORKSPACE"); - - if (!env) { - return -1; - } - - return atoi(env); -} - static prop_area* map_prop_area_rw(const char* filename, const char* context, bool* fsetxattr_failed) { - /* dev is a tmpfs that we can use to carve a shared workspace - * out of, so let's do that... - */ - const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); + /* dev is a tmpfs that we can use to carve a shared workspace + * out of, so let's do that... + */ + const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); - if (fd < 0) { - if (errno == EACCES) { - /* for consistency with the case where the process has already - * mapped the page in and segfaults when trying to write to it - */ - abort(); - } - return nullptr; + if (fd < 0) { + if (errno == EACCES) { + /* for consistency with the case where the process has already + * mapped the page in and segfaults when trying to write to it + */ + abort(); } + return nullptr; + } - if (context) { - if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) { - __libc_format_log(ANDROID_LOG_ERROR, "libc", - "fsetxattr failed to set context (%s) for \"%s\"", context, filename); - /* - * fsetxattr() will fail during system properties tests due to selinux policy. - * We do not want to create a custom policy for the tester, so we will continue in - * this function but set a flag that an error has occurred. - * Init, which is the only daemon that should ever call this function will abort - * when this error occurs. - * Otherwise, the tester will ignore it and continue, albeit without any selinux - * property separation. - */ - if (fsetxattr_failed) { - *fsetxattr_failed = true; - } - } + if (context) { + if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", + "fsetxattr failed to set context (%s) for \"%s\"", context, filename); + /* + * fsetxattr() will fail during system properties tests due to selinux policy. + * We do not want to create a custom policy for the tester, so we will continue in + * this function but set a flag that an error has occurred. + * Init, which is the only daemon that should ever call this function will abort + * when this error occurs. + * Otherwise, the tester will ignore it and continue, albeit without any selinux + * property separation. + */ + if (fsetxattr_failed) { + *fsetxattr_failed = true; + } } + } - if (ftruncate(fd, PA_SIZE) < 0) { - close(fd); - return nullptr; - } - - pa_size = PA_SIZE; - pa_data_size = pa_size - sizeof(prop_area); - compat_mode = false; - - void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - if (memory_area == MAP_FAILED) { - close(fd); - return nullptr; - } - - prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); - + if (ftruncate(fd, PA_SIZE) < 0) { close(fd); - return pa; + return nullptr; + } + + pa_size = PA_SIZE; + pa_data_size = pa_size - sizeof(prop_area); + + void* const memory_area = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (memory_area == MAP_FAILED) { + close(fd); + return nullptr; + } + + prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION); + + close(fd); + return pa; } static prop_area* map_fd_ro(const int fd) { - struct stat fd_stat; - if (fstat(fd, &fd_stat) < 0) { - return nullptr; - } + struct stat fd_stat; + if (fstat(fd, &fd_stat) < 0) { + return nullptr; + } - if ((fd_stat.st_uid != 0) - || (fd_stat.st_gid != 0) - || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) - || (fd_stat.st_size < static_cast(sizeof(prop_area))) ) { - return nullptr; - } + if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) || + ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) || + (fd_stat.st_size < static_cast(sizeof(prop_area)))) { + return nullptr; + } - pa_size = fd_stat.st_size; - pa_data_size = pa_size - sizeof(prop_area); + pa_size = fd_stat.st_size; + pa_data_size = pa_size - sizeof(prop_area); - void* const map_result = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // xsetprop changed PROT_READ to PROT_READ | PROT_WRITE - if (map_result == MAP_FAILED) { - return nullptr; - } + void* const map_result = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // resetprop: add PROT_WRITE + if (map_result == MAP_FAILED) { + return nullptr; + } - prop_area* pa = reinterpret_cast(map_result); - if ((pa->magic() != PROP_AREA_MAGIC) || - (pa->version() != PROP_AREA_VERSION && - pa->version() != PROP_AREA_VERSION_COMPAT)) { - munmap(pa, pa_size); - return nullptr; - } + prop_area* pa = reinterpret_cast(map_result); + if ((pa->magic() != PROP_AREA_MAGIC) || (pa->version() != PROP_AREA_VERSION)) { + munmap(pa, pa_size); + return nullptr; + } - if (pa->version() == PROP_AREA_VERSION_COMPAT) { - compat_mode = true; - } - - return pa; + return pa; } -static prop_area* map_prop_area(const char* filename, bool is_legacy) { - int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // xsetprop changed O_RDONLY to O_RDWR - bool close_fd = true; - if (fd == -1 && errno == ENOENT && is_legacy) { - /* - * For backwards compatibility, if the file doesn't - * exist, we use the environment to get the file descriptor. - * For security reasons, we only use this backup if the kernel - * returns ENOENT. We don't want to use the backup if the kernel - * returns other errors such as ENOMEM or ENFILE, since it - * might be possible for an external program to trigger this - * condition. - * Only do this for the legacy prop file, secured prop files - * do not have a backup - */ - fd = get_fd_from_env(); - close_fd = false; - } +static prop_area* map_prop_area(const char* filename) { + int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // resetprop: O_RDONLY -> O_RDWR + if (fd == -1) return nullptr; - if (fd < 0) { - return nullptr; - } + prop_area* map_result = map_fd_ro(fd); + close(fd); - prop_area* map_result = map_fd_ro(fd); - if (close_fd) { - close(fd); - } - - return map_result; + return map_result; } -void *prop_area::allocate_obj(const size_t size, uint_least32_t *const off) -{ - const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t)); - if (bytes_used_ + aligned > pa_data_size) { - return NULL; - } +void* prop_area::allocate_obj(const size_t size, uint_least32_t* const off) { + const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t)); + if (bytes_used_ + aligned > pa_data_size) { + return nullptr; + } - *off = bytes_used_; - bytes_used_ += aligned; - return data_ + *off; + *off = bytes_used_; + bytes_used_ += aligned; + return data_ + *off; } -prop_bt *prop_area::new_prop_bt(const char *name, uint8_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 != NULL) { - prop_bt* bt = new(p) prop_bt(name, namelen); - *off = new_offset; - return bt; - } +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); + if (p != nullptr) { + prop_bt* bt = new (p) prop_bt(name, namelen); + *off = new_offset; + return bt; + } - return NULL; + return nullptr; } -prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen, - const char *value, uint8_t valuelen, uint_least32_t *const off) -{ - uint_least32_t new_offset; - void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset); - if (p != NULL) { - prop_info* info = new(p) prop_info(name, namelen, value, valuelen); - *off = new_offset; - return info; - } +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); + if (p != nullptr) { + prop_info* info = new (p) prop_info(name, namelen, value, valuelen); + *off = new_offset; + return info; + } - return NULL; + return nullptr; } -void *prop_area::to_prop_obj(uint_least32_t off) -{ - if (off > pa_data_size) - return NULL; +void* prop_area::to_prop_obj(uint_least32_t off) { + if (off > pa_data_size) return nullptr; - return (data_ + off); + return (data_ + off); } -inline prop_bt *prop_area::to_prop_bt(atomic_uint_least32_t* off_p) { +inline prop_bt* prop_area::to_prop_bt(atomic_uint_least32_t* off_p) { uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); return reinterpret_cast(to_prop_obj(off)); } -inline prop_info *prop_area::to_prop_info(atomic_uint_least32_t* off_p) { +inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) { uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume); return reinterpret_cast(to_prop_obj(off)); } -inline prop_bt *prop_area::root_node() -{ - return reinterpret_cast(to_prop_obj(0)); +inline prop_bt* prop_area::root_node() { + return reinterpret_cast(to_prop_obj(0)); } -static int cmp_prop_name(const char *one, uint8_t one_len, const char *two, - uint8_t two_len) -{ - if (one_len < two_len) - return -1; - else if (one_len > two_len) - return 1; - else - return strncmp(one, two, one_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; + else if (one_len > two_len) + return 1; + else + return strncmp(one, two, one_len); } -prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name, - uint8_t namelen, bool alloc_if_needed) -{ - - prop_bt* current = bt; - while (true) { - if (!current) { - return NULL; - } - - const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); - if (ret == 0) { - return current; - } - - if (ret < 0) { - uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed); - if (left_offset != 0) { - current = to_prop_bt(¤t->left); - } else { - if (!alloc_if_needed) { - return NULL; - } - - uint_least32_t new_offset; - prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); - if (new_bt) { - atomic_store_explicit(¤t->left, new_offset, memory_order_release); - } - return new_bt; - } - } else { - uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed); - if (right_offset != 0) { - current = to_prop_bt(¤t->right); - } else { - if (!alloc_if_needed) { - return NULL; - } - - uint_least32_t new_offset; - prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); - if (new_bt) { - atomic_store_explicit(¤t->right, new_offset, memory_order_release); - } - return new_bt; - } - } - } -} - -const prop_info *prop_area::find_property(prop_bt *const trie, const char *name, - uint8_t namelen, const char *value, uint8_t valuelen, - bool alloc_if_needed) -{ - if (!trie) return NULL; - - const char *remaining_name = name; - prop_bt* current = trie; - while (true) { - const char *sep = strchr(remaining_name, '.'); - const bool want_subtree = (sep != NULL); - const uint8_t substr_size = (want_subtree) ? - sep - remaining_name : strlen(remaining_name); - - if (!substr_size) { - return NULL; - } - - prop_bt* root = NULL; - uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); - if (children_offset != 0) { - root = to_prop_bt(¤t->children); - } else if (alloc_if_needed) { - uint_least32_t new_offset; - root = new_prop_bt(remaining_name, substr_size, &new_offset); - if (root) { - atomic_store_explicit(¤t->children, new_offset, memory_order_release); - } - } - - if (!root) { - return NULL; - } - - current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); - if (!current) { - return NULL; - } - - if (!want_subtree) - break; - - remaining_name = sep + 1; +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; + while (true) { + if (!current) { + return nullptr; } - uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); - if (prop_offset != 0) { - return to_prop_info(¤t->prop); - } else if (alloc_if_needed) { + const int ret = cmp_prop_name(name, namelen, current->name, current->namelen); + if (ret == 0) { + return current; + } + + if (ret < 0) { + uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed); + if (left_offset != 0) { + current = to_prop_bt(¤t->left); + } else { + if (!alloc_if_needed) { + return nullptr; + } + uint_least32_t new_offset; - prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset); - if (new_info) { - atomic_store_explicit(¤t->prop, new_offset, memory_order_release); + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + atomic_store_explicit(¤t->left, new_offset, memory_order_release); + } + return new_bt; + } + } else { + uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed); + if (right_offset != 0) { + current = to_prop_bt(¤t->right); + } else { + if (!alloc_if_needed) { + return nullptr; } - return new_info; - } else { - return NULL; + uint_least32_t new_offset; + prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset); + if (new_bt) { + atomic_store_explicit(¤t->right, new_offset, memory_order_release); + } + return new_bt; + } } + } } -const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name) -{ - if (!trie) return NULL; +const prop_info* prop_area::find_property(prop_bt* 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; - while (true) { - const char *sep = strchr(remaining_name, '.'); - const bool want_subtree = (sep != NULL); - const uint8_t substr_size = (want_subtree) ? - sep - remaining_name : strlen(remaining_name); + const char* remaining_name = name; + prop_bt* current = trie; + while (true) { + const char* sep = strchr(remaining_name, '.'); + const bool want_subtree = (sep != nullptr); + const uint32_t substr_size = (want_subtree) ? sep - remaining_name : strlen(remaining_name); - if (!substr_size) { - return NULL; - } - - prop_bt* root = NULL; - uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); - if (children_offset != 0) { - root = to_prop_bt(¤t->children); - } - - if (!root) { - return NULL; - } - - current = find_prop_bt(root, remaining_name, substr_size, false); - if (!current) { - return NULL; - } - - if (!want_subtree) - break; - - remaining_name = sep + 1; + if (!substr_size) { + return nullptr; } - uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); - if (prop_offset != 0) { - // printf("[bt] found at %p: name='%s' prop='%llu' left='%llu' right='%llu' children='%llu'\n", - // static_cast(current), current->name, - // static_cast(atomic_load_explicit(¤t->prop, memory_order_relaxed)), - // static_cast(atomic_load_explicit(¤t->left, memory_order_relaxed)), - // static_cast(atomic_load_explicit(¤t->right, memory_order_relaxed)), - // static_cast(atomic_load_explicit(¤t->children, memory_order_relaxed)) - // ); - atomic_store_explicit(¤t->prop, 0, memory_order_release); - return to_prop_info(¤t->prop); - } else { - // printf("[bt] property not found\n"); - return NULL; + prop_bt* root = nullptr; + uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); + if (children_offset != 0) { + root = to_prop_bt(¤t->children); + } else if (alloc_if_needed) { + uint_least32_t new_offset; + root = new_prop_bt(remaining_name, substr_size, &new_offset); + if (root) { + atomic_store_explicit(¤t->children, new_offset, memory_order_release); + } } + + if (!root) { + return nullptr; + } + + current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); + if (!current) { + return nullptr; + } + + if (!want_subtree) break; + + remaining_name = sep + 1; + } + + uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); + if (prop_offset != 0) { + return to_prop_info(¤t->prop); + } else if (alloc_if_needed) { + uint_least32_t new_offset; + prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset); + if (new_info) { + atomic_store_explicit(¤t->prop, new_offset, memory_order_release); + } + + return new_info; + } else { + return nullptr; + } } -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; +bool prop_area::find_property_and_del(prop_bt* const trie, const char* name) { + if (!trie) return false; + + const char* remaining_name = name; + prop_bt* current = trie; + while (true) { + const char* sep = strchr(remaining_name, '.'); + const bool want_subtree = (sep != nullptr); + const uint32_t substr_size = (want_subtree) ? sep - remaining_name : strlen(remaining_name); + + if (!substr_size) { + return false; + } + + prop_bt* root = nullptr; + uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed); + if (children_offset != 0) { + root = to_prop_bt(¤t->children); + } + + if (!root) { + return false; + } + + current = find_prop_bt(root, remaining_name, substr_size, false); + if (!current) { + return false; + } + + if (!want_subtree) break; + + remaining_name = sep + 1; + } + + uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed); + if (prop_offset != 0) { + atomic_store_explicit(¤t->prop, 0, memory_order_release); // resetprop: nullify the offset to delete the prop + return true; + } else { + return false; + } +} + +class PropertyServiceConnection { + public: + PropertyServiceConnection() : last_error_(0) { + socket_ = ::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); + if (socket_ == -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(&addr), alen)) < 0) { - close(fd); - return -1; + + if (TEMP_FAILURE_RETRY(connect(socket_, reinterpret_cast(&addr), alen)) == -1) { + last_error_ = errno; + close(socket_); + socket_ = -1; + } + } + + bool IsValid() { + return socket_ != -1; + } + + int GetLastError() { + return last_error_; + } + + bool RecvInt32(int32_t* value) { + int result = TEMP_FAILURE_RETRY(recv(socket_, value, sizeof(*value), MSG_WAITALL)); + return CheckSendRecvResult(result, sizeof(*value)); + } + + int socket() { + return socket_; + } + + ~PropertyServiceConnection() { + if (socket_ != -1) { + close(socket_); + } + } + + 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; } - const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0)); + return last_error_ == 0; + } - int result = -1; - 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 - // acknowledges its completion by closing the socket so we - // poll here (on nothing), waiting for the socket to close. - // If you 'adb shell setprop foo bar' you'll see the POLLHUP - // once the socket closes. Out of paranoia we cap our poll - // at 250 ms. - pollfd pollfds[1]; - pollfds[0].fd = fd; - pollfds[0].events = 0; - const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); - if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { - result = 0; - } else { - // Ignore the timeout and treat it like a success anyway. - // The init process is single-threaded and its property - // service is sometimes slow to respond (perhaps it's off - // starting a child process or something) and thus this - // times out and the caller thinks it failed, even though - // it's still getting around to it. So we fake it here, - // mostly for ctl.* properties, but we do try and wait 250 - // 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. - result = 0; - } + int socket_; + int last_error_; + + friend class SocketWriter; +}; + +class SocketWriter { + public: + explicit SocketWriter(PropertyServiceConnection* connection) + : connection_(connection), iov_index_(0), uint_buf_index_(0) + {} + + SocketWriter& WriteUint32(uint32_t value) { + // CHECK(uint_buf_index_ < kUintBufSize); + // CHECK(iov_index_ < kIovSize); + uint32_t* ptr = uint_buf_ + uint_buf_index_; + uint_buf_[uint_buf_index_++] = value; + iov_[iov_index_].iov_base = ptr; + iov_[iov_index_].iov_len = sizeof(*ptr); + ++iov_index_; + return *this; + } + + SocketWriter& WriteString(const char* value) { + uint32_t valuelen = strlen(value); + WriteUint32(valuelen); + if (valuelen == 0) { + return *this; } - close(fd); - return result; -} + // CHECK(iov_index_ < kIovSize); + iov_[iov_index_].iov_base = const_cast(value); + iov_[iov_index_].iov_len = valuelen; + ++iov_index_; -static void find_nth_fn(const prop_info *pi, void *ptr) -{ - find_nth_cookie *cookie = reinterpret_cast(ptr); + return *this; + } - if (cookie->n == cookie->count) - cookie->pi = pi; - - cookie->count++; -} - -bool prop_area::foreach_property(prop_bt *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); - if (err < 0) - return false; - } - uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed); - if (prop_offset != 0) { - prop_info *info = to_prop_info(&trie->prop); - if (!info) - return false; - propfn(info, cookie); - } - 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); - 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); - if (err < 0) - return false; + bool Send() { + if (!connection_->IsValid()) { + return false; } + if (writev(connection_->socket(), iov_, iov_index_) == -1) { + connection_->last_error_ = errno; + return false; + } + + iov_index_ = uint_buf_index_ = 0; return true; + } + + private: + static constexpr size_t kUintBufSize = 8; + static constexpr size_t kIovSize = 8; + + PropertyServiceConnection* connection_; + iovec iov_[kIovSize]; + size_t iov_index_; + uint32_t uint_buf_[kUintBufSize]; + size_t uint_buf_index_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter); +}; + +struct prop_msg { + unsigned cmd; + char name[PROP_NAME_MAX]; + char value[PROP_VALUE_MAX]; +}; + +static int send_prop_msg(const prop_msg* msg) { + PropertyServiceConnection connection; + if (!connection.IsValid()) { + return connection.GetLastError(); + } + + int result = -1; + int s = connection.socket(); + + const int num_bytes = TEMP_FAILURE_RETRY(send(s, 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 + // acknowledges its completion by closing the socket so we + // poll here (on nothing), waiting for the socket to close. + // If you 'adb shell setprop foo bar' you'll see the POLLHUP + // once the socket closes. Out of paranoia we cap our poll + // at 250 ms. + pollfd pollfds[1]; + pollfds[0].fd = s; + pollfds[0].events = 0; + const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); + if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) { + result = 0; + } else { + // Ignore the timeout and treat it like a success anyway. + // The init process is single-threaded and its property + // service is sometimes slow to respond (perhaps it's off + // starting a child process or something) and thus this + // times out and the caller thinks it failed, even though + // it's still getting around to it. So we fake it here, + // mostly for ctl.* properties, but we do try and wait 250 + // 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; + } + } + + return result; } -const prop_info *prop_area::find(const char *name) { - return find_property(root_node(), name, strlen(name), nullptr, 0, false); +static void find_nth_fn(const prop_info* pi, void* ptr) { + find_nth_cookie* cookie = reinterpret_cast(ptr); + + if (cookie->n == cookie->count) cookie->pi = pi; + + cookie->count++; } -bool prop_area::add(const char *name, unsigned int namelen, - const char *value, unsigned int valuelen) { - return find_property(root_node(), name, namelen, value, valuelen, true); +bool prop_area::foreach_property(prop_bt* 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); + if (err < 0) return false; + } + uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed); + if (prop_offset != 0) { + prop_info* info = to_prop_info(&trie->prop); + if (!info) return false; + propfn(info, cookie); + } + 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); + 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); + if (err < 0) return false; + } + + return true; } -const prop_info *prop_area::del(const char *name) { - return find_property_and_del(root_node(), name); +const prop_info* prop_area::find(const char* name) { + return find_property(root_node(), name, strlen(name), nullptr, 0, false); } -bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { - return foreach_property(root_node(), propfn, cookie); +bool prop_area::del(const char* name) { + return find_property_and_del(root_node(), name); +} + +bool prop_area::add(const char* name, unsigned int namelen, const char* value, + unsigned int valuelen) { + return find_property(root_node(), name, namelen, value, valuelen, true); +} + +bool prop_area::foreach (void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { + return foreach_property(root_node(), propfn, cookie); } class context_node { -public: - context_node(context_node* next, const char* context, prop_area* pa) - : next(next), context_(strdup(context)), pa_(pa), no_access_(false) { - lock_.init(false); - } - ~context_node() { - unmap(); - free(context_); - } - bool open(bool access_rw, bool* fsetxattr_failed); - bool check_access_and_open(); - void reset_access(); + public: + context_node(context_node* next, const char* context, prop_area* pa) + : next(next), context_(strdup(context)), pa_(pa), no_access_(false) { + lock_.init(false); + } + ~context_node() { + unmap(); + free(context_); + } + bool open(bool access_rw, bool* fsetxattr_failed); + bool check_access_and_open(); + void reset_access(); - const char* context() const { return context_; } - prop_area* pa() { return pa_; } + const char* context() const { + return context_; + } + prop_area* pa() { + return pa_; + } - context_node* next; + context_node* next; -private: - bool check_access(); - void unmap(); + private: + bool check_access(); + void unmap(); - Lock lock_; - char* context_; - prop_area* pa_; - bool no_access_; + Lock lock_; + char* context_; + prop_area* pa_; + bool no_access_; }; struct prefix_node { - prefix_node(struct prefix_node* next, const char* prefix, context_node* context) - : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) { - } - ~prefix_node() { - free(prefix); - } - char* prefix; - const size_t prefix_len; - context_node* context; - struct prefix_node* next; + prefix_node(struct prefix_node* next, const char* prefix, context_node* context) + : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) { + } + ~prefix_node() { + free(prefix); + } + char* prefix; + const size_t prefix_len; + context_node* context; + struct prefix_node* next; }; template static inline void list_add(List** list, Args... args) { - *list = new List(*list, args...); + *list = new List(*list, args...); } static void list_add_after_len(prefix_node** list, const char* prefix, context_node* context) { - size_t prefix_len = strlen(prefix); + size_t prefix_len = strlen(prefix); - auto next_list = list; + auto next_list = list; - while (*next_list) { - if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') { - list_add(next_list, prefix, context); - return; - } - next_list = &(*next_list)->next; + while (*next_list) { + if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') { + list_add(next_list, prefix, context); + return; } - list_add(next_list, prefix, context); + next_list = &(*next_list)->next; + } + list_add(next_list, prefix, context); } template static void list_foreach(List* list, Func func) { - while (list) { - func(list); - list = list->next; - } + while (list) { + func(list); + list = list->next; + } } template static List* list_find(List* list, Func func) { - while (list) { - if (func(list)) { - return list; - } - list = list->next; + while (list) { + if (func(list)) { + return list; } - return nullptr; + list = list->next; + } + return nullptr; } template static void list_free(List** list) { - while (*list) { - auto old_list = *list; - *list = old_list->next; - delete old_list; - } + while (*list) { + auto old_list = *list; + *list = old_list->next; + delete old_list; + } } static prefix_node* prefixes = nullptr; @@ -807,106 +865,104 @@ static context_node* contexts = nullptr; */ bool context_node::open(bool access_rw, bool* fsetxattr_failed) { - lock_.lock(); - if (pa_) { - lock_.unlock(); - return true; - } - - char filename[PROP_FILENAME_MAX]; - int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", - property_filename, context_); - if (len < 0 || len > PROP_FILENAME_MAX) { - lock_.unlock(); - return false; - } - - if (access_rw) { - pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed); - } else { - pa_ = map_prop_area(filename, false); - } + lock_.lock(); + if (pa_) { lock_.unlock(); - return pa_; + return true; + } + + char filename[PROP_FILENAME_MAX]; + int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", property_filename, context_); + if (len < 0 || len > PROP_FILENAME_MAX) { + lock_.unlock(); + return false; + } + + if (access_rw) { + pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed); + } else { + pa_ = map_prop_area(filename); + } + lock_.unlock(); + return pa_; } bool context_node::check_access_and_open() { - if (!pa_ && !no_access_) { - if (!check_access() || !open(false, nullptr)) { - no_access_ = true; - } + if (!pa_ && !no_access_) { + if (!check_access() || !open(false, nullptr)) { + no_access_ = true; } - return pa_; + } + return pa_; } void context_node::reset_access() { - if (!check_access()) { - unmap(); - no_access_ = true; - } else { - no_access_ = false; - } + if (!check_access()) { + unmap(); + no_access_ = true; + } else { + no_access_ = false; + } } bool context_node::check_access() { - char filename[PROP_FILENAME_MAX]; - int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", - property_filename, context_); - if (len < 0 || len > PROP_FILENAME_MAX) { - return false; - } + char filename[PROP_FILENAME_MAX]; + int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s", property_filename, context_); + if (len < 0 || len > PROP_FILENAME_MAX) { + return false; + } - return access(filename, R_OK) == 0; + return access(filename, R_OK) == 0; } void context_node::unmap() { - if (!pa_) { - return; - } + if (!pa_) { + return; + } - munmap(pa_, pa_size); - if (pa_ == __system_property_area__) { - __system_property_area__ = nullptr; - } - pa_ = nullptr; + munmap(pa_, pa_size); + if (pa_ == __system_property_area__) { + __system_property_area__ = nullptr; + } + pa_ = nullptr; } static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) { - char filename[PROP_FILENAME_MAX]; - int len = __libc_format_buffer(filename, sizeof(filename), - "%s/properties_serial", property_filename); - if (len < 0 || len > PROP_FILENAME_MAX) { - __system_property_area__ = nullptr; - return false; - } + char filename[PROP_FILENAME_MAX]; + int len = + __libc_format_buffer(filename, sizeof(filename), "%s/properties_serial", property_filename); + if (len < 0 || len > PROP_FILENAME_MAX) { + __system_property_area__ = nullptr; + return false; + } - if (access_rw) { - __system_property_area__ = - map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed); - } else { - __system_property_area__ = map_prop_area(filename, false); - } - return __system_property_area__; + if (access_rw) { + __system_property_area__ = + map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed); + } else { + __system_property_area__ = map_prop_area(filename); + } + return __system_property_area__; } static prop_area* get_prop_area_for_name(const char* name) { - auto entry = list_find(prefixes, [name](prefix_node* l) { - return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len); - }); - if (!entry) { - return nullptr; - } + auto entry = list_find(prefixes, [name](prefix_node* l) { + return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len); + }); + if (!entry) { + return nullptr; + } - auto cnode = entry->context; - if (!cnode->pa()) { - /* - * We explicitly do not check no_access_ in this case because unlike the - * case of foreach(), we want to generate an selinux audit for each - * non-permitted property access in this function. - */ - cnode->open(false, nullptr); - } - return cnode->pa(); + auto cnode = entry->context; + if (!cnode->pa()) { + /* + * We explicitly do not check no_access_ in this case because unlike the + * case of foreach(), we want to generate an selinux audit for each + * non-permitted property access in this function. + */ + cnode->open(false, nullptr); + } + return cnode->pa(); } /* @@ -922,29 +978,26 @@ static prop_area* get_prop_area_for_name(const char* name) { */ /* Read an entry from a spec file (e.g. file_contexts) */ -static inline int read_spec_entry(char **entry, char **ptr, int *len) -{ - *entry = NULL; - char *tmp_buf = NULL; +static inline int read_spec_entry(char** entry, char** ptr, int* len) { + *entry = nullptr; + char* tmp_buf = nullptr; - while (isspace(**ptr) && **ptr != '\0') - (*ptr)++; + while (isspace(**ptr) && **ptr != '\0') (*ptr)++; - tmp_buf = *ptr; - *len = 0; + tmp_buf = *ptr; + *len = 0; - while (!isspace(**ptr) && **ptr != '\0') { - (*ptr)++; - (*len)++; - } + while (!isspace(**ptr) && **ptr != '\0') { + (*ptr)++; + (*len)++; + } - if (*len) { - *entry = strndup(tmp_buf, *len); - if (!*entry) - return -1; - } + if (*len) { + *entry = strndup(tmp_buf, *len); + if (!*entry) return -1; + } - return 0; + return 0; } /* @@ -955,290 +1008,356 @@ static inline int read_spec_entry(char **entry, char **ptr, int *len) * * This function calls read_spec_entry() to do the actual string processing. */ -static int read_spec_entries(char *line_buf, int num_args, ...) -{ - char **spec_entry, *buf_p; - int len, rc, items, entry_len = 0; - va_list ap; +static int read_spec_entries(char* line_buf, int num_args, ...) { + char **spec_entry, *buf_p; + int len, rc, items, entry_len = 0; + va_list ap; - len = strlen(line_buf); - if (line_buf[len - 1] == '\n') - line_buf[len - 1] = '\0'; - else - /* Handle case if line not \n terminated by bumping - * the len for the check below (as the line is NUL - * terminated by getline(3)) */ - len++; + len = strlen(line_buf); + if (line_buf[len - 1] == '\n') + line_buf[len - 1] = '\0'; + else + /* Handle case if line not \n terminated by bumping + * the len for the check below (as the line is NUL + * terminated by getline(3)) */ + len++; - buf_p = line_buf; - while (isspace(*buf_p)) - buf_p++; + buf_p = line_buf; + while (isspace(*buf_p)) buf_p++; - /* Skip comment lines and empty lines. */ - if (*buf_p == '#' || *buf_p == '\0') - return 0; + /* Skip comment lines and empty lines. */ + if (*buf_p == '#' || *buf_p == '\0') return 0; - /* Process the spec file entries */ - va_start(ap, num_args); + /* Process the spec file entries */ + va_start(ap, num_args); - items = 0; - while (items < num_args) { - spec_entry = va_arg(ap, char **); + items = 0; + while (items < num_args) { + spec_entry = va_arg(ap, char**); - if (len - 1 == buf_p - line_buf) { - va_end(ap); - return items; - } - - rc = read_spec_entry(spec_entry, &buf_p, &entry_len); - if (rc < 0) { - va_end(ap); - return rc; - } - if (entry_len) - items++; + if (len - 1 == buf_p - line_buf) { + va_end(ap); + return items; } - va_end(ap); - return items; + + rc = read_spec_entry(spec_entry, &buf_p, &entry_len); + if (rc < 0) { + va_end(ap); + return rc; + } + if (entry_len) items++; + } + va_end(ap); + return items; +} + +static bool initialize_properties_from_file(const char* filename) { + FILE* file = fopen(filename, "re"); + if (!file) { + return false; + } + + char* buffer = nullptr; + size_t line_len; + char* prop_prefix = nullptr; + char* context = nullptr; + + while (getline(&buffer, &line_len, file) > 0) { + int items = read_spec_entries(buffer, 2, &prop_prefix, &context); + if (items <= 0) { + continue; + } + if (items == 1) { + free(prop_prefix); + continue; + } + /* + * init uses ctl.* properties as an IPC mechanism and does not write them + * to a property file, therefore we do not need to create property files + * to store them. + */ + if (!strncmp(prop_prefix, "ctl.", 4)) { + free(prop_prefix); + free(context); + continue; + } + + auto old_context = + list_find(contexts, [context](context_node* l) { return !strcmp(l->context(), context); }); + if (old_context) { + list_add_after_len(&prefixes, prop_prefix, old_context); + } else { + list_add(&contexts, context, nullptr); + list_add_after_len(&prefixes, prop_prefix, contexts); + } + free(prop_prefix); + free(context); + } + + free(buffer); + fclose(file); + + return true; } static bool initialize_properties() { - FILE* file = fopen("/property_contexts", "re"); - - if (!file) { - return false; - } - - char* buffer = nullptr; - size_t line_len; - char* prop_prefix = nullptr; - char* context = nullptr; - - while (getline(&buffer, &line_len, file) > 0) { - int items = read_spec_entries(buffer, 2, &prop_prefix, &context); - if (items <= 0) { - continue; - } - if (items == 1) { - free(prop_prefix); - continue; - } - /* - * init uses ctl.* properties as an IPC mechanism and does not write them - * to a property file, therefore we do not need to create property files - * to store them. - */ - if (!strncmp(prop_prefix, "ctl.", 4)) { - free(prop_prefix); - free(context); - continue; - } - - auto old_context = list_find( - contexts, [context](context_node* l) { return !strcmp(l->context(), context); }); - if (old_context) { - list_add_after_len(&prefixes, prop_prefix, old_context); - } else { - list_add(&contexts, context, nullptr); - list_add_after_len(&prefixes, prop_prefix, contexts); - } - free(prop_prefix); - free(context); - } - - free(buffer); - fclose(file); + // If we do find /property_contexts, then this is being + // run as part of the OTA updater on older release that had + // /property_contexts - b/34370523 + if (initialize_properties_from_file("/property_contexts")) { return true; + } + + // Use property_contexts from /system & /vendor, fall back to those from / + if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) { + if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) { + return false; + } + if (!initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts")) { + return false; + } + } else { + if (!initialize_properties_from_file("/plat_property_contexts")) { + return false; + } + if (!initialize_properties_from_file("/nonplat_property_contexts")) { + return false; + } + } + + return true; } static bool is_dir(const char* pathname) { - struct stat info; - if (stat(pathname, &info) == -1) { - return false; - } - return S_ISDIR(info.st_mode); + struct stat info; + if (stat(pathname, &info) == -1) { + return false; + } + return S_ISDIR(info.st_mode); } static void free_and_unmap_contexts() { - list_free(&prefixes); - list_free(&contexts); - if (__system_property_area__) { - munmap(__system_property_area__, pa_size); - __system_property_area__ = nullptr; - } + list_free(&prefixes); + list_free(&contexts); + if (__system_property_area__) { + munmap(__system_property_area__, pa_size); + __system_property_area__ = nullptr; + } } -int __system_properties_init() -{ - if (initialized) { - //list_foreach(contexts, [](context_node* l) { l->reset_access(); }); // xsetprop removed - //return 0; // xsetprop removed - free_and_unmap_contexts(); // xsetprop added - initialized = false; // xsetprop added - } - if (is_dir(property_filename)) { - if (!initialize_properties()) { - return -1; - } - if (!map_system_property_area(false, nullptr)) { - free_and_unmap_contexts(); - return -1; - } - } else { - __system_property_area__ = map_prop_area(property_filename, true); - if (!__system_property_area__) { - return -1; - } - list_add(&contexts, "legacy_system_prop_area", __system_property_area__); - list_add_after_len(&prefixes, "*", contexts); - } - initialized = true; - return 0; -} +int __system_properties_init() { + // This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982). + ErrnoRestorer errno_restorer; -int __system_property_set_filename(const char *filename) -{ - size_t len = strlen(filename); - if (len >= sizeof(property_filename)) - return -1; - - strcpy(property_filename, filename); - return 0; -} - -int __system_property_area_init() -{ - free_and_unmap_contexts(); - mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + if (initialized) { + // list_foreach(contexts, [](context_node* l) { l->reset_access(); }); // resetprop remove + // return 0; // resetprop remove + free_and_unmap_contexts(); // resetprop add + initialized = false; // resetprop add + } + if (is_dir(property_filename)) { if (!initialize_properties()) { - return -1; + return -1; } - bool open_failed = false; - bool fsetxattr_failed = false; - list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) { - if (!l->open(true, &fsetxattr_failed)) { - open_failed = true; - } - }); - if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) { - free_and_unmap_contexts(); - return -1; + if (!map_system_property_area(false, nullptr)) { + free_and_unmap_contexts(); + return -1; } - initialized = true; - return fsetxattr_failed ? -2 : 0; -} - -unsigned int __system_property_area_serial() -{ - prop_area *pa = __system_property_area__; - if (!pa) { - return -1; - } - // Make sure this read fulfilled before __system_property_serial - return atomic_load_explicit(pa->serial(), memory_order_acquire); -} - -const prop_info *__system_property_find(const char *name) -{ + } else { + __system_property_area__ = map_prop_area(property_filename); if (!__system_property_area__) { - return nullptr; + return -1; } - - // if (__predict_false(compat_mode)) { - // return __system_property_find_compat(name); - // } - - prop_area* pa = get_prop_area_for_name(name); - if (!pa) { - __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name); - return nullptr; - } - - return pa->find(name); + list_add(&contexts, "legacy_system_prop_area", __system_property_area__); + list_add_after_len(&prefixes, "*", contexts); + } + initialized = true; + return 0; } -int __system_property_del(const char *name) -{ - if (!__system_property_area__) { - return -1; +int __system_property_set_filename(const char* filename) { + size_t len = strlen(filename); + if (len >= sizeof(property_filename)) return -1; + + strcpy(property_filename, filename); + return 0; +} + +int __system_property_area_init() { + free_and_unmap_contexts(); + mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH); + if (!initialize_properties()) { + return -1; + } + bool open_failed = false; + bool fsetxattr_failed = false; + list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) { + if (!l->open(true, &fsetxattr_failed)) { + open_failed = true; } + }); + if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) { + free_and_unmap_contexts(); + return -1; + } + initialized = true; + return fsetxattr_failed ? -2 : 0; +} - prop_area* pa = get_prop_area_for_name(name); - if (!pa) { - __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied deleting property \"%s\"", name); - return -1; - } +uint32_t __system_property_area_serial() { + prop_area* pa = __system_property_area__; + if (!pa) { + return -1; + } + // Make sure this read fulfilled before __system_property_serial + return atomic_load_explicit(pa->serial(), memory_order_acquire); +} - bool ret = pa->del(name); - if (!ret) - return -1; +const prop_info* __system_property_find(const char* name) { + if (!__system_property_area__) { + return nullptr; + } - // 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( - __system_property_area__->serial(), - atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, - memory_order_release); - __futex_wake(__system_property_area__->serial(), INT32_MAX); - return 0; + prop_area* pa = get_prop_area_for_name(name); + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name); + return nullptr; + } + return pa->find(name); +} + +int __system_property_del(const char *name) { + if (!__system_property_area__) { + return 1; + } + + prop_area* pa = get_prop_area_for_name(name); + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name); + return 1; + } + + if (!pa->del(name)) + return 1; + + // We want to make sure that updates are visible to readers + atomic_store_explicit( + __system_property_area__->serial(), + atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(__system_property_area__->serial(), INT32_MAX); + return 0; } // The C11 standard doesn't allow atomic loads from const fields, // though C++11 does. Fudge it until standards get straightened out. -static inline uint_least32_t load_const_atomic(const atomic_uint_least32_t* s, - memory_order mo) { - atomic_uint_least32_t* non_const_s = const_cast(s); - return atomic_load_explicit(non_const_s, mo); +static inline uint_least32_t load_const_atomic(const atomic_uint_least32_t* s, memory_order mo) { + atomic_uint_least32_t* non_const_s = const_cast(s); + return atomic_load_explicit(non_const_s, mo); } -int __system_property_read(const prop_info *pi, char *name, char *value) -{ - // if (__predict_false(compat_mode)) { - // return __system_property_read_compat(pi, name, value); - // } - - while (true) { - uint32_t serial = __system_property_serial(pi); // acquire semantics - size_t len = SERIAL_VALUE_LEN(serial); - memcpy(value, pi->value, len + 1); - // TODO: Fix the synchronization scheme here. - // There is no fully supported way to implement this kind - // of synchronization in C++11, since the memcpy races with - // updates to pi, and the data being accessed is not atomic. - // The following fence is unintuitive, but would be the - // correct one if memcpy used memory_order_relaxed atomic accesses. - // In practice it seems unlikely that the generated code would - // would be any different, so this should be OK. - atomic_thread_fence(memory_order_acquire); - if (serial == - load_const_atomic(&(pi->serial), memory_order_relaxed)) { - if (name != 0) { - strcpy(name, pi->name); - } - return len; +int __system_property_read(const prop_info* pi, char* name, char* value) { + while (true) { + uint32_t serial = __system_property_serial(pi); // acquire semantics + size_t len = SERIAL_VALUE_LEN(serial); + memcpy(value, pi->value, len + 1); + // TODO: Fix the synchronization scheme here. + // There is no fully supported way to implement this kind + // of synchronization in C++11, since the memcpy races with + // updates to pi, and the data being accessed is not atomic. + // The following fence is unintuitive, but would be the + // correct one if memcpy used memory_order_relaxed atomic accesses. + // In practice it seems unlikely that the generated code would + // would be any different, so this should be OK. + atomic_thread_fence(memory_order_acquire); + if (serial == load_const_atomic(&(pi->serial), memory_order_relaxed)) { + 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; } + } } -int __system_property_get(const char *name, char *value) -{ - const prop_info *pi = __system_property_find(name); +void __system_property_read_callback(const prop_info* pi, + void (*callback)(void* cookie, + const char* name, + const char* value, + uint32_t serial), + void* cookie) { + while (true) { + uint32_t serial = __system_property_serial(pi); // acquire semantics + size_t len = SERIAL_VALUE_LEN(serial); + char value_buf[len + 1]; - if (pi != 0) { - return __system_property_read(pi, 0, value); + 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, serial); + 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, nullptr, value); + } else { + value[0] = 0; + return 0; + } +} + +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(atoll(value)); + if (version >= kProtocolVersion2) { + g_propservice_protocol_version = kProtocolVersion2; } else { - value[0] = 0; - return 0; + __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 == 0) return -1; - if (value == 0) value = ""; +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; + + if (g_propservice_protocol_version == 0) { + detect_protocol_version(); + } + + if (g_propservice_protocol_version == kProtocolVersion1) { + // Old protocol does not support long names if (strlen(key) >= PROP_NAME_MAX) return -1; - if (strlen(value) >= PROP_VALUE_MAX) return -1; prop_msg msg; memset(&msg, 0, sizeof msg); @@ -1246,140 +1365,198 @@ int __system_property_set(const char *key, const char *value) 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; + return send_prop_msg(&msg); + } else { + // Use proper protocol + PropertyServiceConnection connection; + if (!connection.IsValid()) { + errno = connection.GetLastError(); + __libc_format_log(ANDROID_LOG_WARN, + "libc", + "Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", + key, + value, + errno, + strerror(errno)); + return -1; + } + + SocketWriter writer(&connection); + if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) { + errno = connection.GetLastError(); + __libc_format_log(ANDROID_LOG_WARN, + "libc", + "Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)", + key, + value, + errno, + strerror(errno)); + return -1; + } + + int result = -1; + if (!connection.RecvInt32(&result)) { + errno = connection.GetLastError(); + __libc_format_log(ANDROID_LOG_WARN, + "libc", + "Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)", + key, + value, + errno, + strerror(errno)); + return -1; + } + + 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); + return -1; } return 0; + } } -int __system_property_update(prop_info *pi, const char *value, unsigned int len) -{ - if (len >= PROP_VALUE_MAX) - return -1; +int __system_property_update(prop_info* pi, const char* value, unsigned int len) { + if (len >= PROP_VALUE_MAX) { + return -1; + } - prop_area* pa = __system_property_area__; + prop_area* pa = __system_property_area__; - if (!pa) { - return -1; - } + if (!pa) { + return -1; + } - uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed); - serial |= 1; - atomic_store_explicit(&pi->serial, serial, memory_order_relaxed); - // The memcpy call here also races. Again pretend it - // used memory_order_relaxed atomics, and use the analogous - // counterintuitive fence. - atomic_thread_fence(memory_order_release); - memcpy(pi->value, value, len + 1); - atomic_store_explicit( - &pi->serial, - (len << 24) | ((serial + 1) & 0xffffff), - memory_order_release); - __futex_wake(&pi->serial, INT32_MAX); + uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed); + serial |= 1; + atomic_store_explicit(&pi->serial, serial, memory_order_relaxed); + // The memcpy call here also races. Again pretend it + // used memory_order_relaxed atomics, and use the analogous + // counterintuitive fence. + atomic_thread_fence(memory_order_release); + strlcpy(pi->value, value, len + 1); - atomic_store_explicit( - pa->serial(), - atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1, - memory_order_release); - __futex_wake(pa->serial(), INT32_MAX); + atomic_store_explicit(&pi->serial, (len << 24) | ((serial + 1) & 0xffffff), memory_order_release); + __futex_wake(&pi->serial, INT32_MAX); - return 0; + atomic_store_explicit(pa->serial(), atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(pa->serial(), INT32_MAX); + + return 0; } -int __system_property_add(const char *name, unsigned int namelen, - const char *value, unsigned int valuelen) -{ - if (namelen >= PROP_NAME_MAX) - return -1; - if (valuelen >= PROP_VALUE_MAX) - return -1; - if (namelen < 1) - return -1; +int __system_property_add(const char* name, unsigned int namelen, const char* value, + unsigned int valuelen) { + if (valuelen >= PROP_VALUE_MAX) { + return -1; + } - if (!__system_property_area__) { - return -1; - } + if (namelen < 1) { + return -1; + } - prop_area* pa = get_prop_area_for_name(name); + if (!__system_property_area__) { + return -1; + } - if (!pa) { - __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name); - return -1; - } + prop_area* pa = get_prop_area_for_name(name); - bool ret = pa->add(name, namelen, value, valuelen); - if (!ret) - return -1; + if (!pa) { + __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name); + 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. - atomic_store_explicit( - __system_property_area__->serial(), - atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, - memory_order_release); - __futex_wake(__system_property_area__->serial(), INT32_MAX); - return 0; + bool ret = pa->add(name, namelen, value, valuelen); + 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. + atomic_store_explicit( + __system_property_area__->serial(), + atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1, + memory_order_release); + __futex_wake(__system_property_area__->serial(), INT32_MAX); + return 0; } // Wait for non-locked serial, and retrieve it with acquire semantics. -unsigned int __system_property_serial(const prop_info *pi) -{ - uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire); - while (SERIAL_DIRTY(serial)) { - __futex_wait(const_cast( - reinterpret_cast(&pi->serial)), - serial, NULL); - serial = load_const_atomic(&pi->serial, memory_order_acquire); - } - return serial; +uint32_t __system_property_serial(const prop_info* pi) { + uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire); + while (SERIAL_DIRTY(serial)) { + __futex_wait(const_cast<_Atomic(uint_least32_t)*>(&pi->serial), serial, nullptr); + serial = load_const_atomic(&pi->serial, memory_order_acquire); + } + return serial; } -unsigned int __system_property_wait_any(unsigned int serial) -{ - prop_area *pa = __system_property_area__; - uint32_t my_serial; - - if (!pa) { - return 0; - } - - do { - __futex_wait(pa->serial(), serial, NULL); - my_serial = atomic_load_explicit(pa->serial(), memory_order_acquire); - } while (my_serial == serial); - - return my_serial; +uint32_t __system_property_wait_any(uint32_t old_serial) { + uint32_t new_serial; + __system_property_wait(nullptr, old_serial, &new_serial, nullptr); + return new_serial; } -const prop_info *__system_property_find_nth(unsigned n) -{ - find_nth_cookie cookie(n); +bool __system_property_wait(const prop_info* pi, + uint32_t old_serial, + uint32_t* new_serial_ptr, + const timespec* relative_timeout) { + // Are we waiting on the global serial or a specific serial? + atomic_uint_least32_t* serial_ptr; + if (pi == nullptr) { + if (__system_property_area__ == nullptr) return -1; + serial_ptr = __system_property_area__->serial(); + } else { + serial_ptr = const_cast(&pi->serial); + } - const int err = __system_property_foreach(find_nth_fn, &cookie); - if (err < 0) { - return NULL; + uint32_t new_serial; + do { + int rc; + if ((rc = __futex_wait(serial_ptr, old_serial, relative_timeout)) != 0 && rc == -ETIMEDOUT) { + return false; } + new_serial = load_const_atomic(serial_ptr, memory_order_acquire); + } while (new_serial == old_serial); - return cookie.pi; + *new_serial_ptr = new_serial; + return true; } -int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie), - void *cookie) -{ - if (!__system_property_area__) { - return -1; +/* Deprecated, we won't use it anyways */ + +// const prop_info* __system_property_find_nth(unsigned n) { +// if (bionic_get_application_target_sdk_version() >= __ANDROID_API_O__) { +// __libc_fatal( +// "__system_property_find_nth is not supported since Android O," +// " please use __system_property_foreach instead."); +// } + +// find_nth_cookie cookie(n); + +// const int err = __system_property_foreach(find_nth_fn, &cookie); +// if (err < 0) { +// return nullptr; +// } + +// return cookie.pi; +// } + +int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) { + if (!__system_property_area__) { + return -1; + } + + list_foreach(contexts, [propfn, cookie](context_node* l) { + if (l->check_access_and_open()) { + l->pa()->foreach (propfn, cookie); } - - // if (__predict_false(compat_mode)) { - // return __system_property_foreach_compat(propfn, cookie); - // } - - list_foreach(contexts, [propfn, cookie](context_node* l) { - if (l->check_access_and_open()) { - l->pa()->foreach(propfn, cookie); - } - }); - return 0; + }); + return 0; } diff --git a/jni/resetprop/system_properties.h b/jni/resetprop/system_properties.h new file mode 100644 index 000000000..b55566e69 --- /dev/null +++ b/jni/resetprop/system_properties.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2008 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. + */ + +#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H +#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H + +#include +#include +#include +#include + +__BEGIN_DECLS + +typedef struct prop_info prop_info; + +#define PROP_VALUE_MAX 92 + +/* + * Sets system property `key` to `value`, creating the system property if it doesn't already exist. + */ +int __system_property_set(const char* key, const char* value) __INTRODUCED_IN(12); + +/* + * 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. + */ +const prop_info* __system_property_find(const char* name); + +/* + * Calls `callback` with a consistent trio of name, value, and serial number for property `pi`. + */ +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_FUTURE; + +/* + * Passes a `prop_info` for each system property to the provided + * callback. Use __system_property_read_callback() to read the value. + * + * This method is for inspecting and debugging the property system, and not generally useful. + */ +int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) + __INTRODUCED_IN(19); + +/* + * 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 `pi` is null, waits for the global serial number instead. + * + * If you don't know the current serial, use 0. + * + * Returns true and updates `*new_serial_ptr` on success, or false if the call + * timed out. + */ +struct timespec; +bool __system_property_wait(const prop_info* pi, + uint32_t old_serial, + uint32_t* new_serial_ptr, + const struct timespec* relative_timeout) + __INTRODUCED_IN_FUTURE; + +/* Deprecated. In Android O and above, there's no limit on property name length. */ +#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. Aborts in Android O and above. */ +const prop_info *__system_property_find_nth(unsigned n) __REMOVED_IN(26); + +__END_DECLS + +#endif