Merge "Add NR Cell Identity to CellIdentityNr"
diff --git a/camera/device/3.2/ICameraDevice.hal b/camera/device/3.2/ICameraDevice.hal
index 1f523e4..5236bb1 100644
--- a/camera/device/3.2/ICameraDevice.hal
+++ b/camera/device/3.2/ICameraDevice.hal
@@ -148,7 +148,9 @@
      * session handle for active operations.
      *
      * @param callback Interface to invoke by the HAL for device asynchronous
-     *     events.
+     *     events. For HALs newer than version 3.2, HAL must use castFrom
+     *     method to check the exact version of callback sent by camera service.
+     *
      * @return status Status code for the operation, one of:
      *     OK:
      *         On a successful open of the camera device.
diff --git a/camera/device/3.2/ICameraDeviceSession.hal b/camera/device/3.2/ICameraDeviceSession.hal
index e62dc07..278be5d 100644
--- a/camera/device/3.2/ICameraDeviceSession.hal
+++ b/camera/device/3.2/ICameraDeviceSession.hal
@@ -149,9 +149,8 @@
      *           - Including too many output streams of a certain format.
      *           - Unsupported rotation configuration
      *           - Stream sizes/formats don't satisfy the
-     *             camera3_stream_configuration_t->operation_mode requirements
-     *             for non-NORMAL mode, or the requested operation_mode is not
-     *             supported by the HAL.
+     *             StreamConfigurationMode requirements for non-NORMAL mode, or
+     *             the requested operation_mode is not supported by the HAL.
      *           - Unsupported usage flag
      *         The camera service cannot filter out all possible illegal stream
      *         configurations, since some devices may support more simultaneous
diff --git a/camera/device/3.4/ICameraDeviceSession.hal b/camera/device/3.4/ICameraDeviceSession.hal
index c41d90e..e1663e6 100644
--- a/camera/device/3.4/ICameraDeviceSession.hal
+++ b/camera/device/3.4/ICameraDeviceSession.hal
@@ -54,7 +54,7 @@
      *           - Including too many output streams of a certain format.
      *           - Unsupported rotation configuration
      *           - Stream sizes/formats don't satisfy the
-     *             camera3_stream_configuration_t->operation_mode requirements
+     *             StreamConfigurationMode requirements
      *             for non-NORMAL mode, or the requested operation_mode is not
      *             supported by the HAL.
      *           - Unsupported usage flag
diff --git a/camera/metadata/3.2/types.hal b/camera/metadata/3.2/types.hal
index 67b4e44..cef0397 100644
--- a/camera/metadata/3.2/types.hal
+++ b/camera/metadata/3.2/types.hal
@@ -1396,7 +1396,8 @@
      *
      * <p>The arrangement of color filters on sensor;
      * represents the colors in the top-left 2x2 section of
-     * the sensor, in reading order.</p>
+     * the sensor, in reading order, for a Bayer camera, or the
+     * light spectrum it captures for MONOCHROME camera.</p>
      */
     ANDROID_SENSOR_INFO_COLOR_FILTER_ARRANGEMENT,
 
diff --git a/camera/metadata/3.3/types.hal b/camera/metadata/3.3/types.hal
index 04edfe9..27d82b9 100644
--- a/camera/metadata/3.3/types.hal
+++ b/camera/metadata/3.3/types.hal
@@ -100,7 +100,7 @@
 
     /** android.request.availablePhysicalCameraRequestKeys [static, int32[], hidden]
      *
-     * <p>A subset of the available request keys that can be overriden for
+     * <p>A subset of the available request keys that can be overridden for
      * physical devices backing a logical multi-camera.</p>
      */
     ANDROID_REQUEST_AVAILABLE_PHYSICAL_CAMERA_REQUEST_KEYS,
@@ -109,8 +109,8 @@
 
     /** android.statistics.oisDataMode [dynamic, enum, public]
      *
-     * <p>A control for selecting whether OIS position information is included in output
-     * result metadata.</p>
+     * <p>A control for selecting whether optical stabilization (OIS) position
+     * information is included in output result metadata.</p>
      */
     ANDROID_STATISTICS_OIS_DATA_MODE = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_STATISTICS_END,
 
@@ -154,7 +154,7 @@
 
     ANDROID_INFO_END_3_3,
 
-    /** android.logicalMultiCamera.physicalIds [static, byte[], hidden]
+    /** android.logicalMultiCamera.physicalIds [static, byte[], ndk_public]
      *
      * <p>String containing the ids of the underlying physical cameras.</p>
      */
diff --git a/current.txt b/current.txt
index 37de397..06145e4 100644
--- a/current.txt
+++ b/current.txt
@@ -385,14 +385,18 @@
 10ff2fae516346b86121368ce5790d5accdfcb73983246b813f3d488b66db45a android.hardware.wifi.supplicant@1.1::ISupplicantStaNetwork
 
 # ABI preserving changes to HALs during Android Q
