Merge changes from topics "aosp-nnapi-1.3-operations", "aosp-nnapi-async-cmdqueue"

* changes:
  Fix the NNAPI vts tests about validateExecuteFenced
  Add VTS tests for NNAPI IPreparedModel::executeFenced
  Support sync fence in NNAPI
  Add FILL and RANK ops
  Add ELU and HARD_SWISH
  NNAPI: Regenerate types.hal to sync with types.spec
diff --git a/current.txt b/current.txt
index fd08ec5..b77aafe 100644
--- a/current.txt
+++ b/current.txt
@@ -625,9 +625,10 @@
 65c16331e57f6dd68b3971f06f78fe9e3209afb60630c31705aa355f9a52bf0d android.hardware.neuralnetworks@1.3::IBuffer
 d1f382d14e1384b907d5bb5780df7f01934650d556fedbed2f15a90773c657d6 android.hardware.neuralnetworks@1.3::IDevice
 4167dc3ad35e9cd0d2057d4868c7675ae2c3c9d05bbd614c1f5dccfa5fd68797 android.hardware.neuralnetworks@1.3::IExecutionCallback
-7d23020248194abbee8091cc624f39a5a6d7ccba338b172d5d2d3df0cceffbee android.hardware.neuralnetworks@1.3::IPreparedModel
+29e26e83399b69c7998b787bd30426dd5baa2da350effca76bbee1ba877355c9 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
+384fd9fd6e4d43ea11d407e52ea81da5242c3c5f4b458b8707d8feb652a13e36 android.hardware.neuralnetworks@1.3::IPreparedModel
 0439a1fbbec7f16e5e4c653d85ac685d51bfafbae15b8f8cca530acdd7d6a8ce android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-ee65638f8af3f9f4f222e7208eaa9f1f8e7f8e0a21545846ba67d0e27624efa1 android.hardware.neuralnetworks@1.3::types
+5f1a4e0c29fc686ed476f9f04eed35e4405d21288cb2746b978d6891de5cc37d android.hardware.neuralnetworks@1.3::types
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
 a64467bae843569f0d465c5be7f0c7a5b987985b55a3ef4794dd5afc68538650 android.hardware.wifi.supplicant@1.3::ISupplicant
 44445b8a03d7b9e68b2fbd954672c18a8fce9e32851b0692f4f4ab3407f86ecb android.hardware.wifi.supplicant@1.3::ISupplicantStaIface
diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 56011e2..7b02cc5 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/Android.bp
@@ -11,6 +11,7 @@
         "IBuffer.hal",
         "IDevice.hal",
         "IExecutionCallback.hal",
+        "IFencedExecutionCallback.hal",
         "IPreparedModel.hal",
         "IPreparedModelCallback.hal",
     ],
diff --git a/neuralnetworks/1.3/IFencedExecutionCallback.hal b/neuralnetworks/1.3/IFencedExecutionCallback.hal
new file mode 100644
index 0000000..39076b9
--- /dev/null
+++ b/neuralnetworks/1.3/IFencedExecutionCallback.hal
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+package android.hardware.neuralnetworks@1.3;
+
+import @1.2::Timing;
+import ErrorStatus;
+
+/**
+ * IFencedExecutionCallback can be used to query the error status result
+ * and duration information from an IPreparedModel::executeFenced call.
+ */
+interface IFencedExecutionCallback {
+
+    /**
+     * The getExecutionInfo method is used by the clients to query error status
+     * result and duration information. The method must only be called after the actual
+     * evaluation has finished or resulted in an runtime error, as indicated by the status
+     * of the sync fence returned by the IPreparedModel::executeFenced call, otherwise
+     * GENERAL_FAILURE must be returned.
+     *
+     * @return status Error status returned from the asynchronously dispatched execution
+     *                must be:
+     *                - NONE if the asynchronous execution was successful
+     *                - DEVICE_UNAVAILABLE if driver is offline or busy
+     *                - GENERAL_FAILURE if the asynchronous task resulted in an
+     *                  unspecified error
+     * @return timing Duration of execution. Unless MeasureTiming::YES was passed when
+     *                launching the execution and status is NONE, all times must
+     *                be reported as UINT64_MAX. A driver may choose to report
+     *                any time as UINT64_MAX, indicating that particular measurement is
+     *                not available.
+     */
+    getExecutionInfo() generates (ErrorStatus status, Timing timing);
+};
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index bce6ee2..f84bcf4 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -24,6 +24,7 @@
 import OptionalTimePoint;
 import Request;
 import IExecutionCallback;
