Add 1.2 NN HAL interface for dynamic output shape.

Let notify_1_2() notify output shapes.

Document unspecified dimensions and rank.

Bug: 73506513
Bug: 77234888
Test: NeuralNetworksTest_static
Test: VtsHalNeuralnetworksV1_xTargetTest with 1.2 sample driver
Change-Id: I01108913212d9f4aa47daf2f293ea19259925865
diff --git a/neuralnetworks/1.2/Android.bp b/neuralnetworks/1.2/Android.bp
index 7d13104..d8762b0 100644
--- a/neuralnetworks/1.2/Android.bp
+++ b/neuralnetworks/1.2/Android.bp
@@ -27,6 +27,7 @@
         "Operation",
         "OperationType",
         "OperationTypeRange",
+        "OutputShape",
     ],
     gen_java: false,
 }
diff --git a/neuralnetworks/1.2/IExecutionCallback.hal b/neuralnetworks/1.2/IExecutionCallback.hal
index 667e0d6..47de1b6 100644
--- a/neuralnetworks/1.2/IExecutionCallback.hal
+++ b/neuralnetworks/1.2/IExecutionCallback.hal
@@ -18,6 +18,7 @@
 
 import @1.0::ErrorStatus;
 import @1.0::IExecutionCallback;
+import OutputShape;
 
 /**
  * IExecutionCallback must be used to return the error status result from an
@@ -39,10 +40,16 @@
      *               - DEVICE_UNAVAILABLE if driver is offline or busy
      *               - GENERAL_FAILURE if the asynchronous task resulted in an
      *                 unspecified error
-     *               - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
-     *                 not large enough to store the resultant values
+     *               - OUTPUT_INSUFFICIENT_SIZE if at least one output
+     *                 operand buffer is not large enough to store the
+     *                 corresponding output
      *               - INVALID_ARGUMENT if one of the input arguments to
      *                 prepareModel is invalid
+     * @param outputShapes A list of shape information of model output operands.
+     *                     The index into "outputShapes" corresponds with to index
+     *                     of the output operand in the Request outputs vector.
+     *                     outputShapes must be empty unless the status is either
+     *                     NONE or OUTPUT_INSUFFICIENT_SIZE.
      */
-    oneway notify_1_2(ErrorStatus status);
+    oneway notify_1_2(ErrorStatus status, vec<OutputShape> outputShapes);
 };
diff --git a/neuralnetworks/1.2/IPreparedModel.hal b/neuralnetworks/1.2/IPreparedModel.hal
index 4e91c67..044ca28 100644
--- a/neuralnetworks/1.2/IPreparedModel.hal
+++ b/neuralnetworks/1.2/IPreparedModel.hal
@@ -100,11 +100,17 @@
      *                - NONE if execution is performed successfully
      *                - DEVICE_UNAVAILABLE if driver is offline or busy
      *                - GENERAL_FAILURE if there is an unspecified error
-     *                - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
-     *                  not large enough to store the resultant values
+     *                - OUTPUT_INSUFFICIENT_SIZE if at least one output
+     *                  operand buffer is not large enough to store the
+     *                  corresponding output
      *                - INVALID_ARGUMENT if one of the input arguments is
      *                  invalid
+     * @return outputShapes A list of shape information of model output operands.
+     *                      The index into "outputShapes" corresponds to the index
+     *                      of the output operand in the Request outputs vector.
+     *                      outputShapes must be empty unless the status is either
+     *                      NONE or OUTPUT_INSUFFICIENT_SIZE.
      */
     executeSynchronously(Request request)
-        generates (ErrorStatus status);
+        generates (ErrorStatus status, vec<OutputShape> outputShapes);
 };
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 40c07e7..4738cc3 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -234,9 +234,6 @@
      *
      * For a scalar operand, dimensions.size() must be 0.
      *
-     * For a tensor operand, dimensions.size() must be at least 1;
-     * however, any of the dimensions may be unspecified.
-     *
      * A tensor operand with all dimensions specified has "fully
      * specified" dimensions. Whenever possible (i.e., whenever the
      * dimensions are known at model construction time), a tensor
@@ -255,17 +252,20 @@
      *     . The operand has lifetime CONSTANT_COPY or
      *       CONSTANT_REFERENCE.
      *
-     *     . The operand has lifetime MODEL_INPUT or MODEL_OUTPUT. Fully
+     *     . The operand has lifetime MODEL_INPUT. Fully
      *       specified dimensions must either be present in the
      *       Operand or they must be provided in the corresponding
      *       RequestArgument.
-     *       EXCEPTION: If the input or output is optional and omitted
+     *       EXCEPTION: If the input is optional and omitted
      *       (by setting the hasNoValue field of the corresponding
      *       RequestArgument to true) then it need not have fully
      *       specified dimensions.
      *
      * A tensor operand with some number of unspecified dimensions is
      * represented by setting each unspecified dimension to 0.
+     *
+     * A tensor operand with unspecified rank is represented by providing
+     * an empty dimensions vector.
      */
     vec<uint32_t> dimensions;
 
@@ -397,3 +397,18 @@
      */
     bool relaxComputationFloat32toFloat16;
 };
+
+/**
+ * Describes the shape information of an output operand after execution.
+ */
+struct OutputShape {
+    /**
+     * Dimensions of the operand.
+     */
+    vec<uint32_t> dimensions;
+
+    /**
+     * Whether the provided buffer size is sufficient for the output.
+     */
+    bool isSufficient;
+};
diff --git a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
index d80fbcf..1eaea4b 100644
--- a/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.2/vts/functional/ValidateRequest.cpp
@@ -110,15 +110,20 @@
 
         executionCallback->wait();
         ErrorStatus executionReturnStatus = executionCallback->getStatus();
+        const auto& outputShapes = executionCallback->getOutputShapes();
         ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionReturnStatus);
+        ASSERT_EQ(outputShapes.size(), 0);
     }
 
     {
         SCOPED_TRACE(message + " [executeSynchronously]");
 
-        Return<ErrorStatus> executeStatus = preparedModel->executeSynchronously(request);
+        Return<void> executeStatus = preparedModel->executeSynchronously(
+            request, [](ErrorStatus error, const hidl_vec<OutputShape>& outputShapes) {
+                ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, error);
+                EXPECT_EQ(outputShapes.size(), 0);
+            });
         ASSERT_TRUE(executeStatus.isOk());
-        ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeStatus));
     }
 }