xref: /aosp_15_r20/bionic/libc/private/CachedProperty.h (revision 8d67ca893c1523eb926b9080dbe4e2ffd2a27ba1)
1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker  * Copyright (C) 2017 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker  * All rights reserved.
4*8d67ca89SAndroid Build Coastguard Worker  *
5*8d67ca89SAndroid Build Coastguard Worker  * Redistribution and use in source and binary forms, with or without
6*8d67ca89SAndroid Build Coastguard Worker  * modification, are permitted provided that the following conditions
7*8d67ca89SAndroid Build Coastguard Worker  * are met:
8*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions of source code must retain the above copyright
9*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer.
10*8d67ca89SAndroid Build Coastguard Worker  *  * Redistributions in binary form must reproduce the above copyright
11*8d67ca89SAndroid Build Coastguard Worker  *    notice, this list of conditions and the following disclaimer in
12*8d67ca89SAndroid Build Coastguard Worker  *    the documentation and/or other materials provided with the
13*8d67ca89SAndroid Build Coastguard Worker  *    distribution.
14*8d67ca89SAndroid Build Coastguard Worker  *
15*8d67ca89SAndroid Build Coastguard Worker  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*8d67ca89SAndroid Build Coastguard Worker  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*8d67ca89SAndroid Build Coastguard Worker  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18*8d67ca89SAndroid Build Coastguard Worker  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*8d67ca89SAndroid Build Coastguard Worker  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*8d67ca89SAndroid Build Coastguard Worker  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*8d67ca89SAndroid Build Coastguard Worker  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22*8d67ca89SAndroid Build Coastguard Worker  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*8d67ca89SAndroid Build Coastguard Worker  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*8d67ca89SAndroid Build Coastguard Worker  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25*8d67ca89SAndroid Build Coastguard Worker  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8d67ca89SAndroid Build Coastguard Worker  * SUCH DAMAGE.
27*8d67ca89SAndroid Build Coastguard Worker  */
28*8d67ca89SAndroid Build Coastguard Worker 
29*8d67ca89SAndroid Build Coastguard Worker #pragma once
30*8d67ca89SAndroid Build Coastguard Worker 
31*8d67ca89SAndroid Build Coastguard Worker #include <string.h>
32*8d67ca89SAndroid Build Coastguard Worker #include <sys/system_properties.h>
33*8d67ca89SAndroid Build Coastguard Worker 
34*8d67ca89SAndroid Build Coastguard Worker // Cached system property lookup. For code that needs to read the same property multiple times,
35*8d67ca89SAndroid Build Coastguard Worker // this class helps optimize those lookups.
36*8d67ca89SAndroid Build Coastguard Worker class CachedProperty {
37*8d67ca89SAndroid Build Coastguard Worker  public:
38*8d67ca89SAndroid Build Coastguard Worker   // The lifetime of `property_name` must be greater than that of this CachedProperty.
CachedProperty(const char * property_name)39*8d67ca89SAndroid Build Coastguard Worker   explicit CachedProperty(const char* property_name)
40*8d67ca89SAndroid Build Coastguard Worker     : property_name_(property_name),
41*8d67ca89SAndroid Build Coastguard Worker       prop_info_(nullptr),
42*8d67ca89SAndroid Build Coastguard Worker       cached_area_serial_(0),
43*8d67ca89SAndroid Build Coastguard Worker       cached_property_serial_(0),
44*8d67ca89SAndroid Build Coastguard Worker       is_read_only_(strncmp(property_name, "ro.", 3) == 0),
45*8d67ca89SAndroid Build Coastguard Worker       read_only_property_(nullptr) {
46*8d67ca89SAndroid Build Coastguard Worker     cached_value_[0] = '\0';
47*8d67ca89SAndroid Build Coastguard Worker   }
48*8d67ca89SAndroid Build Coastguard Worker 
49*8d67ca89SAndroid Build Coastguard Worker   // Returns true if the property has been updated (based on the serial rather than the value)
50*8d67ca89SAndroid Build Coastguard Worker   // since the last call to Get.
DidChange()51*8d67ca89SAndroid Build Coastguard Worker   bool DidChange() {
52*8d67ca89SAndroid Build Coastguard Worker     uint32_t initial_property_serial_ = cached_property_serial_;
53*8d67ca89SAndroid Build Coastguard Worker     Get();
54*8d67ca89SAndroid Build Coastguard Worker     return (cached_property_serial_ != initial_property_serial_);
55*8d67ca89SAndroid Build Coastguard Worker   }
56*8d67ca89SAndroid Build Coastguard Worker 
57*8d67ca89SAndroid Build Coastguard Worker   // Returns the current value of the underlying system property as cheaply as possible.
58*8d67ca89SAndroid Build Coastguard Worker   // The returned pointer is valid until the next call to Get. It is the caller's responsibility
59*8d67ca89SAndroid Build Coastguard Worker   // to provide a lock for thread-safety.
Get()60*8d67ca89SAndroid Build Coastguard Worker   const char* Get() {
61*8d67ca89SAndroid Build Coastguard Worker     // Do we have a `struct prop_info` yet?
62*8d67ca89SAndroid Build Coastguard Worker     if (prop_info_ == nullptr) {
63*8d67ca89SAndroid Build Coastguard Worker       // `__system_property_find` is expensive, so only retry if a property
64*8d67ca89SAndroid Build Coastguard Worker       // has been created since last time we checked.
65*8d67ca89SAndroid Build Coastguard Worker       uint32_t property_area_serial = __system_property_area_serial();
66*8d67ca89SAndroid Build Coastguard Worker       if (property_area_serial != cached_area_serial_) {
67*8d67ca89SAndroid Build Coastguard Worker         prop_info_ = __system_property_find(property_name_);
68*8d67ca89SAndroid Build Coastguard Worker         cached_area_serial_ = property_area_serial;
69*8d67ca89SAndroid Build Coastguard Worker       }
70*8d67ca89SAndroid Build Coastguard Worker     }
71*8d67ca89SAndroid Build Coastguard Worker 
72*8d67ca89SAndroid Build Coastguard Worker     if (prop_info_ != nullptr) {
73*8d67ca89SAndroid Build Coastguard Worker       // Only bother re-reading the property if it's actually changed since last time.
74*8d67ca89SAndroid Build Coastguard Worker       uint32_t property_serial = __system_property_serial(prop_info_);
75*8d67ca89SAndroid Build Coastguard Worker       if (property_serial != cached_property_serial_) {
76*8d67ca89SAndroid Build Coastguard Worker         __system_property_read_callback(prop_info_, &CachedProperty::Callback, this);
77*8d67ca89SAndroid Build Coastguard Worker       }
78*8d67ca89SAndroid Build Coastguard Worker     }
79*8d67ca89SAndroid Build Coastguard Worker     if (is_read_only_ && read_only_property_ != nullptr) {
80*8d67ca89SAndroid Build Coastguard Worker       return read_only_property_;
81*8d67ca89SAndroid Build Coastguard Worker     }
82*8d67ca89SAndroid Build Coastguard Worker     return cached_value_;
83*8d67ca89SAndroid Build Coastguard Worker   }
84*8d67ca89SAndroid Build Coastguard Worker 
85*8d67ca89SAndroid Build Coastguard Worker  private:
86*8d67ca89SAndroid Build Coastguard Worker   const char* property_name_;
87*8d67ca89SAndroid Build Coastguard Worker   const prop_info* prop_info_;
88*8d67ca89SAndroid Build Coastguard Worker   uint32_t cached_area_serial_;
89*8d67ca89SAndroid Build Coastguard Worker   uint32_t cached_property_serial_;
90*8d67ca89SAndroid Build Coastguard Worker   char cached_value_[PROP_VALUE_MAX];
91*8d67ca89SAndroid Build Coastguard Worker   bool is_read_only_;
92*8d67ca89SAndroid Build Coastguard Worker   const char* read_only_property_;
93*8d67ca89SAndroid Build Coastguard Worker 
Callback(void * data,const char *,const char * value,uint32_t serial)94*8d67ca89SAndroid Build Coastguard Worker   static void Callback(void* data, const char*, const char* value, uint32_t serial) {
95*8d67ca89SAndroid Build Coastguard Worker     CachedProperty* instance = reinterpret_cast<CachedProperty*>(data);
96*8d67ca89SAndroid Build Coastguard Worker     instance->cached_property_serial_ = serial;
97*8d67ca89SAndroid Build Coastguard Worker     // Read only properties can be larger than PROP_VALUE_MAX, but also never change value or
98*8d67ca89SAndroid Build Coastguard Worker     // location, thus we return the pointer from the shared memory directly.
99*8d67ca89SAndroid Build Coastguard Worker     if (instance->is_read_only_) {
100*8d67ca89SAndroid Build Coastguard Worker       instance->read_only_property_ = value;
101*8d67ca89SAndroid Build Coastguard Worker     } else {
102*8d67ca89SAndroid Build Coastguard Worker       strlcpy(instance->cached_value_, value, PROP_VALUE_MAX);
103*8d67ca89SAndroid Build Coastguard Worker     }
104*8d67ca89SAndroid Build Coastguard Worker   }
105*8d67ca89SAndroid Build Coastguard Worker };
106