+import IFencedExecutionCallback;
 
 /**
  * IPreparedModel describes a model that has been prepared for execution and
@@ -91,7 +92,8 @@
      *                 execution cannot be finished by the deadline, the
      *                 execution must be aborted.
      * @param callback A callback object used to return the error status of
-     *                 the execution. The callback object's notify function must
+     *                 the execution, shape information of model output operands, and
+     *                 duration of execution. The callback object's notify function must
      *                 be called exactly once, even if the execution was
      *                 unsuccessful.
      * @return status Error status of the call, must be:
@@ -187,4 +189,57 @@
                              OptionalTimePoint deadline)
                   generates (ErrorStatus status, vec<OutputShape> outputShapes,
                              Timing timing);
+
+    /**
+     * Launch a fenced asynchronous execution on a prepared model.
+     *
+     * The execution is performed asynchronously with respect to the caller.
+     * executeFenced must fully validate the request, and only accept one that is
+     * guaranteed to be completed, unless a hardware failure or kernel panic happens on the device.
+     * If there is an error during validation, executeFenced must immediately return with
+     * the corresponding ErrorStatus. If the request is valid and there is no error launching,
+     * executeFenced must dispatch an asynchronous task to perform the execution in the
+     * background, and immediately return with ErrorStatus::NONE, a sync_fence that will be
+     * signaled once the execution is completed, and a callback that can be used by the client
+     * to query the duration and runtime error status. If the task has finished
+     * before the call returns, empty handle may be returned for the sync fence. If the
+     * asynchronous task fails to launch, executeFenced must immediately return with
+     * ErrorStatus::GENERAL_FAILURE, and empty handle for the sync fence and nullptr
+     * for callback. The execution must wait for all the sync fences (if any) in wait_for to be
+     * signaled before starting the actual execution.
+     *
+     * If any of sync fences in wait_for changes to error status after the executeFenced
+     * call succeeds, the driver must immediately set the returned sync fence to error status.
+     *
+     * When the asynchronous task has finished its execution, it must
+     * immediately signal the sync_fence created when dispatching. After
+     * the sync_fence is signaled, the task must not modify the content of
+     * any data object referenced by 'request' (described by the
+     * {@link @1.0::DataLocation} of a {@link @1.0::RequestArgument}).
+     *
+     * Any number of calls to the executeFenced, execute* and executeSynchronously*
+     * functions, in any combination, may be made concurrently, even on the same
+     * IPreparedModel object.
+     *
+     * @param request The input and output information on which the prepared
+     *                model is to be executed.
+     * @param waitFor A vector of sync fence file descriptors.
+     *                Execution must not start until all sync fences have been signaled.
+     * @param measure Specifies whether or not to measure duration of the execution.
+     *                The duration runs from the time the driver sees the call
+     *                to the executeFenced function to the time sync_fence is triggered.
+     * @return status Error status of the call, must be:
+     *                - NONE if task is successfully launched
+     *                - DEVICE_UNAVAILABLE if driver is offline or busy
+     *                - GENERAL_FAILURE if there is an unspecified error
+     *                - INVALID_ARGUMENT if one of the input arguments is invalid, including
+     *                                   fences in error states.
+     * @return syncFence The sync fence that will be triggered when the task is completed.
+     *                   The sync fence will be set to error if a critical error,
+     *                   e.g. hardware failure or kernel panic, occurs when doing execution.
+     * @return callback The IFencedExecutionCallback can be used to query information like duration
+     *                  and error status when the execution is completed.
+     */
+    executeFenced(Request request, vec<handle> waitFor, MeasureTiming measure)
+        generates (ErrorStatus status, handle syncFence, IFencedExecutionCallback callback);
 };
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index b330b50..abc33e7 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -1415,6 +1415,7 @@
      * * {@link OperandType::TENSOR_FLOAT32}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
      *
      * Supported tensor rank: up to 4
      *
@@ -1425,6 +1426,8 @@
      * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
      *      {@link FusedActivationFunc} values. Specifies the activation to
      *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
      *
      * Outputs:
      * * 0: The product, a tensor of the same {@link OperandType} as input0.
@@ -1905,6 +1908,11 @@
      * dimensions. The output is the result of dividing the first input tensor
      * by the second, optionally modified by an activation function.
      *
+     * For inputs of {@link OperandType::TENSOR_INT32}, performs
+     * "floor division" ("//" in Python). For example,
+     *     5 // 2 = 2
+     *    -5 // 2 = -3
+     *
      * Two dimensions are compatible when:
      *     1. they are equal, or
      *     2. one of them is 1
@@ -1925,6 +1933,7 @@
      * Supported tensor {@link OperandType}:
      * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
      * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
      *
      * Supported tensor rank: up to 4
      *