-f72d23278af99a2f6a9c1d40352b67dbf1f582282f799f88f7235dc7c13892b5 android.hardware.camera.device@3.2::ICameraDeviceSession
+2a55e224aa9bc62c0387cd85ad3c97e33f0c33a4e1489cbae86b2523e6f9df35 android.hardware.camera.device@3.2::ICameraDevice
+8caf9104dc6885852c0b117d853dd93f6d4b61a0a365138295eb8bcd41b36423 android.hardware.camera.device@3.2::ICameraDeviceSession
+684702a60deef03a1e8093961dc0a18c555c857ad5a77ba7340b0635ae01eb70 android.hardware.camera.device@3.4::ICameraDeviceSession
 f8a19622cb0cc890913b1ef3e32b675ffb26089a09e02fef4056ebad324d2b5d android.hardware.camera.device@3.4::types
+291638a1b6d4e63283e9e722ab5049d9351717ffa2b66162124f84d1aa7c2835 android.hardware.camera.metadata@3.2::types
+8a075cf3a17fe99c6d23415a3e9a65612f1fee73ee052a3a8a0ca5b8877395a4 android.hardware.camera.metadata@3.3::types
 da33234403ff5d60f3473711917b9948e6484a4260b5247acdafb111193a9de2 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
 21165b8e30c4b2d52980e4728f661420adc16e38bbe73476c06b2085be908f4c android.hardware.gnss@1.0::IGnssCallback
 d702fb01dc2a0733aa820b7eb65435ee3334f75632ef880bafd2fb8803a20a58 android.hardware.gnss@1.0::IGnssMeasurementCallback
 b7ecf29927055ec422ec44bf776223f07d79ad9f92ccf9becf167e62c2607e7a android.hardware.keymaster@4.0::IKeymasterDevice
 574e8f1499436fb4075894dcae0b36682427956ecb114f17f1fe22d116a83c6b android.hardware.neuralnetworks@1.0::IPreparedModel
-1fb32361286b938d48a55c2539c846732afce0b99fe08590f556643125bc13d3 android.hardware.neuralnetworks@1.0::types
+417ab60fe1ef786778047e4486f3d868ebce570d91addd8fe4251515213072de android.hardware.neuralnetworks@1.0::types
 e22e8135d061d0e9c4c1a70c25c19fdba10f4d3cda9795ef25b6392fc520317c android.hardware.neuralnetworks@1.1::types
 1d4a5776614c08b5d794a5ec5ab04697260cbd4b3441d5935cd53ee71d19da02 android.hardware.radio@1.0::IRadioResponse
 ed9da80ec0c96991fd03f0a46107815d0e50f764656e49dba4980fa5c31d5bc3 android.hardware.radio@1.0::types
diff --git a/neuralnetworks/1.0/types.hal b/neuralnetworks/1.0/types.hal
index 0880b2f..89af35a 100644
--- a/neuralnetworks/1.0/types.hal
+++ b/neuralnetworks/1.0/types.hal
@@ -55,10 +55,20 @@
      */
     TENSOR_QUANT8_ASYMM = 5,
 
-    /** OEM specific scalar value. */
+    /**
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
+     *
+     * OEM specific scalar value.
+     */
     OEM                 = 10000,
 
-    /** A tensor of OEM specific values. */
+    /**
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
+     *
+     * A tensor of OEM specific values.
+     */
     TENSOR_OEM_BYTE     = 10001,
 };
 
