Home | History | Annotate | Download | only in bionic
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 #include <new>
     29 #include <stdio.h>
     30 #include <stdint.h>
     31 #include <stdlib.h>
     32 #include <unistd.h>
     33 #include <stddef.h>
     34 #include <errno.h>
     35 #include <poll.h>
     36 #include <fcntl.h>
     37 #include <stdbool.h>
     38 #include <string.h>
     39 
     40 #include <sys/mman.h>
     41 
     42 #include <sys/socket.h>
     43 #include <sys/un.h>
     44 #include <sys/select.h>
     45 #include <sys/stat.h>
     46 #include <sys/types.h>
     47 #include <netinet/in.h>
     48 #include <unistd.h>
     49 
     50 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
     51 #include <sys/_system_properties.h>
     52 #include <sys/system_properties.h>
     53 
     54 #include "private/bionic_atomic_inline.h"
     55 #include "private/bionic_futex.h"
     56 #include "private/bionic_macros.h"
     57 
     58 static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
     59 
     60 
     61 /*
     62  * Properties are stored in a hybrid trie/binary tree structure.
     63  * Each property's name is delimited at '.' characters, and the tokens are put
     64  * into a trie structure.  Siblings at each level of the trie are stored in a
     65  * binary tree.  For instance, "ro.secure"="1" could be stored as follows:
     66  *
     67  * +-----+   children    +----+   children    +--------+
     68  * |     |-------------->| ro |-------------->| secure |
     69  * +-----+               +----+               +--------+
     70  *                       /    \                /   |
     71  *                 left /      \ right   left /    |  prop   +===========+
     72  *                     v        v            v     +-------->| ro.secure |
     73  *                  +-----+   +-----+     +-----+            +-----------+
     74  *                  | net |   | sys |     | com |            |     1     |
     75  *                  +-----+   +-----+     +-----+            +===========+
     76  */
     77 
     78 // Represents a node in the trie.
     79 struct prop_bt {
     80     uint8_t namelen;
     81     uint8_t reserved[3];
     82 
     83     volatile uint32_t prop;
     84 
     85     volatile uint32_t left;
     86     volatile uint32_t right;
     87 
     88     volatile uint32_t children;
     89 
     90     char name[0];
     91 
     92     prop_bt(const char *name, const uint8_t name_length) {
     93         this->namelen = name_length;
     94         memcpy(this->name, name, name_length);
     95         this->name[name_length] = '\0';
     96         ANDROID_MEMBAR_FULL();
     97     }
     98 
     99 private:
    100     DISALLOW_COPY_AND_ASSIGN(prop_bt);
    101 };
    102 
    103 struct prop_area {
    104     uint32_t bytes_used;
    105     volatile uint32_t serial;
    106     uint32_t magic;
    107     uint32_t version;
    108     uint32_t reserved[28];
    109     char data[0];
    110 
    111     prop_area(const uint32_t magic, const uint32_t version) :
    112         serial(0), magic(magic), version(version) {
    113         memset(reserved, 0, sizeof(reserved));
    114         // Allocate enough space for the root node.
    115         bytes_used = sizeof(prop_bt);
    116     }
    117 
    118 private:
    119     DISALLOW_COPY_AND_ASSIGN(prop_area);
    120 };
    121 
    122 struct prop_info {
    123     volatile uint32_t serial;
    124     char value[PROP_VALUE_MAX];
    125     char name[0];
    126 
    127     prop_info(const char *name, const uint8_t namelen, const char *value,
    128               const uint8_t valuelen) {
    129         memcpy(this->name, name, namelen);
    130         this->name[namelen] = '\0';
    131         this->serial = (valuelen << 24);
    132         memcpy(this->value, value, valuelen);
    133         this->value[valuelen] = '\0';
    134         ANDROID_MEMBAR_FULL();
    135     }
    136 private:
    137     DISALLOW_COPY_AND_ASSIGN(prop_info);
    138 };
    139 
    140 struct find_nth_cookie {
    141     uint32_t count;
    142     const uint32_t n;
    143     const prop_info *pi;
    144 
    145     find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) {
    146     }
    147 };
    148 
    149 static char property_filename[PATH_MAX] = PROP_FILENAME;
    150 static bool compat_mode = false;
    151 static size_t pa_data_size;
    152 static size_t pa_size;
    153 
    154 // NOTE: This isn't static because system_properties_compat.c
    155 // requires it.
    156 prop_area *__system_property_area__ = NULL;
    157 
    158 static int get_fd_from_env(void)
    159 {
    160     // This environment variable consistes of two decimal integer
    161     // values separated by a ",". The first value is a file descriptor
    162     // and the second is the size of the system properties area. The
    163     // size is currently unused.
    164     char *env = getenv("ANDROID_PROPERTY_WORKSPACE");
    165 
    166     if (!env) {
    167         return -1;
    168     }
    169 
    170     return atoi(env);
    171 }
    172 
    173 static int map_prop_area_rw()
    174 {
    175     /* dev is a tmpfs that we can use to carve a shared workspace
    176      * out of, so let's do that...
    177      */
    178     const int fd = open(property_filename,
    179                         O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
    180 
    181     if (fd < 0) {
    182         if (errno == EACCES) {
    183             /* for consistency with the case where the process has already
    184              * mapped the page in and segfaults when trying to write to it
    185              */
    186             abort();
    187         }
    188         return -1;
    189     }
    190 
    191     // TODO: Is this really required ? Does android run on any kernels that
    192     // don't support O_CLOEXEC ?
    193     const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
    194     if (ret < 0) {
    195         close(fd);
    196         return -1;
    197     }
    198 
    199     if (ftruncate(fd, PA_SIZE) < 0) {
    200         close(fd);
    201         return -1;
    202     }
    203 
    204     pa_size = PA_SIZE;
    205     pa_data_size = pa_size - sizeof(prop_area);
    206     compat_mode = false;
    207 
    208     void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    209     if (memory_area == MAP_FAILED) {
    210         close(fd);
    211         return -1;
    212     }
    213 
    214     prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
    215 
    216     /* plug into the lib property services */
    217     __system_property_area__ = pa;
    218 
    219     close(fd);
    220     return 0;
    221 }
    222 
    223 static int map_fd_ro(const int fd) {
    224     struct stat fd_stat;
    225     if (fstat(fd, &fd_stat) < 0) {
    226         return -1;
    227     }
    228 
    229     if ((fd_stat.st_uid != 0)
    230             || (fd_stat.st_gid != 0)
    231             || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
    232             || (fd_stat.st_size < static_cast<off_t>(sizeof(prop_area))) ) {
    233         return -1;
    234     }
    235 
    236     pa_size = fd_stat.st_size;
    237     pa_data_size = pa_size - sizeof(prop_area);
    238 
    239     void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
    240     if (map_result == MAP_FAILED) {
    241         return -1;
    242     }
    243 
    244     prop_area* pa = reinterpret_cast<prop_area*>(map_result);
    245     if ((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION &&
    246                 pa->version != PROP_AREA_VERSION_COMPAT)) {
    247         munmap(pa, pa_size);
    248         return -1;
    249     }
    250 
    251     if (pa->version == PROP_AREA_VERSION_COMPAT) {
    252         compat_mode = true;
    253     }
    254 
    255     __system_property_area__ = pa;
    256     return 0;
    257 }
    258 
    259 static int map_prop_area()
    260 {
    261     int fd(open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
    262     if (fd >= 0) {
    263         /* For old kernels that don't support O_CLOEXEC */
    264         const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
    265         if (ret < 0) {
    266             close(fd);
    267             return -1;
    268         }
    269     }
    270 
    271     bool close_fd = true;
    272     if ((fd < 0) && (errno == ENOENT)) {
    273         /*
    274          * For backwards compatibility, if the file doesn't
    275          * exist, we use the environment to get the file descriptor.
    276          * For security reasons, we only use this backup if the kernel
    277          * returns ENOENT. We don't want to use the backup if the kernel
    278          * returns other errors such as ENOMEM or ENFILE, since it
    279          * might be possible for an external program to trigger this
    280          * condition.
    281          */
    282         fd = get_fd_from_env();
    283         close_fd = false;
    284     }
    285 
    286     if (fd < 0) {
    287         return -1;
    288     }
    289 
    290     const int map_result = map_fd_ro(fd);
    291     if (close_fd) {
    292         close(fd);
    293     }
    294 
    295     return map_result;
    296 }
    297 
    298 static void *allocate_obj(const size_t size, uint32_t *const off)
    299 {
    300     prop_area *pa = __system_property_area__;
    301     const size_t aligned = BIONIC_ALIGN(size, sizeof(uint32_t));
    302     if (pa->bytes_used + aligned > pa_data_size) {
    303         return NULL;
    304     }
    305 
    306     *off = pa->bytes_used;
    307     pa->bytes_used += aligned;
    308     return pa->data + *off;
    309 }
    310 
    311 static prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint32_t *const off)
    312 {
    313     uint32_t new_offset;
    314     void *const offset = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
    315     if (offset) {
    316         prop_bt* bt = new(offset) prop_bt(name, namelen);
    317         *off = new_offset;
    318         return bt;
    319     }
    320 
    321     return NULL;
    322 }
    323 
    324 static prop_info *new_prop_info(const char *name, uint8_t namelen,
    325         const char *value, uint8_t valuelen, uint32_t *const off)
    326 {
    327     uint32_t off_tmp;
    328     void* const offset = allocate_obj(sizeof(prop_info) + namelen + 1, &off_tmp);
    329     if (offset) {
    330         prop_info* info = new(offset) prop_info(name, namelen, value, valuelen);
    331         *off = off_tmp;
    332         return info;
    333     }
    334 
    335     return NULL;
    336 }
    337 
    338 static void *to_prop_obj(const uint32_t off)
    339 {
    340     if (off > pa_data_size)
    341         return NULL;
    342     if (!__system_property_area__)
    343         return NULL;
    344 
    345     return (__system_property_area__->data + off);
    346 }
    347 
    348 static prop_bt *root_node()
    349 {
    350     return reinterpret_cast<prop_bt*>(to_prop_obj(0));
    351 }
    352 
    353 static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
    354         uint8_t two_len)
    355 {
    356     if (one_len < two_len)
    357         return -1;
    358     else if (one_len > two_len)
    359         return 1;
    360     else
    361         return strncmp(one, two, one_len);
    362 }
    363 
    364 static prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
    365                              uint8_t namelen, bool alloc_if_needed)
    366 {
    367 
    368     prop_bt* current = bt;
    369     while (true) {
    370         if (!current) {
    371             return NULL;
    372         }
    373 
    374         const int ret = cmp_prop_name(name, namelen, current->name, current->namelen);
    375         if (ret == 0) {
    376             return current;
    377         }
    378 
    379         if (ret < 0) {
    380             if (current->left) {
    381                 current = reinterpret_cast<prop_bt*>(to_prop_obj(current->left));
    382             } else {
    383                 if (!alloc_if_needed) {
    384                    return NULL;
    385                 }
    386 
    387                 // Note that there isn't a race condition here. "clients" never
    388                 // reach this code-path since It's only the (single threaded) server
    389                 // that allocates new nodes. Though "bt->left" is volatile, it can't
    390                 // have changed since the last value was last read.
    391                 uint32_t new_offset = 0;
    392                 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
    393                 if (new_bt) {
    394                     current->left = new_offset;
    395                 }
    396                 return new_bt;
    397             }
    398         } else {
    399             if (current->right) {
    400                 current = reinterpret_cast<prop_bt*>(to_prop_obj(current->right));
    401             } else {
    402                 if (!alloc_if_needed) {
    403                    return NULL;
    404                 }
    405 
    406                 uint32_t new_offset;
    407                 prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
    408                 if (new_bt) {
    409                     current->right = new_offset;
    410                 }
    411                 return new_bt;
    412             }
    413         }
    414     }
    415 }
    416 
    417 static const prop_info *find_property(prop_bt *const trie, const char *name,
    418         uint8_t namelen, const char *value, uint8_t valuelen,
    419         bool alloc_if_needed)
    420 {
    421     if (!trie) return NULL;
    422 
    423     const char *remaining_name = name;
    424     prop_bt* current = trie;
    425     while (true) {
    426         const char *sep = strchr(remaining_name, '.');
    427         const bool want_subtree = (sep != NULL);
    428         const uint8_t substr_size = (want_subtree) ?
    429             sep - remaining_name : strlen(remaining_name);
    430 
    431         if (!substr_size) {
    432             return NULL;
    433         }
    434 
    435         prop_bt* root = NULL;
    436         if (current->children) {
    437             root = reinterpret_cast<prop_bt*>(to_prop_obj(current->children));
    438         } else if (alloc_if_needed) {
    439             uint32_t new_bt_offset;
    440             root = new_prop_bt(remaining_name, substr_size, &new_bt_offset);
    441             if (root) {
    442                 current->children = new_bt_offset;
    443             }
    444         }
    445 
    446         if (!root) {
    447             return NULL;
    448         }
    449 
    450         current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
    451         if (!current) {
    452             return NULL;
    453         }
    454 
    455         if (!want_subtree)
    456             break;
    457 
    458         remaining_name = sep + 1;
    459     }
    460 
    461     if (current->prop) {
    462         return reinterpret_cast<prop_info*>(to_prop_obj(current->prop));
    463     } else if (alloc_if_needed) {
    464         uint32_t new_info_offset;
    465         prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_info_offset);
    466         if (new_info) {
    467             current->prop = new_info_offset;
    468         }
    469 
    470         return new_info;
    471     } else {
    472         return NULL;
    473     }
    474 }
    475 
    476 static int send_prop_msg(const prop_msg *msg)
    477 {
    478     const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
    479     if (fd == -1) {
    480         return -1;
    481     }
    482 
    483     const size_t namelen = strlen(property_service_socket);
    484 
    485     sockaddr_un addr;
    486     memset(&addr, 0, sizeof(addr));
    487     strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
    488     addr.sun_family = AF_LOCAL;
    489     socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
    490     if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) {
    491         close(fd);
    492         return -1;
    493     }
    494 
    495     const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
    496 
    497     int result = -1;
    498     if (num_bytes == sizeof(prop_msg)) {
    499         // We successfully wrote to the property server but now we
    500         // wait for the property server to finish its work.  It
    501         // acknowledges its completion by closing the socket so we
    502         // poll here (on nothing), waiting for the socket to close.
    503         // If you 'adb shell setprop foo bar' you'll see the POLLHUP
    504         // once the socket closes.  Out of paranoia we cap our poll
    505         // at 250 ms.
    506         pollfd pollfds[1];
    507         pollfds[0].fd = fd;
    508         pollfds[0].events = 0;
    509         const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
    510         if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
    511             result = 0;
    512         } else {
    513             // Ignore the timeout and treat it like a success anyway.
    514             // The init process is single-threaded and its property
    515             // service is sometimes slow to respond (perhaps it's off
    516             // starting a child process or something) and thus this
    517             // times out and the caller thinks it failed, even though
    518             // it's still getting around to it.  So we fake it here,
    519             // mostly for ctl.* properties, but we do try and wait 250
    520             // ms so callers who do read-after-write can reliably see
    521             // what they've written.  Most of the time.
    522             // TODO: fix the system properties design.
    523             result = 0;
    524         }
    525     }
    526 
    527     close(fd);
    528     return result;
    529 }
    530 
    531 static void find_nth_fn(const prop_info *pi, void *ptr)
    532 {
    533     find_nth_cookie *cookie = reinterpret_cast<find_nth_cookie*>(ptr);
    534 
    535     if (cookie->n == cookie->count)
    536         cookie->pi = pi;
    537 
    538     cookie->count++;
    539 }
    540 
    541 static int foreach_property(const uint32_t off,
    542         void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
    543 {
    544     prop_bt *trie = reinterpret_cast<prop_bt*>(to_prop_obj(off));
    545     if (!trie)
    546         return -1;
    547 
    548     if (trie->left) {
    549         const int err = foreach_property(trie->left, propfn, cookie);
    550         if (err < 0)
    551             return -1;
    552     }
    553     if (trie->prop) {
    554         prop_info *info = reinterpret_cast<prop_info*>(to_prop_obj(trie->prop));
    555         if (!info)
    556             return -1;
    557         propfn(info, cookie);
    558     }
    559     if (trie->children) {
    560         const int err = foreach_property(trie->children, propfn, cookie);
    561         if (err < 0)
    562             return -1;
    563     }
    564     if (trie->right) {
    565         const int err = foreach_property(trie->right, propfn, cookie);
    566         if (err < 0)
    567             return -1;
    568     }
    569 
    570     return 0;
    571 }
    572 
    573 int __system_properties_init()
    574 {
    575     return map_prop_area();
    576 }
    577 
    578 int __system_property_set_filename(const char *filename)
    579 {
    580     size_t len = strlen(filename);
    581     if (len >= sizeof(property_filename))
    582         return -1;
    583 
    584     strcpy(property_filename, filename);
    585     return 0;
    586 }
    587 
    588 int __system_property_area_init()
    589 {
    590     return map_prop_area_rw();
    591 }
    592 
    593 const prop_info *__system_property_find(const char *name)
    594 {
    595     if (__predict_false(compat_mode)) {
    596         return __system_property_find_compat(name);
    597     }
    598     return find_property(root_node(), name, strlen(name), NULL, 0, false);
    599 }
    600 
    601 int __system_property_read(const prop_info *pi, char *name, char *value)
    602 {
    603     if (__predict_false(compat_mode)) {
    604         return __system_property_read_compat(pi, name, value);
    605     }
    606 
    607     while (true) {
    608         uint32_t serial = __system_property_serial(pi);
    609         size_t len = SERIAL_VALUE_LEN(serial);
    610         memcpy(value, pi->value, len + 1);
    611         ANDROID_MEMBAR_FULL();
    612         if (serial == pi->serial) {
    613             if (name != 0) {
    614                 strcpy(name, pi->name);
    615             }
    616             return len;
    617         }
    618     }
    619 }
    620 
    621 int __system_property_get(const char *name, char *value)
    622 {
    623     const prop_info *pi = __system_property_find(name);
    624 
    625     if (pi != 0) {
    626         return __system_property_read(pi, 0, value);
    627     } else {
    628         value[0] = 0;
    629         return 0;
    630     }
    631 }
    632 
    633 int __system_property_set(const char *key, const char *value)
    634 {
    635     if (key == 0) return -1;
    636     if (value == 0) value = "";
    637     if (strlen(key) >= PROP_NAME_MAX) return -1;
    638     if (strlen(value) >= PROP_VALUE_MAX) return -1;
    639 
    640     prop_msg msg;
    641     memset(&msg, 0, sizeof msg);
    642     msg.cmd = PROP_MSG_SETPROP;
    643     strlcpy(msg.name, key, sizeof msg.name);
    644     strlcpy(msg.value, value, sizeof msg.value);
    645 
    646     const int err = send_prop_msg(&msg);
    647     if (err < 0) {
    648         return err;
    649     }
    650 
    651     return 0;
    652 }
    653 
    654 int __system_property_update(prop_info *pi, const char *value, unsigned int len)
    655 {
    656     prop_area *pa = __system_property_area__;
    657 
    658     if (len >= PROP_VALUE_MAX)
    659         return -1;
    660 
    661     pi->serial = pi->serial | 1;
    662     ANDROID_MEMBAR_FULL();
    663     memcpy(pi->value, value, len + 1);
    664     ANDROID_MEMBAR_FULL();
    665     pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);
    666     __futex_wake(&pi->serial, INT32_MAX);
    667 
    668     pa->serial++;
    669     __futex_wake(&pa->serial, INT32_MAX);
    670 
    671     return 0;
    672 }
    673 
    674 int __system_property_add(const char *name, unsigned int namelen,
    675             const char *value, unsigned int valuelen)
    676 {
    677     prop_area *pa = __system_property_area__;
    678     const prop_info *pi;
    679 
    680     if (namelen >= PROP_NAME_MAX)
    681         return -1;
    682     if (valuelen >= PROP_VALUE_MAX)
    683         return -1;
    684     if (namelen < 1)
    685         return -1;
    686 
    687     pi = find_property(root_node(), name, namelen, value, valuelen, true);
    688     if (!pi)
    689         return -1;
    690 
    691     pa->serial++;
    692     __futex_wake(&pa->serial, INT32_MAX);
    693     return 0;
    694 }
    695 
    696 unsigned int __system_property_serial(const prop_info *pi)
    697 {
    698     uint32_t serial = pi->serial;
    699     while (SERIAL_DIRTY(serial)) {
    700         __futex_wait(const_cast<volatile uint32_t*>(&pi->serial), serial, NULL);
    701         serial = pi->serial;
    702     }
    703     return serial;
    704 }
    705 
    706 unsigned int __system_property_wait_any(unsigned int serial)
    707 {
    708     prop_area *pa = __system_property_area__;
    709 
    710     do {
    711         __futex_wait(&pa->serial, serial, NULL);
    712     } while (pa->serial == serial);
    713 
    714     return pa->serial;
    715 }
    716 
    717 const prop_info *__system_property_find_nth(unsigned n)
    718 {
    719     find_nth_cookie cookie(n);
    720 
    721     const int err = __system_property_foreach(find_nth_fn, &cookie);
    722     if (err < 0) {
    723         return NULL;
    724     }
    725 
    726     return cookie.pi;
    727 }
    728 
    729 int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
    730         void *cookie)
    731 {
    732     if (__predict_false(compat_mode)) {
    733         return __system_property_foreach_compat(propfn, cookie);
    734     }
    735 
    736     return foreach_property(0, propfn, cookie);
    737 }
    738