@@ -1935,6 +1944,8 @@
      * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
      *      {@link FusedActivationFunc} values. Specifies the activation to
      *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
      *
      * Outputs:
      * * 0: A tensor of the same {@link OperandType} as input0.
@@ -2186,6 +2197,7 @@
      * * {@link OperandType::TENSOR_FLOAT32}
      * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
      * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
      *
      * Supported tensor rank: up to 4
      *
@@ -2196,6 +2208,8 @@
      * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
      *      {@link FusedActivationFunc} values. Specifies the activation to
      *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
      *
      * Outputs:
      * * 0: A tensor of the same {@link OperandType} as input0.
@@ -2242,6 +2256,7 @@
      * Supported tensor {@link OperandType}:
      * * {@link OperandType::TENSOR_FLOAT16}
      * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
      *
      * Supported tensor rank: from 1.
      *
@@ -4972,6 +4987,106 @@
     WHILE = 97,
 
     /**
+     * Computes exponential linear activation on the input tensor element-wise.
+     *
+     * The output is calculated using the following formula:
+     *
+     *     ELU(x) = max(0, x) + min(0, alpha * (exp(x) - 1))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input. May be zero-sized.
+     * * 1: A scalar, specifying the alpha parameter.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16},
+     *      the alpha value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32},
+     *      the alpha value must be of {@link OperandType::FLOAT32}.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape and type as input0.
+     */
+    ELU = 98,
+
+    /**
+     * Computes hard-swish activation on the input tensor element-wise.
+     *
+     * Hard swish activation is introduced in
+     * https://arxiv.org/pdf/1905.02244.pdf
+     *
+     * The output is calculated using the following formula:
+     *
+     *     h-swish(x) = x * max(0, min(6, (x + 3))) / 6
+
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input. May be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape and type as input0.
+     *      Scale and zero point of this tensor may be different from the input
+     *      tensor's parameters.
+     */
+    HARD_SWISH = 99,
+
+    /**
+     * Creates a tensor filled with a scalar value.
+     *
+     * Supported output tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Inputs:
+     * * 0: A 1-D tensor, specifying the desired output tensor shape.
+     * * 1: A scalar, specifying the value to fill the output tensors with.
+     *      For output tensor of {@link OperandType::TENSOR_FLOAT16},
+     *      the scalar must be of {@link OperandType::FLOAT16}.
+     *      For output tensor of {@link OperandType::TENSOR_FLOAT32},
+     *      the scalar must be of {@link OperandType::FLOAT32}.
+     *      For output tensor of {@link OperandType::TENSOR_INT32},
+     *      the scalar must be of {@link OperandType::INT32}.
+     *
+     * Outputs:
+     * * 0: The output tensor.
+     */
+    FILL = 100,
+
+    /**
+     * Returns the rank of a tensor.
+     *
+     * The rank of a tensor is the number of dimensions in it. Also known as
+     * "order", "degree", "ndims".
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT16_SYMM}
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     * * {@link OperandType::TENSOR_QUANT16_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *
+     * Inputs:
+     * * 0: The input tensor.
+     *
+     * Outputs:
+     * * 0: A scalar of {@link OperandType::INT32}, specifying the rank
+     *      of the input tensor.
+     */
+    RANK = 101,
+
+    /**
      * DEPRECATED. Since NNAPI 1.2, extensions are the preferred alternative to
      * OEM operation and data types.
      *
@@ -4993,7 +5108,7 @@
 enum OperationTypeRange : uint32_t {
     BASE_MIN        = 0,
     FUNDAMENTAL_MIN = 0,
-    FUNDAMENTAL_MAX = 97,
+    FUNDAMENTAL_MAX = 101,
     OEM_MIN         = 10000,
     OEM_MAX         = 10000,
     BASE_MAX        = 0xFFFF,
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index ce2d3a9..8e7e9b9 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -65,6 +65,7 @@
         "libhidlmemory",
         "libneuralnetworks_generated_test_harness",
         "libneuralnetworks_utils",
+        "libsync",
     ],
     whole_static_libs: [
         "neuralnetworks_generated_V1_0_example",
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index a2c0c4e..88837db 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -29,11 +29,13 @@
 #include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
 #include <android/hardware/neuralnetworks/1.2/types.h>
 #include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
 #include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
 #include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
 #include <android/hardware/neuralnetworks/1.3/types.h>
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/memory/1.0/IMemory.h>
+#include <android/sync.h>
 #include <gtest/gtest.h>
 #include <hidlmemory/mapping.h>
 
@@ -70,7 +72,7 @@
 
 namespace {
 
-enum class Executor { ASYNC, SYNC, BURST };
+enum class Executor { ASYNC, SYNC, BURST, FENCED };
 
 enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT };
 
@@ -562,9 +564,48 @@
 
             break;
         }
+        case Executor::FENCED: {
+            SCOPED_TRACE("fenced");
+            ErrorStatus result;
+            hidl_handle sync_fence_handle;
+            sp<IFencedExecutionCallback> fenced_callback;
+            Return<void> ret = preparedModel->executeFenced(
+                    request, {}, testConfig.measureTiming,
+                    [&result, &sync_fence_handle, &fenced_callback](
+                            ErrorStatus error, const hidl_handle& handle,
+                            const sp<IFencedExecutionCallback>& callback) {
+                        result = error;
+                        sync_fence_handle = handle;
+                        fenced_callback = callback;
+                    });
+            ASSERT_TRUE(ret.isOk());
+            if (result != ErrorStatus::NONE) {
+                ASSERT_EQ(sync_fence_handle.getNativeHandle(), nullptr);
+                ASSERT_EQ(fenced_callback, nullptr);
+                executionStatus = ErrorStatus::GENERAL_FAILURE;
+            } else if (sync_fence_handle.getNativeHandle()) {
+                constexpr int kInfiniteTimeout = -1;
+                int sync_fd = sync_fence_handle.getNativeHandle()->data[0];
+                ASSERT_GT(sync_fd, 0);
+                int r = sync_wait(sync_fd, kInfiniteTimeout);
+                ASSERT_GE(r, 0);
+            }
+            if (result == ErrorStatus::NONE) {
+                ASSERT_NE(fenced_callback, nullptr);
+                Return<void> ret = fenced_callback->getExecutionInfo(
+                        [&executionStatus, &timing](ErrorStatus error, Timing t) {
+                            executionStatus = error;
+                            timing = t;
+                        });
+                ASSERT_TRUE(ret.isOk());
+            }
+            break;
+        }
     }
 
-    if (testConfig.outputType != OutputType::FULLY_SPECIFIED &&
+    // The driver is allowed to reject executeFenced, and if they do, we should skip.
+    if ((testConfig.outputType != OutputType::FULLY_SPECIFIED ||
+         testConfig.executor == Executor::FENCED) &&
         executionStatus == ErrorStatus::GENERAL_FAILURE) {
         if (skipped != nullptr) {
             *skipped = true;
@@ -648,6 +689,11 @@
             executorList = {Executor::ASYNC, Executor::SYNC};
             memoryType = MemoryType::DEVICE;
         } break;
+        case TestKind::FENCED_COMPUTE: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
+            executorList = {Executor::FENCED};
+        } break;
         case TestKind::QUANTIZATION_COUPLING: {
             LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
             return;
@@ -671,7 +717,8 @@
                                    const TestModel& coupledModel) {
     const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED};
     const std::vector<MeasureTiming> measureTimingList = {MeasureTiming::NO, MeasureTiming::YES};
-    const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST};
+    const std::vector<Executor> executorList = {Executor::ASYNC, Executor::SYNC, Executor::BURST,
+                                                Executor::FENCED};
 
     for (const OutputType outputType : outputTypesList) {
         for (const MeasureTiming measureTiming : measureTimingList) {
@@ -708,7 +755,8 @@
     switch (testKind) {
         case TestKind::GENERAL:
         case TestKind::DYNAMIC_SHAPE:
-        case TestKind::MEMORY_DOMAIN: {
+        case TestKind::MEMORY_DOMAIN:
+        case TestKind::FENCED_COMPUTE: {
             createPreparedModel(device, model, &preparedModel);
             if (preparedModel == nullptr) return;
             EvaluatePreparedModel(device, preparedModel, testModel, testKind);
@@ -771,6 +819,9 @@
 // Tag for the memory domain tests
 class MemoryDomainTest : public GeneratedTest {};
 
+// Tag for the fenced compute tests
+class FencedComputeTest : public GeneratedTest {};
+
 // Tag for the dynamic output shape tests
 class QuantizationCouplingTest : public GeneratedTest {};
 
@@ -786,6 +837,10 @@
     Execute(kDevice, kTestModel, /*testKind=*/TestKind::MEMORY_DOMAIN);
 }
 