@@ -1448,7 +1458,8 @@
     TANH = 28,
 
     /**
-     * OEM specific operation.
+     * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
+     * OEM operation and data types.
      *
      * This operation is OEM specific. It should only be used for OEM
      * applications.
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index c2ecd9a..2e13854 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include "GeneratedTestHarness.h"
 #include "Callbacks.h"
 #include "ExecutionBurstController.h"
 #include "TestHarness.h"
@@ -364,6 +365,51 @@
                           kDefaultRtol, executor, measure, outputType);
 }
 
+void EvaluatePreparedModel(sp<V1_2::IPreparedModel>& preparedModel,
+                           std::function<bool(int)> is_ignored,
+                           const std::vector<MixedTypedExample>& examples,
+                           bool hasRelaxedFloat32Model, bool testDynamicOutputShape) {
+    if (testDynamicOutputShape) {
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::NO, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::NO, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::YES, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::YES, OutputType::UNSPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::NO, OutputType::INSUFFICIENT);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::NO, OutputType::INSUFFICIENT);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::YES, OutputType::INSUFFICIENT);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::YES, OutputType::INSUFFICIENT);
+    } else {
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::ASYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::SYNC, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+        EvaluatePreparedModel(preparedModel, is_ignored, examples, hasRelaxedFloat32Model,
+                              Executor::BURST, MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
+    }
+}
+
 static void getPreparedModel(sp<PreparedModelCallback> callback,
                              sp<V1_0::IPreparedModel>* preparedModel) {
     *preparedModel = callback->getPreparedModel();
@@ -468,12 +514,8 @@
                           MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
 }
 
-// TODO: Reduce code duplication.
-void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
-             std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
-             bool testDynamicOutputShape) {
-    V1_2::Model model = create_model();
-
+void PrepareModel(const sp<V1_2::IDevice>& device, const V1_2::Model& model,
+                  sp<V1_2::IPreparedModel>* preparedModel) {
     // see if service can handle model
     bool fullySupportsModel = false;
     Return<void> supportedCall = device->getSupportedOperations_1_2(
@@ -496,12 +538,11 @@
     // retrieve prepared model
     preparedModelCallback->wait();
     ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
-    sp<V1_2::IPreparedModel> preparedModel;
-    getPreparedModel(preparedModelCallback, &preparedModel);
+    getPreparedModel(preparedModelCallback, preparedModel);
 
     // early termination if vendor service cannot fully prepare model
     if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
-        ASSERT_EQ(nullptr, preparedModel.get());
+        ASSERT_EQ(nullptr, preparedModel->get());
         LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
                      "prepare model that it does not support.";
         std::cout << "[          ]   Early termination of test because vendor service cannot "
@@ -510,65 +551,18 @@
         GTEST_SKIP();
     }
     EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
-    ASSERT_NE(nullptr, preparedModel.get());
+    ASSERT_NE(nullptr, preparedModel->get());
+}
 
-    if (testDynamicOutputShape) {
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::NO, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::NO, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::NO, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::YES, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::YES, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::YES, OutputType::UNSPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::NO, OutputType::INSUFFICIENT);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::NO, OutputType::INSUFFICIENT);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::NO, OutputType::INSUFFICIENT);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::YES, OutputType::INSUFFICIENT);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::YES, OutputType::INSUFFICIENT);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::YES, OutputType::INSUFFICIENT);
-    } else {
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::NO, OutputType::FULLY_SPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::ASYNC,
-                              MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::SYNC,
-                              MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
-        EvaluatePreparedModel(preparedModel, is_ignored, examples,
-                              model.relaxComputationFloat32toFloat16, Executor::BURST,
-                              MeasureTiming::YES, OutputType::FULLY_SPECIFIED);
-    }
+// TODO: Reduce code duplication.
+void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
+             std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
+             bool testDynamicOutputShape) {
+    V1_2::Model model = create_model();
+    sp<V1_2::IPreparedModel> preparedModel = nullptr;
+    PrepareModel(device, model, &preparedModel);
+    EvaluatePreparedModel(preparedModel, is_ignored, examples,
+                          model.relaxComputationFloat32toFloat16, testDynamicOutputShape);
 }
 
 }  // namespace generated_tests
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
new file mode 100644
index 0000000..c7d2399
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
+#define VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
+
+#include "TestHarness.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+
+namespace generated_tests {
+using ::test_helper::MixedTypedExample;
+
+void PrepareModel(const sp<V1_2::IDevice>& device, const V1_2::Model& model,
+                  sp<V1_2::IPreparedModel>* preparedModel);
+
+void EvaluatePreparedModel(sp<V1_2::IPreparedModel>& preparedModel,
+                           std::function<bool(int)> is_ignored,
+                           const std::vector<MixedTypedExample>& examples,
+                           bool hasRelaxedFloat32Model, bool testDynamicOutputShape);
+
+void Execute(const sp<V1_0::IDevice>& device, std::function<V1_0::Model(void)> create_model,
+             std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples);
+
+void Execute(const sp<V1_1::IDevice>& device, std::function<V1_1::Model(void)> create_model,
+             std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples);
+
+void Execute(const sp<V1_2::IDevice>& device, std::function<V1_2::Model(void)> create_model,
+             std::function<bool(int)> is_ignored, const std::vector<MixedTypedExample>& examples,
+             bool testDynamicOutputShape = false);
+
+}  // namespace generated_tests
+
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
+
+#endif  // VTS_HAL_NEURALNETWORKS_GENERATED_TEST_HARNESS_H
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
index 55e5861..d1c7de3 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,13 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_0::IDevice>&, std::function<V1_0::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-}  // namespace generated_tests
-
 namespace V1_0 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
index d98ea04..4db1276 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,13 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_1::IDevice>&, std::function<V1_1::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-}  // namespace generated_tests
-
 namespace V1_1 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp b/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
index 1df3218..e67ef8e 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestsV1_0.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,13 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_1::IDevice>&, std::function<V1_1::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&);
-}  // namespace generated_tests
-
 namespace V1_1 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.2/IDevice.hal b/neuralnetworks/1.2/IDevice.hal
index de249b0..b9fa388 100644
--- a/neuralnetworks/1.2/IDevice.hal
+++ b/neuralnetworks/1.2/IDevice.hal
@@ -76,6 +76,21 @@
     getType() generates (ErrorStatus status, DeviceType type);
 
     /**
+     * Gets information about extensions supported by the driver implementation.
+     *
+     * All extension operations and operands must be fully supported for the
+     * extension to appear in the list of supported extensions.
+     *
+     * @return status Error status of the call, must be:
+     *     - NONE if successful
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     * @return extensions A list of supported extensions.
+     */
+    getSupportedExtensions()
+        generates (ErrorStatus status, vec<Extension> extensions);
+
+    /**
      * Gets the supported operations in a model.
      *
      * getSupportedOperations indicates which operations of a model are fully
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 4d5f0f4..5b1c7f9 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -47,7 +47,7 @@
      * used to convert the 16 bit number to a real value in the following way:
      * realValue = integerValue * scale.
      *
-     * scale is a 32 bit floating point with value greater then zero.
+     * scale is a 32 bit floating point with value greater than zero.
      */
     TENSOR_QUANT16_SYMM = 7,
     /** A tensor of IEEE 754 16 bit floating point values. */
