summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.cpp73
-rw-r--r--core/jni/android_app_PropertyInvalidatedCache.h184
2 files changed, 160 insertions, 97 deletions
diff --git a/core/jni/android_app_PropertyInvalidatedCache.cpp b/core/jni/android_app_PropertyInvalidatedCache.cpp
index ead66660a0a4..12585d5f8137 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.cpp
+++ b/core/jni/android_app_PropertyInvalidatedCache.cpp
@@ -28,24 +28,77 @@
#include "core_jni_helpers.h"
#include "android_app_PropertyInvalidatedCache.h"
+namespace android::app::PropertyInvalidatedCache {
+
+// These provide run-time access to the sizing parameters.
+int NonceStore::getMaxNonce() const {
+ return kMaxNonce;
+}
+
+size_t NonceStore::getMaxByte() const {
+ return kMaxByte;
+}
+
+// Fetch a nonce, returning UNSET if the index is out of range. This method specifically
+// does not throw or generate an error if the index is out of range; this allows the method
+// to be called in a CriticalNative JNI API.
+int64_t NonceStore::getNonce(int index) const {
+ if (index < 0 || index >= kMaxNonce) {
+ return UNSET;
+ } else {
+ return nonce()[index];
+ }
+}
+
+// Set a nonce and return true. Return false if the index is out of range. This method
+// specifically does not throw or generate an error if the index is out of range; this
+// allows the method to be called in a CriticalNative JNI API.
+bool NonceStore::setNonce(int index, int64_t value) {
+ if (index < 0 || index >= kMaxNonce) {
+ return false;
+ } else {
+ nonce()[index] = value;
+ return true;
+ }
+}
+
+// Fetch just the byte-block hash
+int32_t NonceStore::getHash() const {
+ return mByteHash;
+}
+
+// Copy the byte block to the target and return the current hash.
+int32_t NonceStore::getByteBlock(block_t* block, size_t len) const {
+ memcpy(block, (void*) byteBlock(), std::min(kMaxByte, len));
+ return mByteHash;
+}
+
+// Set the byte block and the hash.
+void NonceStore::setByteBlock(int hash, const block_t* block, size_t len) {
+ memcpy((void*) byteBlock(), block, len = std::min(kMaxByte, len));
+ mByteHash = hash;
+}
+
+} // namespace android::app::PropertyInvalidatedCache;
+
namespace {
using namespace android::app::PropertyInvalidatedCache;
// Convert a jlong to a nonce block. This is a convenience function that should be inlined by
// the compiler.
-inline SystemCacheNonce* sysCache(jlong ptr) {
- return reinterpret_cast<SystemCacheNonce*>(ptr);
+inline NonceStore* nonceCache(jlong ptr) {
+ return reinterpret_cast<NonceStore*>(ptr);
}
// Return the number of nonces in the nonce block.
jint getMaxNonce(JNIEnv*, jclass, jlong ptr) {
- return sysCache(ptr)->getMaxNonce();
+ return nonceCache(ptr)->getMaxNonce();
}
// Return the number of string bytes in the nonce block.
jint getMaxByte(JNIEnv*, jclass, jlong ptr) {
- return sysCache(ptr)->getMaxByte();
+ return nonceCache(ptr)->getMaxByte();
}
// Set the byte block. The first int is the hash to set and the second is the array to copy.
@@ -56,25 +109,25 @@ void setByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", "null byte block");
return;
}
- sysCache(ptr)->setByteBlock(hash, value.get(), value.size());
+ nonceCache(ptr)->setByteBlock(hash, value.get(), value.size());
}
// Fetch the byte block. If the incoming hash is the same as the local hash, the Java layer is
// presumed to have an up-to-date copy of the byte block; do not copy byte array. The local
// hash is returned.
jint getByteBlock(JNIEnv* env, jclass, jlong ptr, jint hash, jbyteArray val) {
- if (sysCache(ptr)->getHash() == hash) {
+ if (nonceCache(ptr)->getHash() == hash) {
return hash;
}
ScopedByteArrayRW value(env, val);
- return sysCache(ptr)->getByteBlock(value.get(), value.size());
+ return nonceCache(ptr)->getByteBlock(value.get(), value.size());
}
// Fetch the byte block hash.
//
// This is a CriticalNative method and therefore does not get the JNIEnv or jclass parameters.
jint getByteBlockHash(jlong ptr) {
- return sysCache(ptr)->getHash();
+ return nonceCache(ptr)->getHash();
}
// Get a nonce value. So that this method can be CriticalNative, it returns 0 if the value is
@@ -83,7 +136,7 @@ jint getByteBlockHash(jlong ptr) {
//
// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
jlong getNonce(jlong ptr, jint index) {
- return sysCache(ptr)->getNonce(index);
+ return nonceCache(ptr)->getNonce(index);
}
// Set a nonce value. So that this method can be CriticalNative, it returns a boolean: false if
@@ -92,7 +145,7 @@ jlong getNonce(jlong ptr, jint index) {
//
// This method is @CriticalNative and does not take a JNIEnv* or jclass argument.
jboolean setNonce(jlong ptr, jint index, jlong value) {
- return sysCache(ptr)->setNonce(index, value);
+ return nonceCache(ptr)->setNonce(index, value);
}
static const JNINativeMethod gMethods[] = {
diff --git a/core/jni/android_app_PropertyInvalidatedCache.h b/core/jni/android_app_PropertyInvalidatedCache.h
index eefa8fa88624..00aa281b572f 100644
--- a/core/jni/android_app_PropertyInvalidatedCache.h
+++ b/core/jni/android_app_PropertyInvalidatedCache.h
@@ -18,129 +18,139 @@
#include <memory.h>
#include <atomic>
+#include <cstdint>
-namespace android {
-namespace app {
-namespace PropertyInvalidatedCache {
+namespace android::app::PropertyInvalidatedCache {
/**
- * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
- * byte array has an associated hash. This class provides methods to read and write the fields
- * of the block but it does not interpret the fields.
- *
- * On initialization, all fields are set to zero.
- *
- * In general, methods do not report errors. This allows the methods to be used in
- * CriticalNative JNI APIs.
- *
- * The template is parameterized by the number of nonces it supports and the number of bytes in
- * the string block.
+ * A head of a CacheNonce object. This contains all the fields that have a fixed size and
+ * location. Fields with a variable location are found via offsets. The offsets make this
+ * object position-independent, which is required because it is in shared memory and would be
+ * mapped into different virtual addresses for different processes.
*/
-template<int maxNonce, size_t maxByte> class CacheNonce {
-
- // The value of an unset field.
- static const int UNSET = 0;
-
+class NonceStore {
+ protected:
// A convenient typedef. The jbyteArray element type is jbyte, which the compiler treats as
// signed char.
typedef signed char block_t;
- // The array of nonces
- volatile std::atomic<int64_t> mNonce[maxNonce];
+ // The nonce type.
+ typedef std::atomic<int64_t> nonce_t;
- // The byte array. This is not atomic but it is guarded by the mByteHash.
- volatile block_t mByteBlock[maxByte];
+ // Atomics should be safe to use across processes if they are lock free.
+ static_assert(nonce_t::is_always_lock_free == true);
- // The hash that validates the byte block
- volatile std::atomic<int32_t> mByteHash;
+ // The value of an unset field.
+ static constexpr int UNSET = 0;
- // Pad the class to a multiple of 8 bytes.
- int32_t _pad;
+ // The size of the nonce array.
+ const int32_t kMaxNonce;
- public:
+ // The size of the byte array.
+ const size_t kMaxByte;
- // The expected size of this instance. This is a compile-time constant and can be used in a
- // static assertion.
- static const int expectedSize =
- maxNonce * sizeof(std::atomic<int64_t>)
- + sizeof(std::atomic<int32_t>)
- + maxByte * sizeof(block_t)
- + sizeof(int32_t);
+ // The offset to the nonce array.
+ const size_t mNonceOffset;
- // These provide run-time access to the sizing parameters.
- int getMaxNonce() const {
- return maxNonce;
- }
+ // The offset to the byte array.
+ const size_t mByteOffset;
- size_t getMaxByte() const {
- return maxByte;
- }
+ // The byte block hash. This is fixed and at a known offset, so leave it in the base class.
+ volatile std::atomic<int32_t> mByteHash;
- // Construct and initialize the memory.
- CacheNonce() {
- for (int i = 0; i < maxNonce; i++) {
- mNonce[i] = UNSET;
- }
- mByteHash = UNSET;
- memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ // 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) :
+ kMaxNonce(kMaxNonce),
+ kMaxByte(kMaxByte),
+ mNonceOffset(offset(this, const_cast<nonce_t*>(nonce))),
+ mByteOffset(offset(this, const_cast<block_t*>(block))) {
}
+ public:
+
+ // These provide run-time access to the sizing parameters.
+ int getMaxNonce() const;
+ size_t getMaxByte() const;
+
// Fetch a nonce, returning UNSET if the index is out of range. This method specifically
// does not throw or generate an error if the index is out of range; this allows the method
// to be called in a CriticalNative JNI API.
- int64_t getNonce(int index) const {
- if (index < 0 || index >= maxNonce) {
- return UNSET;
- } else {
- return mNonce[index];
- }
- }
+ int64_t getNonce(int index) const;
// Set a nonce and return true. Return false if the index is out of range. This method
// specifically does not throw or generate an error if the index is out of range; this
// allows the method to be called in a CriticalNative JNI API.
- bool setNonce(int index, int64_t value) {
- if (index < 0 || index >= maxNonce) {
- return false;
- } else {
- mNonce[index] = value;
- return true;
- }
- }
+ bool setNonce(int index, int64_t value);
// Fetch just the byte-block hash
- int32_t getHash() const {
- return mByteHash;
- }
+ int32_t getHash() const;
// Copy the byte block to the target and return the current hash.
- int32_t getByteBlock(block_t* block, size_t len) const {
- memcpy(block, (void*) mByteBlock, std::min(maxByte, len));
- return mByteHash;
- }
+ int32_t getByteBlock(block_t* block, size_t len) const;
// Set the byte block and the hash.
- void setByteBlock(int hash, const block_t* block, size_t len) {
- memcpy((void*) mByteBlock, block, len = std::min(maxByte, len));
- mByteHash = hash;
+ void setByteBlock(int hash, const block_t* block, size_t len);
+
+ private:
+
+ // A convenience function to compute the offset between two unlike pointers.
+ static size_t offset(void const* base, void const* member) {
+ return reinterpret_cast<uintptr_t>(member) - reinterpret_cast<std::uintptr_t>(base);
+ }
+
+ // Return the address of the nonce array.
+ volatile nonce_t* nonce() const {
+ // The array is located at an offset from <this>.
+ return reinterpret_cast<nonce_t*>(
+ reinterpret_cast<std::uintptr_t>(this) + mNonceOffset);
+ }
+
+ // Return the address of the byte block array.
+ volatile block_t* byteBlock() const {
+ // The array is located at an offset from <this>.
+ return reinterpret_cast<block_t*>(
+ reinterpret_cast<std::uintptr_t>(this) + mByteOffset);
}
};
/**
- * Sizing parameters for the system_server PropertyInvalidatedCache support. A client can
- * retrieve the values through the accessors in CacheNonce instances.
+ * A cache nonce block contains an array of std::atomic<int64_t> and an array of bytes. The
+ * byte array has an associated hash. This class provides methods to read and write the fields
+ * of the block but it does not interpret the fields.
+ *
+ * On initialization, all fields are set to zero.
+ *
+ * In general, methods do not report errors. This allows the methods to be used in
+ * CriticalNative JNI APIs.
+ *
+ * The template is parameterized by the number of nonces it supports and the number of bytes in
+ * the string block.
*/
-static const int MAX_NONCE = 64;
-static const int BYTE_BLOCK_SIZE = 8192;
+template<int maxNonce, size_t maxByte> class CacheNonce : public NonceStore {
+
+ // The array of nonces
+ volatile nonce_t mNonce[maxNonce];
-// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes.
-typedef CacheNonce<MAX_NONCE, BYTE_BLOCK_SIZE> SystemCacheNonce;
+ // The byte array. This is not atomic but it is guarded by the mByteHash.
+ volatile block_t mByteBlock[maxByte];
+
+ public:
+ // Construct and initialize the memory.
+ CacheNonce() :
+ NonceStore(maxNonce, maxByte, &mNonce[0], &mByteBlock[0])
+ {
+ for (int i = 0; i < maxNonce; i++) {
+ mNonce[i] = UNSET;
+ }
+ mByteHash = UNSET;
+ memset((void*) mByteBlock, UNSET, sizeof(mByteBlock));
+ }
+};
-// The goal of this assertion is to ensure that the data structure is the same size across 32-bit
-// and 64-bit systems.
-static_assert(sizeof(SystemCacheNonce) == SystemCacheNonce::expectedSize,
- "Unexpected SystemCacheNonce size");
+// The CacheNonce for system server holds 64 nonces with a string block of 8192 bytes. This is
+// more than enough for system_server PropertyInvalidatedCache support. The configuration
+// values are not defined as visible constants. Clients should use the accessors on the
+// SystemCacheNonce instance if they need the sizing parameters.
+typedef CacheNonce</* max nonce */ 64, /* byte block size */ 8192> SystemCacheNonce;
-} // namespace PropertyInvalidatedCache
-} // namespace app
-} // namespace android
+} // namespace android.app.PropertyInvalidatedCache