+TEST_P(FencedComputeTest, Test) {
+    Execute(kDevice, kTestModel, /*testKind=*/TestKind::FENCED_COMPUTE);
+}
+
 TEST_P(QuantizationCouplingTest, Test) {
     Execute(kDevice, kTestModel, /*testKind=*/TestKind::QUANTIZATION_COUPLING);
 }
@@ -793,12 +848,16 @@
 INSTANTIATE_GENERATED_TEST(GeneratedTest,
                            [](const TestModel& testModel) { return !testModel.expectFailure; });
 
-INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest,
-                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel) {
+    return !testModel.expectFailure && !testModel.hasScalarOutputs();
+});
 
 INSTANTIATE_GENERATED_TEST(MemoryDomainTest,
                            [](const TestModel& testModel) { return !testModel.expectFailure; });
 
+INSTANTIATE_GENERATED_TEST(FencedComputeTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
 INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) {
     return testModel.hasQuant8CoupledOperands() && testModel.operations.size() == 1;
 });
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index fe695b4..e597fac 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -65,6 +65,8 @@
     DYNAMIC_SHAPE,
     // Same as GENERAL but use device memories for inputs and outputs
     MEMORY_DOMAIN,
+    // Same as GENERAL but use executeFenced for exeuction
+    FENCED_COMPUTE,
     // Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result
     // (OK/SKIPPED/FAILED) as the model with all such tensors converted to
     // TENSOR_QUANT8_ASYMM_SIGNED.
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index a211428..1245432 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -337,6 +337,7 @@
         // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
         // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of
         //     TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED.