@@ -73,10 +73,11 @@
      * These fields are located inside Operand's extraParams union, inside the
      * SymmPerChannelQuantParams struct.
      *
-     * An Operand of this type must use 'channelQuant' field of its extraParams
-     * union.
+     * An Operand of this type must use the 'channelQuant' variant of its
+     * extraParams field.
      *
-     * The channel dimension of this tensor must not be unknown (dimensions[channelDim] != 0).
+     * The channel dimension of this tensor must be known, i.e.
+     * dimensions[channelDim] must be non-zero.
      *
      * The formula for real values:
      * realValue[..., C, ...] =
@@ -96,6 +97,16 @@
      * real_value = (integer_value - zeroPoint) * scale.
      */
     TENSOR_QUANT16_ASYMM = 12,
+    /**
+     * A tensor of 8 bit signed integers that represent real numbers.
+     *
+     * Attached to this tensor is a number representing real value scale that is
+     * used to convert the 8 bit number to a real value in the following way:
+     * realValue = integerValue * scale.
+     *
+     * scale is a 32 bit floating point with value greater than zero.
+     */
+    TENSOR_QUANT8_SYMM = 13,
     /* ADDING A NEW FUNDAMENTAL TYPE REQUIRES UPDATING THE VALUE OF
      * OperandTypeRange::FUNDAMENTAL_MAX.
      */
@@ -108,10 +119,12 @@
  * The range of operand values in the OperandType enum.
  */
 enum OperandTypeRange : uint32_t {
+    BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 12,
-    OEM_MIN     = 10000,
-    OEM_MAX     = 10001,
+    FUNDAMENTAL_MAX = 13,
+    OEM_MIN         = 10000,
+    OEM_MAX         = 10001,
+    BASE_MAX        = 0xFFFF,
 };
 
 /**
@@ -189,10 +202,12 @@
  * The range of values in the OperationType enum.
  */
 enum OperationTypeRange : uint32_t {
+    BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
     FUNDAMENTAL_MAX = 93,
-    OEM_MIN = 10000,
-    OEM_MAX = 10000,
+    OEM_MIN         = 10000,
+    OEM_MAX         = 10000,
+    BASE_MAX        = 0xFFFF,
 };
 
 /**
@@ -221,6 +236,10 @@
 struct Operation {
     /**
      * The operation type.
+     *
+     * Besides the values listed in {@link OperationType}, any value above
+     * {@link OperationTypeRange::BASE_MAX} is possible and should be interpreted
+     * as an extension type according to {@link Model::extensionNameToPrefix}.
      */
     OperationType type;
 
@@ -247,21 +266,16 @@
     uint32_t channelDim;
 };
 
-// TODO(slavash): Operand Extension support
-// /**
-//  * Parameters for an unknown (as of 1.2) operand extension. This is
-//  * a vendor-specific extension or a platform extension (backport of
-//  * functionality from newer NNAPI interface).
-//  */
-// struct OperandParamsUnknown {
-// };
-
 /**
  * Describes one operand of the model's graph.
  */
 struct Operand {
     /**
-     * Data type of the operand.
+     * The data type.
+     *
+     * Besides the values listed in {@link OperandType}, any value above
+     * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
+     * as an extension type according to {@link Model::extensionNameToPrefix}.
      */
     OperandType type;
 
@@ -351,25 +365,28 @@
     DataLocation location;
 
     /**
-     * Union of extra parameters, used by some types of Operands that need additional
-     * information for the complete definition of an Operand.
+     * Additional parameters specific to a particular operand type.
      */
     safe_union ExtraParams {
        /**
-        * Placeholder for operand with no extra parameters.
+        * No additional parameters.
         */
        Monostate none;
 
        /**
-        * Used with TENSOR_QUANT8_SYMM_PER_CHANNEL operand type.
+        * Symmetric per-channel quantization parameters.
+        *
+        * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
         */
        SymmPerChannelQuantParams channelQuant;
 
-       // TODO(slavash): Operand Extension support
-       // /**
-       //  * Used with Extension operand type.
-       //  */
-       // OperandParamsUnknown unknown;
+       /**
+        * Extension operand parameters.
+        *
+        * The framework treats this as an opaque data blob.
+        * The format is up to individual extensions.
+        */
+       vec<uint8_t> extension;
     } extraParams;
 };
 
