Merge "External Camera: support less thumbnail sizes" into qt-dev
diff --git a/automotive/evs/1.0/vts/functional/FormatConvert.cpp b/automotive/evs/1.0/vts/functional/FormatConvert.cpp
index 1e8929d..3d82d32 100644
--- a/automotive/evs/1.0/vts/functional/FormatConvert.cpp
+++ b/automotive/evs/1.0/vts/functional/FormatConvert.cpp
@@ -38,7 +38,8 @@
 }
 
 
-static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const unsigned char Vin) {
+static uint32_t yuvToRgbx(const unsigned char Y, const unsigned char Uin, const unsigned char Vin,
+                          bool bgrxFormat = false) {
     // Don't use this if you want to see the best performance.  :)
     // Better to do this in a pixel shader if we really have to, but on actual
     // embedded hardware we expect to be able to texture directly from the YUV data
@@ -52,16 +53,24 @@
     unsigned char G = (unsigned char)clamp(Gf, 0.0f, 255.0f);
     unsigned char B = (unsigned char)clamp(Bf, 0.0f, 255.0f);
 
-    return (R      ) |
-           (G <<  8) |
-           (B << 16) |
-           0xFF000000;  // Fill the alpha channel with ones
+    if (!bgrxFormat) {
+        return (R      ) |
+               (G <<  8) |
+               (B << 16) |
+               0xFF000000;  // Fill the alpha channel with ones
+    } else {
+        return (R << 16) |
+               (G <<  8) |
+               (B      ) |
+               0xFF000000;  // Fill the alpha channel with ones
+    }
 }
 
 
 void copyNV21toRGB32(unsigned width, unsigned height,
                      uint8_t* src,
-                     uint32_t* dst, unsigned dstStridePixels)
+                     uint32_t* dst, unsigned dstStridePixels,
+                     bool bgrxFormat)
 {
     // The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
     // U/V array.  It assumes an even width and height for the overall image, and a horizontal
@@ -84,7 +93,7 @@
         for (unsigned c = 0; c < width; c++) {
             unsigned uCol = (c & ~1);   // uCol is always even and repeats 1:2 with Y values
             unsigned vCol = uCol | 1;   // vCol is always odd
-            rowDest[c] = yuvToRgbx(rowY[c], rowUV[uCol], rowUV[vCol]);
+            rowDest[c] = yuvToRgbx(rowY[c], rowUV[uCol], rowUV[vCol], bgrxFormat);
         }
     }
 }
@@ -92,7 +101,8 @@
 
 void copyYV12toRGB32(unsigned width, unsigned height,
                      uint8_t* src,
-                     uint32_t* dst, unsigned dstStridePixels)
+                     uint32_t* dst, unsigned dstStridePixels,
+                     bool bgrxFormat)
 {
     // The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed
     // by another 1/2 x 1/2 V array.  It assumes an even width and height for the overall image,
@@ -118,7 +128,7 @@
         uint32_t* rowDest = dst + r*dstStridePixels;
 
         for (unsigned c = 0; c < width; c++) {
-            rowDest[c] = yuvToRgbx(rowY[c], rowU[c], rowV[c]);
+            rowDest[c] = yuvToRgbx(rowY[c], rowU[c], rowV[c], bgrxFormat);
         }
     }
 }
@@ -126,7 +136,8 @@
 
 void copyYUYVtoRGB32(unsigned width, unsigned height,
                      uint8_t* src, unsigned srcStridePixels,
-                     uint32_t* dst, unsigned dstStridePixels)
+                     uint32_t* dst, unsigned dstStridePixels,
+                     bool bgrxFormat)
 {
     uint32_t* srcWords = (uint32_t*)src;
 
@@ -144,8 +155,8 @@
             uint8_t V  = (srcPixel >> 24) & 0xFF;
 
             // On the RGB output, we're writing one pixel at a time
-            *(dst+0) = yuvToRgbx(Y1, U, V);
-            *(dst+1) = yuvToRgbx(Y2, U, V);
+            *(dst+0) = yuvToRgbx(Y1, U, V, bgrxFormat);
+            *(dst+1) = yuvToRgbx(Y2, U, V, bgrxFormat);
             dst += 2;
         }
 
@@ -156,6 +167,30 @@
 }
 
 
