xref: /aosp_15_r20/frameworks/base/core/jni/android_app_PropertyInvalidatedCache.h (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <string.h>
18 #include <memory.h>
19 
20 #include <atomic>
21 #include <cstdint>
22 
23 namespace android::app::PropertyInvalidatedCache {
24 
25 /**
26  * A head of a CacheNonce object.  This contains all the fields that have a fixed size and
27  * location.  Fields with a variable location are found via offsets.  The offsets make this
28  * object position-independent, which is required because it is in shared memory and would be
29  * mapped into different virtual addresses for different processes.
30  */
31 class NonceStore {
32   protected:
33     // A convenient typedef.  The jbyteArray element type is jbyte, which the compiler treats as
34     // signed char.
35     typedef signed char block_t;
36 
37     // The nonce type.
38     typedef std::atomic<int64_t> nonce_t;
39 
40     // Atomics should be safe to use across processes if they are lock free.
41     static_assert(nonce_t::is_always_lock_free == true);
42 
43     // The value of an unset field.
44     static constexpr int UNSET = 0;
45 
46     // The size of the nonce array.
47     const int32_t kMaxNonce;
48 
49     // The size of the byte array.
50     const size_t kMaxByte;
51 
52     // The offset to the nonce array.
53     const size_t mNonceOffset;
54 
55     // The offset to the byte array.
56     const size_t mByteOffset;
57 
58     // The byte block hash.  This is fixed and at a known offset, so leave it in the base class.
59     volatile std::atomic<int32_t> mByteHash;
60 
61     // The constructor is protected!  It only makes sense when called from a subclass.
NonceStore(int kMaxNonce,size_t kMaxByte,volatile nonce_t * nonce,volatile block_t * block)62     NonceStore(int kMaxNonce, size_t kMaxByte, volatile nonce_t* nonce, volatile block_t* block) :
63             kMaxNonce(kMaxNonce),
64             kMaxByte(kMaxByte),
65             mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))),
66             mByteOffset(offset(this, const_cast<block_t*>(block))) {
67     }
68 
69   public:
70 
71     // These provide run-time access to the sizing parameters.
72     int getMaxNonce() const;
73     size_t getMaxByte() const;
74 
75     // Fetch a nonce, returning UNSET if the index is out of range.  This method specifically
76     // does not throw or generate an error if the index is out of range; this allows the method
77     // to be called in a CriticalNative JNI API.
78     int64_t getNonce(int index) const;
79 
80     // Set a nonce and return true. Return false if the index is out of range.  This method
81     // specifically does not throw or generate an error if the index is out of range; this
82     // allows the method to be called in a CriticalNative JNI API.
83     bool setNonce(int index, int64_t value);
84 
85     // Fetch just the byte-block hash
86     int32_t getHash() const;
87 
88     // Copy the byte block to the target and return the current hash.
89     int32_t getByteBlock(block_t* block, size_t len) const;
90 
91     // Set the byte block and the hash.
92     void setByteBlock(int hash, const block_t* block, size_t len);
93 
94   private:
95 
96     // A convenience function to compute the offset between two unlike pointers.
offset(void const * base,void const * member)97     static size_t offset(void const* base, void const* member) {
98         return reinterpret_cast<uintptr_t>(member) - reinterpret_cast<std::uintptr_t>(base);
99     }
100 
101     // Return the address of the nonce array.
nonce()102     volatile nonce_t* nonce() const {
103         // The array is located at an offset from <this>.
104         return reinterpret_cast<nonce_t*>(
105             reinterpret_cast<std::uintptr_t>(this) + mNonceOffset);
106     }
107 
108     // Return the address of the byte block array.
byteBlock()109     volatile block_t* byteBlock() const {
110         // The array is located at an offset from <this>.
111         return reinterpret_cast<block_t*>(
112             reinterpret_cast<std::uintptr_t>(this) + mByteOffset);
113     }
114 };
115 
116 /**
117  * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes.  The
118  * byte array has an associated hash.  This class provides methods to read and write the fields
119  * of the block but it does not interpret the fields.
120  *
121  * On initialization, all fields are set to zero.
122  *
123  * In general, methods do not report errors.  This allows the methods to be used in
124  * CriticalNative JNI APIs.
125  *
126  * The template is parameterized by the number of nonces it supports and the number of bytes in
127  * the string block.
128  */
129 template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore {
130 
131     // The array of nonces
132     volatile nonce_t mNonce[maxNonce];
133 
134     // The byte array.  This is not atomic but it is guarded by the mByteHash.
135     volatile block_t mByteBlock[maxByte];
136 
137   public:
138     // Construct and initialize the memory.
CacheNonce()139     CacheNonce() :
140             NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0])
141     {
142         for (int i = 0; i < maxNonce; i++) {
143             mNonce[i] = UNSET;
144         }
145         mByteHash = UNSET;
146         memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
147     }
148 };
149 
150 // The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.  This is
151 // more than enough for system_server PropertyInvalidatedCache support.  The configuration
152 // values are not defined as visible constants.  Clients should use the accessors on the
153 // SystemCacheNonce instance if they need the sizing parameters.
154 typedef CacheNonce</* max nonce */ 64, /* byte block size */ 8192> SystemCacheNonce;
155 
156 } // namespace android.app.PropertyInvalidatedCache
157