@@ -432,6 +449,63 @@
      * range and precision of the IEEE 754 32-bit floating-point format.
      */
     bool relaxComputationFloat32toFloat16;
+
+    /**
+     * The mapping between extension names and prefixes of operand and
+     * operation type values.
+     *
+     * An operand or operation whose numeric type value is above
+     * {@link OperandTypeRange::BASE_MAX} or
+     * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
+     * as an extension operand. The low
+     * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
+     * correspond to the type ID within the extension and the high
+     * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
+     * the "prefix", which maps uniquely to the extension name.
+     *
+     * For example, if a model contains an operation whose value is
+     * 0xAAAABBBB and extensionNameToPrefix contains an entry with
+     * prefix=0xAAAA and name="vendor.test.test_extension", then
+     * the operation should be interpreted as the operation 0xBBBB
+     * of the extension named vendor.test.test_extension.
+     *
+     * This is a one-to-one correspondence. That is, there must be at most one
+     * prefix corresponding to each extension name and at most one extension
+     * name corresponding to each prefix.
+     */
+    vec<ExtensionNameAndPrefix> extensionNameToPrefix;
+
+    /**
+     * A correspondence between an extension name and a prefix of operand and
+     * operation type values.
+     */
+    struct ExtensionNameAndPrefix {
+        /**
+         * The extension name.
+         *
+         * See {@link Extension::name}.
+         */
+        string name;
+
+        /**
+         * The unique extension identifier within the model.
+         *
+         * See {@link Model::extensionNameToPrefix}.
+         */
+        uint16_t prefix;
+    };
+
+    /**
+     * Numeric values of extension operand and operation types have the
+     * following structure:
+     * - 16 high bits represent the "prefix", which corresponds uniquely to the
+     *   extension name.
+     * - 16 low bits represent the type ID within the extension.
+     */
+    enum ExtensionTypeEncoding : uint8_t {
+        HIGH_BITS_PREFIX = 16,
+        LOW_BITS_TYPE = 16,
+    };
 };
 
 /**
@@ -685,3 +759,43 @@
      */
     Timing executionTiming;
 };
+
+/**
+ * Information about an extension.
+ */
+struct Extension {
+    /**
+     * The extension name.
+     *
+     * The name must start with the reverse domain name of the vendor.
+     * Example: com.google.test_extension
+     */
+    string name;
+
+    /**
+     * Information about an extension operand type.
+     */
+    struct OperandTypeInformation {
+        /**
+         * The extension operand type.
+         */
+        uint16_t type;
+
+        /**
+         * Indicates whether the extension operand type represents a tensor or
+         * a scalar.
+         */
+        bool isTensor;
+
+        /**
+         * The byte size of the operand (if scalar) or of a single element (if
+         * tensor).
+         */
+        uint32_t byteSize;
+    };
+
+    /**
+     * Information about operand types defined by the extension.
+     */
+    vec<OperandTypeInformation> operandTypes;
+};
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 0cb9e16..510a0d5 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -46,6 +46,7 @@
     defaults: ["VtsHalNeuralNetworksTargetTestDefaults"],
     srcs: [
         "BasicTests.cpp",
+        "CompilationCachingTests.cpp",
         "GeneratedTests.cpp",
     ],
     cflags: [
diff --git a/neuralnetworks/1.2/vts/functional/BasicTests.cpp b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
index 8c3ad15..2b88edd 100644
--- a/neuralnetworks/1.2/vts/functional/BasicTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/BasicTests.cpp
@@ -55,6 +55,29 @@
     });
     EXPECT_TRUE(ret.isOk());
 }