+void copyNV21toBGR32(unsigned width, unsigned height,
+                     uint8_t* src,
+                     uint32_t* dst, unsigned dstStridePixels)
+{
+    return copyNV21toRGB32(width, height, src, dst, dstStridePixels, true);
+}
+
+
+void copyYV12toBGR32(unsigned width, unsigned height,
+                     uint8_t* src,
+                     uint32_t* dst, unsigned dstStridePixels)
+{
+    return copyYV12toRGB32(width, height, src, dst, dstStridePixels, true);
+}
+
+
+void copyYUYVtoBGR32(unsigned width, unsigned height,
+                     uint8_t* src, unsigned srcStridePixels,
+                     uint32_t* dst, unsigned dstStridePixels)
+{
+    return copyYUYVtoRGB32(width, height, src, srcStridePixels, dst, dstStridePixels, true);
+}
+
+
 void copyMatchedInterleavedFormats(unsigned width, unsigned height,
                                    void* src, unsigned srcStridePixels,
                                    void* dst, unsigned dstStridePixels,
diff --git a/automotive/evs/1.0/vts/functional/FormatConvert.h b/automotive/evs/1.0/vts/functional/FormatConvert.h
index 3ff1eec..4a94f99 100644
--- a/automotive/evs/1.0/vts/functional/FormatConvert.h
+++ b/automotive/evs/1.0/vts/functional/FormatConvert.h
@@ -21,31 +21,45 @@
 #include <stdint.h>
 
 
-// Given an image buffer in NV21 format (HAL_PIXEL_FORMAT_YCRCB_420_SP), output 32bit RGBx values.
-// The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
+// Given an image buffer in NV21 format (HAL_PIXEL_FORMAT_YCRCB_420_SP), output 32bit RGBx/BGRx
+// values.  The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
 // U/V array.  It assumes an even width and height for the overall image, and a horizontal
 // stride that is an even multiple of 16 bytes for both the Y and UV arrays.
 void copyNV21toRGB32(unsigned width, unsigned height,
                      uint8_t* src,
+                     uint32_t* dst, unsigned dstStridePixels,
+                     bool bgrxFormat = false);
+
+void copyNV21toBGR32(unsigned width, unsigned height,
+                     uint8_t* src,
                      uint32_t* dst, unsigned dstStridePixels);
 
 
-// Given an image buffer in YV12 format (HAL_PIXEL_FORMAT_YV12), output 32bit RGBx values.
+// Given an image buffer in YV12 format (HAL_PIXEL_FORMAT_YV12), output 32bit RGBx/BGRx values.
 // The YV12 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 U array, followed
 // by another 1/2 x 1/2 V array.  It assumes an even width and height for the overall image,
 // and a horizontal stride that is an even multiple of 16 bytes for each of the Y, U,
 // and V arrays.
 void copyYV12toRGB32(unsigned width, unsigned height,
                      uint8_t* src,
+                     uint32_t* dst, unsigned dstStridePixels,
+                     bool bgrxFormat = false);
+
+void copyYV12toBGR32(unsigned width, unsigned height,
+                     uint8_t* src,
                      uint32_t* dst, unsigned dstStridePixels);
 
-
-// Given an image buffer in YUYV format (HAL_PIXEL_FORMAT_YCBCR_422_I), output 32bit RGBx values.
-// The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
+// Given an image buffer in YUYV format (HAL_PIXEL_FORMAT_YCBCR_422_I), output 32bit RGBx/BGRx
+// values.  The NV21 format provides a Y array of 8bit values, followed by a 1/2 x 1/2 interleaved
 // U/V array.  It assumes an even width and height for the overall image, and a horizontal
 // stride that is an even multiple of 16 bytes for both the Y and UV arrays.
 void copyYUYVtoRGB32(unsigned width, unsigned height,
                      uint8_t* src, unsigned srcStrideBytes,
+                     uint32_t* dst, unsigned dstStrideBytes,
+                     bool bgrxFormat = false);
+
+void copyYUYVtoBGR32(unsigned width, unsigned height,
+                     uint8_t* src, unsigned srcStrideBytes,
                      uint32_t* dst, unsigned dstStrideBytes);
 
 
diff --git a/automotive/evs/1.0/vts/functional/FrameHandler.cpp b/automotive/evs/1.0/vts/functional/FrameHandler.cpp
index a69f72b..d44ba41 100644
--- a/automotive/evs/1.0/vts/functional/FrameHandler.cpp
+++ b/automotive/evs/1.0/vts/functional/FrameHandler.cpp
@@ -231,16 +231,12 @@
     uint8_t* srcPixels = nullptr;
     src->lock(GRALLOC_USAGE_SW_READ_OFTEN, (void**)&srcPixels);
 
-    // Lock our target buffer for writing (should be RGBA8888 format)
+    // Lock our target buffer for writing (should be either RGBA8888 or BGRA8888 format)
     uint32_t* tgtPixels = nullptr;
     tgt->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)&tgtPixels);
 
     if (srcPixels && tgtPixels) {
-        if (tgtBuffer.format != HAL_PIXEL_FORMAT_RGBA_8888) {
-            // We always expect 32 bit RGB for the display output for now.  Is there a need for 565?
-            ALOGE("Diplay buffer is always expected to be 32bit RGBA");
-            success = false;
-        } else {
+        if (tgtBuffer.format == HAL_PIXEL_FORMAT_RGBA_8888) {
             if (srcBuffer.format == HAL_PIXEL_FORMAT_YCRCB_420_SP) {   // 420SP == NV21
                 copyNV21toRGB32(width, height,
                                 srcPixels,
@@ -258,7 +254,36 @@
                                               srcPixels, srcBuffer.stride,
                                               tgtPixels, tgtBuffer.stride,
                                               tgtBuffer.pixelSize);
+            } else {
+                ALOGE("Camera buffer format is not supported");
+                success = false;
             }
+        } else if (tgtBuffer.format == HAL_PIXEL_FORMAT_BGRA_8888) {
+            if (srcBuffer.format == HAL_PIXEL_FORMAT_YCRCB_420_SP) {   // 420SP == NV21
+                copyNV21toBGR32(width, height,
+                                srcPixels,
+                                tgtPixels, tgtBuffer.stride);
+            } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YV12) { // YUV_420P == YV12
+                copyYV12toBGR32(width, height,
+                                srcPixels,
+                                tgtPixels, tgtBuffer.stride);
+            } else if (srcBuffer.format == HAL_PIXEL_FORMAT_YCBCR_422_I) { // YUYV
+                copyYUYVtoBGR32(width, height,
+                                srcPixels, srcBuffer.stride,
+                                tgtPixels, tgtBuffer.stride);
+            } else if (srcBuffer.format == tgtBuffer.format) {  // 32bit RGBA
+                copyMatchedInterleavedFormats(width, height,
+                                              srcPixels, srcBuffer.stride,
+                                              tgtPixels, tgtBuffer.stride,
+                                              tgtBuffer.pixelSize);
+            } else {
+                ALOGE("Camera buffer format is not supported");
+                success = false;
+            }
+        } else {
+            // We always expect 32 bit RGB for the display output for now.  Is there a need for 565?
+            ALOGE("Diplay buffer is always expected to be 32bit RGBA");
+            success = false;
         }
     } else {
         ALOGE("Failed to lock buffer contents for contents transfer");
diff --git a/current.txt b/current.txt
index 3a6241c..d1c5290 100644
--- a/current.txt
+++ b/current.txt
@@ -515,7 +515,7 @@
 92714960d1a53fc2ec557302b41c7cc93d2636d8364a44bd0f85be0c92927ff8 android.hardware.neuralnetworks@1.2::IExecutionCallback
 36e1064c869965dee533c537cefbe87e54db8bd8cd45be7e0e93e00e8a43863a android.hardware.neuralnetworks@1.2::IPreparedModel
 e1c734d1545e1a4ae749ff1dd9704a8e594c59aea7c8363159dc258e93e0df3b android.hardware.neuralnetworks@1.2::IPreparedModelCallback
-73e995644b1bb2678ec3ab850feb7a1b4495501953951c22316cefd67b900b3e android.hardware.neuralnetworks@1.2::types
+51725cd8541bf459190d8a911f9aae9a0825678256b88fc39fac81d69feb04af android.hardware.neuralnetworks@1.2::types
 cf7a4ba516a638f9b82a249c91fb603042c2d9ca43fd5aad9cf6c0401ed2a5d7 android.hardware.nfc@1.2::INfc
 abf98c2ae08bf765db54edc8068e36d52eb558cff6706b6fd7c18c65a1f3fc18 android.hardware.nfc@1.2::types
 4cb252dc6372a874aef666b92a6e9529915aa187521a700f0789065c3c702ead android.hardware.power.stats@1.0::IPowerStats
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index fc96724..cf12e2a 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -414,7 +414,7 @@
     EXPECT_NE(strcmp(property_value, "nogood"), 0);
     string prop_string(property_value);
     EXPECT_EQ(prop_string.size(), 64);
-    EXPECT_EQ(0, memcmp(verified_boot_hash.data(), prop_string.data(), verified_boot_hash.size()));
+    EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));
 
     property_get("ro.boot.vbmeta.device_state", property_value, "nogood");
     EXPECT_NE(property_value, "nogood");
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 087249a..31576fa 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -3531,6 +3531,8 @@
      * * {@link OperandType::TENSOR_INT32}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM}
      *
