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});