+
+// device supported extensions test
+TEST_F(NeuralnetworksHidlTest, GetDeviceSupportedExtensionsTest) {
+    Return<void> ret = device->getSupportedExtensions(
+            [](ErrorStatus status, const hidl_vec<Extension>& extensions) {
+                EXPECT_EQ(ErrorStatus::NONE, status);
+                for (auto& extension : extensions) {
+                    std::string extensionName = extension.name;
+                    EXPECT_FALSE(extensionName.empty());
+                    EXPECT_NE(extensionName.find("."), std::string::npos)
+                            << "Extension name must start with the reverse domain name of the "
+                               "vendor";
+                }
+            });
+    EXPECT_TRUE(ret.isOk());
+}
+
+// isCachingSupported test
+TEST_F(NeuralnetworksHidlTest, IsCachingSupported) {
+    Return<void> ret = device->isCachingSupported(
+            [](ErrorStatus status, bool) { EXPECT_EQ(ErrorStatus::NONE, status); });
+    EXPECT_TRUE(ret.isOk());
+}
 }  // namespace functional
 }  // namespace vts
 }  // namespace V1_2
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
new file mode 100644
index 0000000..454aa1f
--- /dev/null
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -0,0 +1,652 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_hidl_hal_test"
+
+#include "VtsHalNeuralnetworks.h"
+
+#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>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_2 {
+namespace vts {
+namespace functional {
+
+using ::android::hardware::neuralnetworks::V1_2::implementation::ExecutionCallback;
+using ::android::hardware::neuralnetworks::V1_2::implementation::PreparedModelCallback;
+using ::android::nn::allocateSharedMemory;
+using ::test_helper::MixedTypedExample;
+
+namespace {
+
+// In frameworks/ml/nn/runtime/tests/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"
+
+// Prevent the compiler from complaining about an otherwise unused function.
+[[maybe_unused]] auto dummy_createTestModel = createTestModel_dynamic_output_shape;
+[[maybe_unused]] auto dummy_get_examples = get_examples_dynamic_output_shape;
+
+enum class AccessMode { READ_ONLY, WRITE_ONLY };
+
+void createCacheHandle(const std::vector<std::string>& files, AccessMode mode,
+                       hidl_handle* handle) {
+    std::vector<int> fds;
+    for (const auto& file : files) {
+        int fd;
+        if (mode == AccessMode::READ_ONLY) {
+            fd = open(file.c_str(), O_RDONLY);
+        } else if (mode == AccessMode::WRITE_ONLY) {
+            fd = open(file.c_str(), O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+        } else {
+            FAIL();
+        }
+        ASSERT_GE(fd, 0);
+        fds.push_back(fd);
+    }
+    native_handle_t* cacheNativeHandle = native_handle_create(fds.size(), 0);
+    ASSERT_NE(cacheNativeHandle, nullptr);
+    for (uint32_t i = 0; i < fds.size(); i++) {
+        cacheNativeHandle->data[i] = fds[i];
+    }
+    handle->setTo(cacheNativeHandle, /*shouldOwn=*/true);
+}
+
+}  // namespace
+
+// Tag for the compilation caching tests.
+class CompilationCachingTest : public NeuralnetworksHidlTest {
+  protected:
+    void SetUp() override {
+        NeuralnetworksHidlTest::SetUp();
+
+        // Create cache directory.
+        char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
+        char* cacheDir = mkdtemp(cacheDirTemp);
+        ASSERT_NE(cacheDir, nullptr);
+        mCache1 = cacheDir + mCache1;
+        mCache2 = cacheDir + mCache2;
+        mCache3 = cacheDir + mCache3;
+
+        // Check if caching is supported.
+        bool isCachingSupported;
+        Return<void> ret = device->isCachingSupported(
+                [&isCachingSupported](ErrorStatus status, bool supported) {
+                    EXPECT_EQ(ErrorStatus::NONE, status);
+                    isCachingSupported = supported;
+                });
+        EXPECT_TRUE(ret.isOk());
+        if (isCachingSupported) {
+            mIsCachingSupported = true;
+        } else {
+            LOG(INFO) << "NN VTS: Early termination of test because vendor service does not "
+                         "support compilation caching.";
+            std::cout << "[          ]   Early termination of test because vendor service does not "
+                         "support compilation caching."
+                      << std::endl;
+            mIsCachingSupported = false;
+        }
+
+        // Create empty cache files.
+        hidl_handle handle;
+        createCacheHandle({mCache1, mCache2, mCache3}, AccessMode::WRITE_ONLY, &handle);
+    }
+
+    void saveModelToCache(sp<IPreparedModel> preparedModel, const hidl_handle& cache1,
+                          const hidl_handle& cache2, ErrorStatus* status) {
+        // Save IPreparedModel to cache.
+        hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
+        Return<ErrorStatus> saveToCacheStatus =
+                preparedModel->saveToCache(cache1, cache2, cacheToken);
+        ASSERT_TRUE(saveToCacheStatus.isOk());
+        *status = static_cast<ErrorStatus>(saveToCacheStatus);
+    }
+
+    bool checkEarlyTermination(ErrorStatus status) {
+        if (status == ErrorStatus::GENERAL_FAILURE) {
+            LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                         "save the prepared model that it does not support.";
+            std::cout << "[          ]   Early termination of test because vendor service cannot "
+                         "save the prepared model that it does not support."
+                      << std::endl;
+            return true;
+        }
+        return false;
+    }
+
+    void prepareModelFromCache(const hidl_handle& cache1, const hidl_handle& cache2,
+                               sp<IPreparedModel>* preparedModel, ErrorStatus* status) {
+        // Launch prepare model from cache.
+        sp<PreparedModelCallback> preparedModelCallback = new PreparedModelCallback();
+        ASSERT_NE(nullptr, preparedModelCallback.get());
+        hidl_array<uint8_t, sizeof(mToken)> cacheToken(mToken);
+        Return<ErrorStatus> prepareLaunchStatus =
+                device->prepareModelFromCache(cache1, cache2, cacheToken, preparedModelCallback);
+        ASSERT_TRUE(prepareLaunchStatus.isOk());
+        if (static_cast<ErrorStatus>(prepareLaunchStatus) != ErrorStatus::NONE) {
+            *preparedModel = nullptr;
+            *status = static_cast<ErrorStatus>(prepareLaunchStatus);
+            return;
+        }
+
+        // Retrieve prepared model.
+        preparedModelCallback->wait();
+        *status = preparedModelCallback->getStatus();
+        *preparedModel = V1_2::IPreparedModel::castFrom(preparedModelCallback->getPreparedModel())
+                                 .withDefault(nullptr);
+    }
+
+    std::string mCache1 = "/cache1";
+    std::string mCache2 = "/cache2";
+    std::string mCache3 = "/cache3";
+    uint8_t mToken[static_cast<uint32_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)] = {};
+    bool mIsCachingSupported;
+};
+
+TEST_F(CompilationCachingTest, CacheSavingAndRetrieval) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (!mIsCachingSupported) {
+            EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        } else {
+            if (checkEarlyTermination(status)) return;
+            ASSERT_EQ(status, ErrorStatus::NONE);
+        }
+    }
+
+    // Retrieve preparedModel from cache.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        if (!mIsCachingSupported) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+            ASSERT_NE(preparedModel, nullptr);
+        }
+    }
+
+    // Execute and verify results.
+    generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; }, get_examples(),
+                                           testModel.relaxComputationFloat32toFloat16,
+                                           /*testDynamicOutputShape=*/false);
+}
+
+TEST_F(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (!mIsCachingSupported) {
+            EXPECT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        } else {
+            if (checkEarlyTermination(status)) return;
+            ASSERT_EQ(status, ErrorStatus::NONE);
+        }
+    }
+
+    // Retrieve preparedModel from cache.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        uint8_t dummyByte = 0;
+        // Advance offset by one byte.
+        ASSERT_GE(read(cache1.getNativeHandle()->data[0], &dummyByte, 1), 0);
+        ASSERT_GE(read(cache2.getNativeHandle()->data[0], &dummyByte, 1), 0);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        if (!mIsCachingSupported) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+            ASSERT_NE(preparedModel, nullptr);
+        }
+    }
+
+    // Execute and verify results.
+    generated_tests::EvaluatePreparedModel(preparedModel, [](int) { return false; }, get_examples(),
+                                           testModel.relaxComputationFloat32toFloat16,
+                                           /*testDynamicOutputShape=*/false);
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidNumFd) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // cache1 with invalid NumFd.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1, mCache3}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+    }
+
+    // cache2 with invalid NumFd.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2, mCache3}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+        }
+    }
+}
+
+TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidNumFd) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+        }
+    }
+
+    // cache1 with invalid NumFd.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1, mCache3}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+            ASSERT_EQ(preparedModel, nullptr);
+        }
+    }
+
+    // cache2 with invalid NumFd.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2, mCache3}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::INVALID_ARGUMENT);
+            ASSERT_EQ(preparedModel, nullptr);
+        }
+    }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidAccessMode) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // cache1 with invalid access mode.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+
+    // cache2 with invalid access mode.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+}
+
+TEST_F(CompilationCachingTest, PrepareModelFromCacheInvalidAccessMode) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (status != ErrorStatus::GENERAL_FAILURE) {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+        }
+    }
+
+    // cache1 with invalid access mode.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+
+    // cache2 with invalid access mode.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidOffset) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // cache1 with invalid file descriptor offset.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        uint8_t dummyByte = 0;
+        // Advance offset by one byte.
+        ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+
+    // cache2 with invalid file descriptor offset.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        uint8_t dummyByte = 0;
+        // Advance offset by one byte.
+        ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+}
+
+TEST_F(CompilationCachingTest, SaveToCacheInvalidFileSize) {
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // cache1 with invalid file size.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        uint8_t dummyByte = 0;
+        // Write one byte and seek back to the beginning.
+        ASSERT_EQ(write(cache1.getNativeHandle()->data[0], &dummyByte, 1), 1);
+        ASSERT_EQ(lseek(cache1.getNativeHandle()->data[0], 0, SEEK_SET), 0);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+
+    // cache2 with invalid file size.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        uint8_t dummyByte = 0;
+        // Write one byte and seek back to the beginning.
+        ASSERT_EQ(write(cache2.getNativeHandle()->data[0], &dummyByte, 1), 1);
+        ASSERT_EQ(lseek(cache2.getNativeHandle()->data[0], 0, SEEK_SET), 0);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+    }
+}
+
+class CompilationCachingSecurityTest : public CompilationCachingTest,
+                                       public ::testing::WithParamInterface<uint32_t> {
+  protected:
+    void SetUp() {
+        CompilationCachingTest::SetUp();
+        generator.seed(kSeed);
+    }
+
+    // Get a random integer within a closed range [lower, upper].
+    template <typename T>
+    T getRandomInt(T lower, T upper) {
+        std::uniform_int_distribution<T> dis(lower, upper);
+        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();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (checkEarlyTermination(status)) return;
+        ASSERT_EQ(status, ErrorStatus::NONE);
+    }
+
+    // Randomly flip one single bit of the cache entry.
+    FILE* pFile = fopen(mCache1.c_str(), "r+");
+    ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
+    long int fileSize = ftell(pFile);
+    ASSERT_GT(fileSize, 0);
+    ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
+    int readByte = fgetc(pFile);
+    ASSERT_NE(readByte, EOF);
+    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.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthSecuritySensitiveCache) {
+    if (!mIsCachingSupported) return;
+
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (checkEarlyTermination(status)) return;
+        ASSERT_EQ(status, ErrorStatus::NONE);
+    }
+
+    // Randomly append bytes to the cache entry.
+    FILE* pFile = fopen(mCache1.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);
+
+    // Retrieve preparedModel from cache, expect failure.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongToken) {
+    if (!mIsCachingSupported) return;
+
+    // Create test HIDL model and compile.
+    Model testModel = createTestModel();
+    sp<IPreparedModel> preparedModel = nullptr;
+    generated_tests::PrepareModel(device, testModel, &preparedModel);
+    // Terminate early if the driver cannot prepare the model.
+    if (preparedModel == nullptr) return;
+
+    // Save the compilation to cache.
+    {
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::WRITE_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::WRITE_ONLY, &cache2);
+        saveModelToCache(preparedModel, cache1, cache2, &status);
+        if (checkEarlyTermination(status)) return;
+        ASSERT_EQ(status, ErrorStatus::NONE);
+    }
+
+    // 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.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        hidl_handle cache1, cache2;
+        createCacheHandle({mCache1}, AccessMode::READ_ONLY, &cache1);
+        createCacheHandle({mCache2}, AccessMode::READ_ONLY, &cache2);
+        prepareModelFromCache(cache1, cache2, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
+                        ::testing::Range(0U, 10U));
+
+}  // namespace functional
+}  // namespace vts
+}  // namespace V1_2
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
index 4bc891f..2c3287a 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTests.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,14 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&,
-                    bool testDynamicOutputShape = false);
-}  // namespace generated_tests
-
 namespace V1_2 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