+     * Supported tensor rank: from 1.
+     *
      * Inputs:
      * * 0: A tensor.
      * * 1: A tensor of the same {@link OperandType} and compatible dimensions
@@ -3552,6 +3554,8 @@
      * * {@link OperandType::TENSOR_INT32}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM}
      *
+     * Supported tensor rank: from 1.
+     *
      * Inputs:
      * * 0: A tensor.
      * * 1: A tensor of the same {@link OperandType} and compatible dimensions
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 167fc09..3b4acde 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -16,21 +16,22 @@
 
 #define LOG_TAG "neuralnetworks_hidl_hal_test"
 
-#include "VtsHalNeuralnetworks.h"
+#include <android-base/logging.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <ftw.h>
+#include <gtest/gtest.h>
+#include <hidlmemory/mapping.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <random>
 
 #include "Callbacks.h"
 #include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
-
-#include <android-base/logging.h>
-#include <android/hidl/memory/1.0/IMemory.h>
-#include <hidlmemory/mapping.h>
-#include <cstdio>
-#include <cstdlib>
-#include <random>
-
-#include <gtest/gtest.h>
+#include "VtsHalNeuralnetworks.h"
 
 namespace android {
 namespace hardware {
@@ -46,7 +47,7 @@
 
 namespace {
 
-// In frameworks/ml/nn/runtime/tests/generated/, creates a hidl model of mobilenet.
+// In frameworks/ml/nn/runtime/test/generated/, creates a hidl model of mobilenet.
 #include "examples/mobilenet_224_gender_basic_fixed.example.cpp"
 #include "vts_models/mobilenet_224_gender_basic_fixed.model.cpp"
 
@@ -89,6 +90,118 @@
     createCacheHandles(fileGroups, std::vector<AccessMode>(fileGroups.size(), mode), handles);
 }
 
+// Create a chain of broadcast operations. The second operand is always constant tensor [1].
+// For simplicity, activation scalar is shared. The second operand is not shared
+// in the model to let driver maintain a non-trivial size of constant data and the corresponding
+// data locations in cache.
+//
+//                --------- activation --------
+//                ↓      ↓      ↓             ↓
+// E.g. input -> ADD -> ADD -> ADD -> ... -> ADD -> output
+//                ↑      ↑      ↑             ↑
+//               [1]    [1]    [1]           [1]
+//
+Model createLargeTestModel(OperationType op, uint32_t len) {
+    // Model operations and operands.
+    std::vector<Operation> operations(len);
+    std::vector<Operand> operands(len * 2 + 2);
+
+    // The constant buffer pool. This contains the activation scalar, followed by the
+    // per-operation constant operands.
+    std::vector<uint8_t> operandValues(sizeof(int32_t) + len * sizeof(float));
+
+    // The activation scalar, value = 0.
+    operands[0] = {
+            .type = OperandType::INT32,
+            .dimensions = {},
+            .numberOfConsumers = len,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = sizeof(int32_t)},
+    };
+    memset(operandValues.data(), 0, sizeof(int32_t));
+
+    const float floatBufferValue = 1.0f;
+    for (uint32_t i = 0; i < len; i++) {
+        const uint32_t firstInputIndex = i * 2 + 1;
+        const uint32_t secondInputIndex = firstInputIndex + 1;
+        const uint32_t outputIndex = secondInputIndex + 1;
+
+        // The first operation input.
+        operands[firstInputIndex] = {
+                .type = OperandType::TENSOR_FLOAT32,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = (i == 0 ? OperandLifeTime::MODEL_INPUT
+                                    : OperandLifeTime::TEMPORARY_VARIABLE),
+                .location = {},
+        };
+
+        // The second operation input, value = 1.
+        operands[secondInputIndex] = {
+                .type = OperandType::TENSOR_FLOAT32,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = 0.0f,
+                .zeroPoint = 0,
+                .lifetime = OperandLifeTime::CONSTANT_COPY,
+                .location = {.poolIndex = 0,
+                             .offset = static_cast<uint32_t>(i * sizeof(float) + sizeof(int32_t)),
+                             .length = sizeof(float)},
+        };
+        memcpy(operandValues.data() + sizeof(int32_t) + i * sizeof(float), &floatBufferValue,
+               sizeof(float));
+
+        // The operation. All operations share the same activation scalar.
+        // The output operand is created as an input in the next iteration of the loop, in the case
+        // of all but the last member of the chain; and after the loop as a model output, in the
+        // case of the last member of the chain.
+        operations[i] = {
+                .type = op,
+                .inputs = {firstInputIndex, secondInputIndex, /*activation scalar*/ 0},
+                .outputs = {outputIndex},
+        };
+    }
+
+    // The model output.
+    operands.back() = {
+            .type = OperandType::TENSOR_FLOAT32,
+            .dimensions = {1},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {},
+    };
+
+    const std::vector<uint32_t> inputIndexes = {1};
+    const std::vector<uint32_t> outputIndexes = {len * 2 + 1};
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+            .operands = operands,
+            .operations = operations,
+            .inputIndexes = inputIndexes,
+            .outputIndexes = outputIndexes,
+            .operandValues = operandValues,
+            .pools = pools,
+    };
+}
+
+// MixedTypedExample is defined in frameworks/ml/nn/tools/test_generator/include/TestHarness.h.
+// This function assumes the operation is always ADD.
+std::vector<MixedTypedExample> getLargeModelExamples(uint32_t len) {
+    float outputValue = 1.0f + static_cast<float>(len);
+    return {{.operands = {
+                     // Input
+                     {.operandDimensions = {{0, {1}}}, .float32Operands = {{0, {1.0f}}}},
+                     // Output
+                     {.operandDimensions = {{0, {1}}}, .float32Operands = {{0, {outputValue}}}}}}};
+};
+
 }  // namespace
 
 // Tag for the compilation caching tests.