+        // - RANK's input can have any TENSOR_* type.
         switch (operation.type) {
             case OperationType::LSH_PROJECTION: {
                 if (operand == operation.inputs[1]) {
@@ -399,6 +400,20 @@
                     return true;
                 }
             } break;
+            case OperationType::RANK: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
+                     type == OperandType::TENSOR_INT32 ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT16_SYMM ||
+                     type == OperandType::TENSOR_BOOL8 ||
+                     type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
+                     type == OperandType::TENSOR_QUANT16_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
+                    return true;
+                }
+            } break;
             default:
                 break;
         }
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index be4112a..1ddd09c 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -16,7 +16,9 @@
 
 #define LOG_TAG "neuralnetworks_hidl_hal_test"
 
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
 #include <chrono>
+
 #include "1.0/Utils.h"
 #include "1.3/Callbacks.h"
 #include "ExecutionBurstController.h"
@@ -136,6 +138,22 @@
             burst->freeMemory(keys.front());
         }
     }
+
+    // dispatch
+    {
+        SCOPED_TRACE(message + " [executeFenced]");
+        Return<void> ret = preparedModel->executeFenced(
+                request, {}, MeasureTiming::NO,
+                [](ErrorStatus error, const hidl_handle& handle,
+                   const sp<IFencedExecutionCallback>& callback) {
+                    if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
+                        ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+                    }
+                    ASSERT_EQ(handle.getNativeHandle(), nullptr);
+                    ASSERT_EQ(callback, nullptr);
+                });
+        ASSERT_TRUE(ret.isOk());
+    }
 }
 
 ///////////////////////// REMOVE INPUT ////////////////////////////////////
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
index 93c8f13..c84f5b7 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.cpp
@@ -133,6 +133,23 @@
 // Forward declaration from ValidateBurst.cpp
 void validateBurst(const sp<IPreparedModel>& preparedModel, const V1_0::Request& request);
 
+// Validate sync_fence handles for dispatch with valid input
+void validateExecuteFenced(const sp<IPreparedModel>& preparedModel, const Request& request) {
+    SCOPED_TRACE("Expecting request to fail [executeFenced]");
+    Return<void> ret_null =
+            preparedModel->executeFenced(request, {hidl_handle(nullptr)}, V1_2::MeasureTiming::NO,
+                                         [](ErrorStatus error, const hidl_handle& handle,
+                                            const sp<IFencedExecutionCallback>& callback) {
+                                             // TODO: fix this once sample driver impl is merged.
+                                             if (error != ErrorStatus::DEVICE_UNAVAILABLE) {
+                                                 ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+                                             }
+                                             ASSERT_EQ(handle.getNativeHandle(), nullptr);
+                                             ASSERT_EQ(callback, nullptr);
+                                         });
+    ASSERT_TRUE(ret_null.isOk());
+}
+
 void validateEverything(const sp<IDevice>& device, const Model& model, const Request& request,
                         std::pair<bool, bool> supportsDeadlines) {
     const auto [prepareModelDeadlineSupported, executionDeadlineSupported] = supportsDeadlines;
@@ -144,6 +161,7 @@
     if (preparedModel == nullptr) return;
 
     validateRequest(preparedModel, request, executionDeadlineSupported);
+    validateExecuteFenced(preparedModel, request);
 
     // TODO(butlermichael): Check if we need to test burst in V1_3 if the interface remains V1_2.
     ASSERT_TRUE(nn::compliantWithV1_0(request));