index 956926a..990cab9 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_0.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,14 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&,
-                    bool testDynamicOutputShape = false);
-}  // namespace generated_tests
-
 namespace V1_2 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
index 425690f..fa6d54d 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestsV1_1.cpp
@@ -19,6 +19,7 @@
 #include "VtsHalNeuralnetworks.h"
 
 #include "Callbacks.h"
+#include "GeneratedTestHarness.h"
 #include "TestHarness.h"
 #include "Utils.h"
 
@@ -29,14 +30,6 @@
 namespace android {
 namespace hardware {
 namespace neuralnetworks {
-
-namespace generated_tests {
-using ::test_helper::MixedTypedExample;
-extern void Execute(const sp<V1_2::IDevice>&, std::function<V1_2::Model(void)>,
-                    std::function<bool(int)>, const std::vector<MixedTypedExample>&,
-                    bool testDynamicOutputShape = false);
-}  // namespace generated_tests
-
 namespace V1_2 {
 namespace vts {
 namespace functional {
diff --git a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
index afa8e5f..590116e 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateModel.cpp
@@ -161,6 +161,8 @@
         case OperandType::TENSOR_FLOAT32:
         case OperandType::TENSOR_INT32:
         case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
         case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
             return 0;
@@ -198,7 +200,9 @@
             return 1.0f;
         case OperandType::TENSOR_INT32:
             return -1.0f;
+        case OperandType::TENSOR_QUANT8_SYMM:
         case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
             return 0.0f;
         default:
@@ -233,6 +237,10 @@
             return {1};
         case OperandType::TENSOR_QUANT8_ASYMM:
             return {-1, 256};
+        case OperandType::TENSOR_QUANT8_SYMM:
+            return {-129, -1, 1, 128};
+        case OperandType::TENSOR_QUANT16_ASYMM:
+            return {-1, 65536};
         case OperandType::TENSOR_QUANT16_SYMM:
             return {-32769, -1, 1, 32768};
         default:
@@ -288,6 +296,8 @@
             newOperand.zeroPoint = 0;
             break;
         case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
         case OperandType::TENSOR_QUANT16_SYMM:
             newOperand.dimensions =
                 operand->dimensions.size() > 0 ? operand->dimensions : hidl_vec<uint32_t>({1});