Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f95c2a0d authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 12766088 from 0b3b6a65 to 25Q1-release

Change-Id: I3ee26d153829881f3a167a83ceddc6beec218991
parents 8ebc0db4 0b3b6a65
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -554,10 +554,16 @@ public final class SQLiteRawStatement implements Closeable {
     *
     * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_type</a>
     *
     * If the row has no data then a {@link SQLiteMisuseException} is thrown.  This condition can
     * occur the last call to {@link #step()} returned false or if {@link #step()} was not called
     * before the statement was created or after the last call to {@link #reset()}.  Note that
     * {@link SQLiteMisuseException} may be thrown for other reasons.
     *
     * @param columnIndex The index of a column in the result row. It is zero-based.
     * @return The type of the value in the column of the result row.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data.
     * @throws SQLiteException if a native error occurs.
     */
    @SQLiteDataType
@@ -580,6 +586,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The name of the column in the result row.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name.
     */
    @NonNull
@@ -606,6 +613,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The length, in bytes, of the value in the column.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int getColumnLength(int columnIndex) {
@@ -631,6 +639,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as a blob, or null if the column is NULL.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    @Nullable
@@ -664,6 +673,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws IllegalArgumentException if the buffer is too small for offset+length.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset,
@@ -691,6 +701,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of a column as a double.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public double getColumnDouble(int columnIndex) {
@@ -715,6 +726,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as an int.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public int getColumnInt(int columnIndex) {
@@ -739,6 +751,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as an long.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    public long getColumnLong(int columnIndex) {
@@ -763,6 +776,7 @@ public final class SQLiteRawStatement implements Closeable {
     * @return The value of the column as a string.
     * @throws IllegalStateException if the statement is closed or this is a foreign thread.
     * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range.
     * @throws SQLiteMisuseException if the row has no data. See {@link #getColumnType()}.
     * @throws SQLiteException if a native error occurs.
     */
    @NonNull
+63 −10
Original line number Diff line number Diff line
@@ -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[] = {
+97 −87
Original line number Diff line number Diff line
@@ -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
+24 −15
Original line number Diff line number Diff line
@@ -70,12 +70,32 @@ static void throwInvalidParameter(JNIEnv *env, jlong stmtPtr, jint index) {
    }
}

// If the last operation failed, throw an exception and return true.  Otherwise return false.
static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
    switch (sqlite3_errcode(db(stmtPtr))) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW: return false;
    }
    throw_sqlite3_exception(env, db(stmtPtr), nullptr);
    return true;
}

// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out
// of bounds.  It returns true if an exception was thrown.
// This throws a SQLiteBindOrColumnIndexOutOfRangeException if the column index is out of
// bounds.  It throws SQLiteMisuseException if the statement's column count is zero; that
// generally occurs because the client has forgotten to call step() or the client has stepped
// past the end of the query.  The function returns true if an exception was thrown.
static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    if (col < 0 || col >= sqlite3_data_count(stmt(stmtPtr))) {
    int count = sqlite3_data_count(stmt(stmtPtr));
    if (throwIfError(env, stmtPtr)) {
        return true;
    } else if (count == 0) {
        // A count of zero indicates a misuse: the statement has never been step()'ed.
        const char* message = "row has no data";
        const char* errmsg = sqlite3_errstr(SQLITE_MISUSE);
        throw_sqlite3_exception(env, SQLITE_MISUSE, errmsg, message);
        return true;
    } else if (col < 0 || col >= count) {
        std::string message = android::base::StringPrintf(
            "column index %d out of bounds [0,%d]", col, count - 1);
        char const * errmsg = sqlite3_errstr(SQLITE_RANGE);
@@ -86,17 +106,6 @@ static bool throwIfInvalidColumn(JNIEnv *env, jlong stmtPtr, jint col) {
    }
}

// If the last operation failed, throw an exception and return true.  Otherwise return false.
static bool throwIfError(JNIEnv *env, jlong stmtPtr) {
    switch (sqlite3_errcode(db(stmtPtr))) {
        case SQLITE_OK:
        case SQLITE_DONE:
        case SQLITE_ROW: return false;
    }
    throw_sqlite3_exception(env, db(stmtPtr), nullptr);
    return true;
}

static jint bindParameterCount(JNIEnv* env, jclass, jlong stmtPtr) {
    return sqlite3_bind_parameter_count(stmt(stmtPtr));
}
+25 −2
Original line number Diff line number Diff line
@@ -1010,6 +1010,7 @@ public class SQLiteRawStatementTest {
        mDatabase.beginTransaction();
        try {
            mDatabase.execSQL("CREATE TABLE t1 (i int, j int);");
            mDatabase.execSQL("INSERT INTO t1 (i, j) VALUES (2, 20)");
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
@@ -1017,13 +1018,35 @@ public class SQLiteRawStatementTest {

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            s.step();
            s.getColumnText(5); // out-of-range column
            assertTrue(s.step());
            s.getColumnText(5); // out-of-range column: the range is [0,2).
            fail("JNI exception not thrown");
        } catch (SQLiteBindOrColumnIndexOutOfRangeException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            // Do not step the statement.  The column count will be zero.
            s.getColumnText(5); // out-of-range column: never stepped.
            fail("JNI exception not thrown");
        } catch (SQLiteMisuseException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }

        mDatabase.beginTransactionReadOnly();
        try (SQLiteRawStatement s = mDatabase.createRawStatement("SELECT * from t1")) {
            // Do not step the statement.  The column count will be zero.
            s.getColumnText(0); // out-of-range column: never stepped.
            fail("JNI exception not thrown");
        } catch (SQLiteMisuseException e) {
            // Passing case.
        } finally {
            mDatabase.endTransaction();
        }
    }
}