@@ -139,11 +252,13 @@
     }
 
     void TearDown() override {
-        // The tmp directory is only removed when the driver reports caching not supported,
-        // otherwise it is kept for debugging purpose.
-        if (!mIsCachingSupported) {
-            remove(mTmpCache.c_str());
-            rmdir(mCacheDir.c_str());
+        // If the test passes, remove the tmp directory.  Otherwise, keep it for debugging purposes.
+        if (!::testing::Test::HasFailure()) {
+            // Recursively remove the cache directory specified by mCacheDir.
+            auto callback = [](const char* entry, const struct stat*, int, struct FTW*) {
+                return remove(entry);
+            };
+            nftw(mCacheDir.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
         }
         NeuralnetworksHidlTest::TearDown();
     }
@@ -864,6 +979,212 @@
     }
 }
 
+// Copy file contents between file groups.
+// The outer vector corresponds to handles and the inner vector is for fds held by each handle.
+// The outer vector sizes must match and the inner vectors must have size = 1.
+static void copyCacheFiles(const std::vector<std::vector<std::string>>& from,
+                           const std::vector<std::vector<std::string>>& to) {
+    constexpr size_t kBufferSize = 1000000;
+    uint8_t buffer[kBufferSize];
+
+    ASSERT_EQ(from.size(), to.size());
+    for (uint32_t i = 0; i < from.size(); i++) {
+        ASSERT_EQ(from[i].size(), 1u);
+        ASSERT_EQ(to[i].size(), 1u);
+        int fromFd = open(from[i][0].c_str(), O_RDONLY);
+        int toFd = open(to[i][0].c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+        ASSERT_GE(fromFd, 0);
+        ASSERT_GE(toFd, 0);
+
+        ssize_t readBytes;
+        while ((readBytes = read(fromFd, &buffer, kBufferSize)) > 0) {
+            ASSERT_EQ(write(toFd, &buffer, readBytes), readBytes);
+        }
+        ASSERT_GE(readBytes, 0);
+
+        close(fromFd);
+        close(toFd);
+    }
+}
+
+// Number of operations in the large test model.
+constexpr uint32_t kLargeModelSize = 100;
+constexpr uint32_t kNumIterationsTOCTOU = 100;
+
+TEST_F(CompilationCachingTest, SaveToCache_TOCTOU) {
+    if (!mIsCachingSupported) return;
+
+    // Save the testModelMul compilation to cache.
+    Model testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache[0].append("_mul");
+    }
+    {
+        hidl_vec<hidl_handle> modelCache, dataCache;
+        createCacheHandles(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        bool supported;
+        saveModelToCache(testModelMul, modelCache, dataCache, &supported);
+        if (checkEarlyTermination(supported)) return;
+    }
+
+    // Use a different token for testModelAdd.
+    mToken[0]++;
+
+    // This test is probabilistic, so we run it multiple times.
+    Model testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+        // Save the testModelAdd compilation to cache.
+        {
+            bool supported;
+            hidl_vec<hidl_handle> modelCache, dataCache;
+            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+
+            // Spawn a thread to copy the cache content concurrently while saving to cache.
+            std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
+            saveModelToCache(testModelAdd, modelCache, dataCache, &supported);
+            thread.join();
+            if (checkEarlyTermination(supported)) return;
+        }
+
+        // Retrieve preparedModel from cache.
+        {
+            sp<IPreparedModel> preparedModel = nullptr;
+            ErrorStatus status;
+            hidl_vec<hidl_handle> modelCache, dataCache;
+            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+
+            // The preparation may fail or succeed, but must not crash. If the preparation succeeds,
+            // the prepared model must be executed with the correct result and not crash.
+            if (status != ErrorStatus::NONE) {
+                ASSERT_EQ(preparedModel, nullptr);
+            } else {
+                ASSERT_NE(preparedModel, nullptr);
+                generated_tests::EvaluatePreparedModel(
+                        preparedModel, [](int) { return false; },
+                        getLargeModelExamples(kLargeModelSize),
+                        testModelAdd.relaxComputationFloat32toFloat16,
+                        /*testDynamicOutputShape=*/false);
+            }
+        }
+    }
+}
+
+TEST_F(CompilationCachingTest, PrepareFromCache_TOCTOU) {
+    if (!mIsCachingSupported) return;
+
+    // Save the testModelMul compilation to cache.
+    Model testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache[0].append("_mul");
+    }
+    {
+        hidl_vec<hidl_handle> modelCache, dataCache;
+        createCacheHandles(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        bool supported;
+        saveModelToCache(testModelMul, modelCache, dataCache, &supported);
+        if (checkEarlyTermination(supported)) return;
+    }
+
+    // Use a different token for testModelAdd.
+    mToken[0]++;
+
+    // This test is probabilistic, so we run it multiple times.
+    Model testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+        // Save the testModelAdd compilation to cache.
+        {
+            bool supported;
+            hidl_vec<hidl_handle> modelCache, dataCache;
+            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            saveModelToCache(testModelAdd, modelCache, dataCache, &supported);
+            if (checkEarlyTermination(supported)) return;
+        }
+
+        // Retrieve preparedModel from cache.
+        {
+            sp<IPreparedModel> preparedModel = nullptr;
+            ErrorStatus status;
+            hidl_vec<hidl_handle> modelCache, dataCache;
+            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+
+            // Spawn a thread to copy the cache content concurrently while preparing from cache.
+            std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+            thread.join();
+
+            // The preparation may fail or succeed, but must not crash. If the preparation succeeds,
+            // the prepared model must be executed with the correct result and not crash.
+            if (status != ErrorStatus::NONE) {
+                ASSERT_EQ(preparedModel, nullptr);
+            } else {
+                ASSERT_NE(preparedModel, nullptr);
+                generated_tests::EvaluatePreparedModel(
+                        preparedModel, [](int) { return false; },
+                        getLargeModelExamples(kLargeModelSize),
+                        testModelAdd.relaxComputationFloat32toFloat16,
+                        /*testDynamicOutputShape=*/false);
+            }
+        }
+    }
+}
+
+TEST_F(CompilationCachingTest, ReplaceSecuritySensitiveCache) {
+    if (!mIsCachingSupported) return;
+
+    // Save the testModelMul compilation to cache.
+    Model testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache[0].append("_mul");
+    }
+    {
+        hidl_vec<hidl_handle> modelCache, dataCache;
+        createCacheHandles(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        bool supported;
+        saveModelToCache(testModelMul, modelCache, dataCache, &supported);
+        if (checkEarlyTermination(supported)) return;
+    }
+
+    // Use a different token for testModelAdd.
+    mToken[0]++;
+
+    // Save the testModelAdd compilation to cache.
+    Model testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    {
+        bool supported;
+        hidl_vec<hidl_handle> modelCache, dataCache;
+        createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(testModelAdd, modelCache, dataCache, &supported);
+        if (checkEarlyTermination(supported)) return;
+    }
+
+    // Replace the model cache of testModelAdd with testModelMul.
+    copyCacheFiles(modelCacheMul, mModelCache);
+
+    // Retrieve the preparedModel from cache, expect failure.
+    {
+        sp<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_vec<hidl_handle> modelCache, dataCache;
+        createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
 class CompilationCachingSecurityTest : public CompilationCachingTest,
                                        public ::testing::WithParamInterface<uint32_t> {
   protected:
@@ -879,34 +1200,15 @@
         return dis(generator);
     }
 
-    const uint32_t kSeed = GetParam();
-    std::mt19937 generator;
-};
-
-TEST_P(CompilationCachingSecurityTest, CorruptedSecuritySensitiveCache) {
-    if (!mIsCachingSupported) return;
-
-    // Create test HIDL model and compile.
-    Model testModel = createTestModel();
-
-    for (uint32_t i = 0; i < mNumModelCache; i++) {
-        // Save the compilation to cache.
-        {
-            bool supported;
-            hidl_vec<hidl_handle> modelCache, dataCache;
-            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
-            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-            saveModelToCache(testModel, modelCache, dataCache, &supported);
-            if (checkEarlyTermination(supported)) return;
-        }
-
-        // Randomly flip one single bit of the cache entry.
-        FILE* pFile = fopen(mModelCache[i][0].c_str(), "r+");
+    // Randomly flip one single bit of the cache entry.
+    void flipOneBitOfCache(const std::string& filename, bool* skip) {
+        FILE* pFile = fopen(filename.c_str(), "r+");
         ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
         long int fileSize = ftell(pFile);
         if (fileSize == 0) {
             fclose(pFile);
-            continue;
+            *skip = true;
+            return;
         }
         ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
         int readByte = fgetc(pFile);
@@ -914,28 +1216,29 @@
         ASSERT_EQ(fseek(pFile, -1, SEEK_CUR), 0);
         ASSERT_NE(fputc(static_cast<uint8_t>(readByte) ^ (1U << getRandomInt(0, 7)), pFile), EOF);
         fclose(pFile);
-
-        // Retrieve preparedModel from cache, expect failure.
-        {
-            sp<IPreparedModel> preparedModel = nullptr;
-            ErrorStatus status;
-            hidl_vec<hidl_handle> modelCache, dataCache;
-            createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
-            createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
-            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
-            ASSERT_EQ(preparedModel, nullptr);
-        }
+        *skip = false;
     }
-}
 
-TEST_P(CompilationCachingSecurityTest, WrongLengthSecuritySensitiveCache) {
-    if (!mIsCachingSupported) return;
+    // Randomly append bytes to the cache entry.
+    void appendBytesToCache(const std::string& filename, bool* skip) {
+        FILE* pFile = fopen(filename.c_str(), "a");
+        uint32_t appendLength = getRandomInt(1, 256);
+        for (uint32_t i = 0; i < appendLength; i++) {
+            ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
+        }
+        fclose(pFile);
+        *skip = false;
+    }
 
-    // Create test HIDL model and compile.
-    Model testModel = createTestModel();
+    enum class ExpectedResult { GENERAL_FAILURE, NOT_CRASH };
 
-    for (uint32_t i = 0; i < mNumModelCache; i++) {
+    // Test if the driver behaves as expected when given corrupted cache or token.
+    // The modifier will be invoked after save to cache but before prepare from cache.
+    // The modifier accepts one pointer argument "skip" as the returning value, indicating
+    // whether the test should be skipped or not.
+    void testCorruptedCache(ExpectedResult expected, std::function<void(bool*)> modifier) {
+        Model testModel = createTestModel();
+
         // Save the compilation to cache.
         {
             bool supported;
@@ -946,15 +1249,11 @@
             if (checkEarlyTermination(supported)) return;
         }
 
-        // Randomly append bytes to the cache entry.
-        FILE* pFile = fopen(mModelCache[i][0].c_str(), "a");
-        uint32_t appendLength = getRandomInt(1, 256);
-        for (uint32_t i = 0; i < appendLength; i++) {
-            ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
-        }
-        fclose(pFile);
+        bool skip = false;
+        modifier(&skip);
+        if (skip) return;
 
-        // Retrieve preparedModel from cache, expect failure.
+        // Retrieve preparedModel from cache.
         {
             sp<IPreparedModel> preparedModel = nullptr;
             ErrorStatus status;
@@ -962,43 +1261,66 @@
             createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
             createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
             prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
-            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
-            ASSERT_EQ(preparedModel, nullptr);
+
+            switch (expected) {
+                case ExpectedResult::GENERAL_FAILURE:
+                    ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+                    ASSERT_EQ(preparedModel, nullptr);
+                    break;
+                case ExpectedResult::NOT_CRASH:
+                    ASSERT_EQ(preparedModel == nullptr, status != ErrorStatus::NONE);
+                    break;
+                default:
+                    FAIL();
+            }
         }
     }
+
+    const uint32_t kSeed = GetParam();
+    std::mt19937 generator;
+};
+
+TEST_P(CompilationCachingSecurityTest, CorruptedModelCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        testCorruptedCache(ExpectedResult::GENERAL_FAILURE,
+                           [this, i](bool* skip) { flipOneBitOfCache(mModelCache[i][0], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthModelCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        testCorruptedCache(ExpectedResult::GENERAL_FAILURE,
+                           [this, i](bool* skip) { appendBytesToCache(mModelCache[i][0], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, CorruptedDataCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        testCorruptedCache(ExpectedResult::NOT_CRASH,
+                           [this, i](bool* skip) { flipOneBitOfCache(mDataCache[i][0], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthDataCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        testCorruptedCache(ExpectedResult::NOT_CRASH,
+                           [this, i](bool* skip) { appendBytesToCache(mDataCache[i][0], skip); });
+    }
 }
 
 TEST_P(CompilationCachingSecurityTest, WrongToken) {
     if (!mIsCachingSupported) return;
-
-    // Create test HIDL model and compile.
-    Model testModel = createTestModel();
-
-    // Save the compilation to cache.
-    {
-        bool supported;
-        hidl_vec<hidl_handle> modelCache, dataCache;
-        createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
-        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-        saveModelToCache(testModel, modelCache, dataCache, &supported);
-        if (checkEarlyTermination(supported)) return;
-    }
-
-    // Randomly flip one single bit in mToken.
-    uint32_t ind = getRandomInt(0u, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN) - 1);
-    mToken[ind] ^= (1U << getRandomInt(0, 7));
-
-    // Retrieve the preparedModel from cache, expect failure.
-    {
-        sp<IPreparedModel> preparedModel = nullptr;
-        ErrorStatus status;
-        hidl_vec<hidl_handle> modelCache, dataCache;
-        createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
-        createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
-        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
-        ASSERT_EQ(preparedModel, nullptr);
-    }
+    testCorruptedCache(ExpectedResult::GENERAL_FAILURE, [this](bool* skip) {
+        // Randomly flip one single bit in mToken.
+        uint32_t ind =
+                getRandomInt(0u, static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN) - 1);
+        mToken[ind] ^= (1U << getRandomInt(0, 7));
+        *skip = false;
+    });
 }
 
 INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
diff --git a/soundtrigger/2.2/vts/functional/Android.bp b/soundtrigger/2.2/vts/functional/Android.bp
new file mode 100644
index 0000000..08ccd7b
--- /dev/null
+++ b/soundtrigger/2.2/vts/functional/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalSoundtriggerV2_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalSoundtriggerV2_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.soundtrigger@2.0",
+        "android.hardware.soundtrigger@2.1",
+        "android.hardware.soundtrigger@2.2",
+    ],
+    test_suites: ["general-tests"],
+}