Merge "omxvts: fix binary for 64bit platforms" into oc-mr1-dev
diff --git a/cas/1.0/vts/functional/Android.bp b/cas/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..d601235
--- /dev/null
+++ b/cas/1.0/vts/functional/Android.bp
@@ -0,0 +1,23 @@
+//
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalCasV1_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalCasV1_0TargetTest.cpp"],
+    static_libs: ["android.hardware.cas@1.0"],
+}
+
diff --git a/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp
new file mode 100644
index 0000000..f346bae
--- /dev/null
+++ b/cas/1.0/vts/functional/VtsHalCasV1_0TargetTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 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 "mediacas_hidl_hal_test"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/cas/1.0/ICas.h>
+#include <android/hardware/cas/1.0/ICasListener.h>
+#include <android/hardware/cas/1.0/IDescramblerBase.h>
+#include <android/hardware/cas/1.0/IMediaCasService.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+
+#include <cinttypes>
+#include <utility>
+
+// CA System Ids used for testing
+#define CLEAR_KEY_SYSTEM_ID 0xF6D8
+#define INVALID_SYSTEM_ID 0
+
+using android::Condition;
+using android::hardware::cas::V1_0::ICas;
+using android::hardware::cas::V1_0::ICasListener;
+using android::hardware::cas::V1_0::IDescramblerBase;
+using android::hardware::cas::V1_0::IMediaCasService;
+using android::hardware::cas::V1_0::HidlCasPluginDescriptor;
+using android::hardware::Void;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::Mutex;
+using android::sp;
+
+namespace {
+
+class MediaCasHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   public:
+    virtual void SetUp() override {
+        mService = ::testing::VtsHalHidlTargetTestBase::getService<IMediaCasService>();
+        ASSERT_NE(mService, nullptr);
+    }
+
+    sp<IMediaCasService> mService;
+
+   protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+};
+
+class MediaCasListener : public ICasListener {
+   public:
+    virtual ::android::hardware::Return<void> onEvent(
+        int32_t event, int32_t arg, const ::android::hardware::hidl_vec<uint8_t>& data) override {
+        ALOGI("Info: received event: %d, arg: %d, size: %zu", event, arg, data.size());
+
+        return Void();
+    }
+};
+
+TEST_F(MediaCasHidlTest, EnumeratePlugins) {
+    description("Test enumerate plugins");
+    hidl_vec<HidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService
+                    ->enumeratePlugins([&descriptors](
+                        hidl_vec<HidlCasPluginDescriptor> const& desc) { descriptors = desc; })
+                    .isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+        return;
+    }
+
+    sp<MediaCasListener> casListener = new MediaCasListener();
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+        bool status = mService->isSystemIdSupported(caSystemId);
+        ASSERT_EQ(status, true);
+
+        status = mService->isDescramblerSupported(caSystemId);
+        ASSERT_EQ(status, true);
+
+        sp<ICas> mediaCas = mService->createPlugin(caSystemId, casListener);
+        ASSERT_NE(mediaCas, nullptr);
+
+        sp<IDescramblerBase> descramblerBase = mService->createDescrambler(caSystemId);
+        ASSERT_NE(descramblerBase, nullptr);
+    }
+}
+
+TEST_F(MediaCasHidlTest, TestInvalidSystemIdFails) {
+    description("Test failure for invalid system ID");
+    sp<MediaCasListener> casListener = new MediaCasListener();
+
+    ASSERT_FALSE(mService->isSystemIdSupported(INVALID_SYSTEM_ID));
+    ASSERT_FALSE(mService->isDescramblerSupported(INVALID_SYSTEM_ID));
+
+    sp<ICas> mediaCas = mService->createPlugin(INVALID_SYSTEM_ID, casListener);
+    EXPECT_EQ(mediaCas, nullptr);
+
+    sp<IDescramblerBase> descramblerBase = mService->createDescrambler(INVALID_SYSTEM_ID);
+    EXPECT_EQ(descramblerBase, nullptr);
+}
+
+TEST_F(MediaCasHidlTest, TestClearKeyPluginInstalled) {
+    description("Test if ClearKey plugin is installed");
+    hidl_vec<HidlCasPluginDescriptor> descriptors;
+    EXPECT_TRUE(mService
+                    ->enumeratePlugins([&descriptors](
+                        hidl_vec<HidlCasPluginDescriptor> const& _desc) { descriptors = _desc; })
+                    .isOk());
+
+    if (descriptors.size() == 0) {
+        ALOGW("[   WARN   ] enumeratePlugins list empty");
+    }
+
+    for (size_t i = 0; i < descriptors.size(); i++) {
+        int32_t caSystemId = descriptors[i].caSystemId;
+        if (CLEAR_KEY_SYSTEM_ID == caSystemId) {
+            return;
+        }
+    }
+
+    ASSERT_TRUE(false) << "ClearKey plugin not installed";
+}
+
+}  // anonymous namespace
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/cas/Android.bp b/cas/Android.bp
index 57532a0..8208d3d 100644
--- a/cas/Android.bp
+++ b/cas/Android.bp
@@ -1,6 +1,7 @@
 // This is an autogenerated file, do not edit.
 subdirs = [
     "1.0",
+    "1.0/vts/functional",
     "1.0/default",
     "native/1.0",
 ]
diff --git a/neuralnetworks/1.0/IDevice.hal b/neuralnetworks/1.0/IDevice.hal
index b6f9433..91a9555 100644
--- a/neuralnetworks/1.0/IDevice.hal
+++ b/neuralnetworks/1.0/IDevice.hal
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-/* This HAL is a work in progress */
-
 package android.hardware.neuralnetworks@1.0;
 
 import IEvent;
@@ -28,7 +26,10 @@
     /**
      * Gets the capabilities of a driver.
      *
-     * @return status ErrorStatus::NONE if successful.
+     * @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 capabilities Capabilities of the driver.
      */
     getCapabilities() generates (ErrorStatus status, Capabilities capabilities);
@@ -43,7 +44,11 @@
      *
      * @param model A model whose operations--and their corresponding
      *              operands--are to be verified by the driver.
-     * @return status ErrorStatus::NONE if successful.
+     * @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
+     *                - INVALID_ARGUMENT when provided model is invalid
      * @return supportedOperations A list of supported operations, where true
      *                             indicates the operation is supported and
      *                             false indicates the operation is not
@@ -52,7 +57,7 @@
      *                             it is describing.
      */
     getSupportedOperations(Model model)
-            generates (ErrorStatus status, vec<bool> supportedOperations);
+                generates (ErrorStatus status, vec<bool> supportedOperations);
 
     /**
      * Prepares a model for execution.
@@ -60,7 +65,7 @@
      * prepareModel is used to make any necessary transformations or alternative
      * representations to a model for execution, possible including
      * transformations on the constant data, optimization on the model's graph,
-     * or compilation into the device's native binary.
+     * or compilation into the device's native binary format.
      *
      * The only information that may be unknown to the model at this stage is
      * the shape of the tensors, which may only be known at execution time.
@@ -68,16 +73,25 @@
      * @param model The model to be prepared for execution.
      * @param event A synchronization callback that must be signaled once the
      *              execution has finished.
-     * @return status ErrorStatus::NONE if successful.
+     * @return status Error status of the call, must be:
+     *                - NONE if preparation task is successfully launched
+     *                - DEVICE_UNAVAILABLE if driver is offline or busy
+     *                - GENERAL_FAILURE if there is an unspecified error
+     *                - INVALID_ARGUMENT when one of the input arguments is
+     *                  invalid
      * @return preparedModel A handle to the resultant prepared model.
      */
     prepareModel(Model model, IEvent event)
-            generates (ErrorStatus status, IPreparedModel preparedModel);
+      generates (ErrorStatus status, IPreparedModel preparedModel);
 
     /**
      * Returns the current status of a driver.
      *
-     * @return status Status of the driver.
+     * @return status Status of the driver, one of:
+     *                - DeviceStatus::AVAILABLE
+     *                - DeviceStatus::BUSY
+     *                - DeviceStatus::OFFLINE
+     *                - DeviceStatus::UNKNOWN
      */
     getStatus() generates (DeviceStatus status);
 };
diff --git a/neuralnetworks/1.0/IEvent.hal b/neuralnetworks/1.0/IEvent.hal
index 2ebda58..2fe454c 100644
--- a/neuralnetworks/1.0/IEvent.hal
+++ b/neuralnetworks/1.0/IEvent.hal
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-/* This HAL is a work in progress */
-
 package android.hardware.neuralnetworks@1.0;
 
 /**
@@ -37,7 +35,11 @@
      * the work) to mark the event as completed so that any threads requiring
      * the corresponding output can continue executing.
      *
-     * @param status ErrorStatus::NONE if successful.
+     * @param status Error status returned from the asynchronous task, must be:
+     *               - NONE if asynchronous task was successful
+     *               - DEVICE_UNAVAILABLE if driver is offline or busy
+     *               - GENERAL_FAILURE if the asynchronous task resulted in an
+     *                 unspecified error
      */
     oneway notify(ErrorStatus status);
 };
diff --git a/neuralnetworks/1.0/IPreparedModel.hal b/neuralnetworks/1.0/IPreparedModel.hal
index a7c3342..5df883e 100644
--- a/neuralnetworks/1.0/IPreparedModel.hal
+++ b/neuralnetworks/1.0/IPreparedModel.hal
@@ -36,11 +36,16 @@
      *
      * @param request The input and output information on which the prepared
      *                model is to be executed.
-     *                prepared model.
      * @param event A callback used for synchronization that must be signaled
      *              once the execution has finished.
-     * @return status ErrorStatus::NONE if the asynchronous task was
-     *                successfully launched.
+     * @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
+     *                - OUTPUT_INSUFFICIENT_SIZE if provided output buffer is
+     *                  not large enough to store the resultant values
+     *                - INVALID_ARGUMENT when one of the input arguments is
+     *                  invalid
      */
     execute(Request request, IEvent event) generates (ErrorStatus status);
 };
diff --git a/neuralnetworks/1.0/types.hal b/neuralnetworks/1.0/types.hal
index 39e3d34..54ed402 100644
--- a/neuralnetworks/1.0/types.hal
+++ b/neuralnetworks/1.0/types.hal
@@ -218,7 +218,7 @@
      *
      * Inputs:
      * 0: A 4-D tensor, of shape [batches, height, width, depth_in], specifying the input.
-     * 1: A 4-D tensor, of shape [depth_out, filter_height, filter_width, depth_in],
+     * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
      *    specifying the filter.
      * 2: A 1-D tensor, of shape [depth_out], specifying the bias.
      *    For input tensor of {@link OperandType::TENSOR_FLOAT32} type, the bias should
@@ -1003,21 +1003,6 @@
 };
 
 /**
- * A typed operation.
- */
-struct OperationTuple {
-    /**
-     * The type of operation.
-     */
-    OperationType operationType;
-
-    /**
-     * The input data type of operation.
-     */
-    OperandType operandType;
-};
-
-/**
  * Performance information for the reference workload.
  *
  * Used by a driver to report its performance characteristics.
@@ -1039,20 +1024,6 @@
  */
 struct Capabilities {
     /**
-     * A collection of typed operations supported by the driver.
-     */
-    vec<OperationTuple> supportedOperationTuples;
-
-    /**
-     * Indicates whether a driver caches its prepared model for reuse the next
-     * time the application begins. This is useful because the model may have
-     * been prepared in a previous run.
-     *
-     * True if caching is supported, false otherwise.
-     */
-    bool cachesCompilation;
-
-    /**
      * Driver performance when operating on float32 data.
      */
     PerformanceInfo float32Performance;
@@ -1105,14 +1076,16 @@
     /**
      * Quantized scale of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
+     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
+     * TENSOR_INT32.
      */
     float scale;
 
     /**
      * Quantized zero-point offset of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
+     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
+     * TENSOR_INT32.
      */
     int32_t zeroPoint;
 
@@ -1142,9 +1115,9 @@
  */
 struct Operation {
     /**
-     * The tuple describing the operation type and input type.
+     * The operation type.
      */
-    OperationTuple opTuple;
+    OperationType type;
 
     /**
      * Describes the table that contains the indexes of the inputs of the
@@ -1195,12 +1168,18 @@
 
     /**
      * A byte buffer containing operand data that were copied into the model.
+     *
+     * An operand's value must be located here if and only if Operand::lifetime
+     * equals OperandLifeTime::CONSTANT_COPY.
      */
     vec<uint8_t> operandValues;
 
     /**
      * A collection of shared memory pools containing operand data that were
      * registered by the model.
+     *
+     * An operand's value must be located here if and only if Operand::lifetime
+     * equals OperandLifeTime::CONSTANT_REFERENCE.
      */
     vec<memory> pools;
 };
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index 2318430..89e1021 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -19,6 +19,7 @@
     srcs: [
         "Event.cpp",
         "GeneratedTestHarness.cpp",
+        "Models.cpp",
         "VtsHalNeuralnetworksV1_0TargetTest.cpp",
     ],
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
index db90ac2..4b8daec 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.cpp
@@ -42,6 +42,24 @@
 using ::generated_tests::Float32Operands;
 using ::generated_tests::Int32Operands;
 using ::generated_tests::Quant8Operands;
+using ::generated_tests::compare;
+
+template <typename ty>
+void copy_back_(MixedTyped* dst, const std::vector<RequestArgument>& ra, char* src) {
+    MixedTyped& test = *dst;
+    for_each(test, [&ra, src](int index, std::vector<ty>& m) {
+        ASSERT_EQ(m.size(), ra[index].location.length / sizeof(ty));
+        char* begin = src + ra[index].location.offset;
+        memcpy(m.data(), begin, ra[index].location.length);
+    });
+}
+
+void copy_back(MixedTyped* dst, const std::vector<RequestArgument>& ra, char* src) {
+    copy_back_<float>(dst, ra, src);
+    copy_back_<int32_t>(dst, ra, src);
+    copy_back_<uint8_t>(dst, ra, src);
+}
+
 // Top level driver for models and examples generated by test_generator.py
 // Test driver for those generated from ml/nn/runtime/test/spec
 void Execute(const sp<IDevice>& device, std::function<Model(void)> create_model,
@@ -97,9 +115,7 @@
         MixedTyped test;  // holding test results
 
         // Go through all outputs, initialize RequestArgument descriptors
-        resize_accordingly<float>(golden, test);
-        resize_accordingly<int32_t>(golden, test);
-        resize_accordingly<uint8_t>(golden, test);
+        resize_accordingly(golden, test);
         for_all(golden, [&outputs_info, &outputSize](int index, auto, auto s) {
             if (outputs_info.size() <= static_cast<size_t>(index)) outputs_info.resize(index + 1);
             RequestArgument arg = {
@@ -156,40 +172,16 @@
 
         // validate results
         outputMemory->read();
-#define COPY_BACK(ty)                                                              \
-    for_each<ty>(test, [&outputs_info, outputPtr](int index, std::vector<ty>& m) { \
-        RequestArgument& i = outputs_info[index];                                  \
-        ASSERT_EQ(m.size(), i.location.length / sizeof(ty));                       \
-        char* begin = outputPtr + i.location.offset;                               \
-        memcpy(m.data(), begin, i.location.length);                                \
-    });
-        COPY_BACK(float);
-        COPY_BACK(int32_t);
-        COPY_BACK(uint8_t);
-#undef COPY_BACK
+        copy_back(&test, outputs_info, outputPtr);
         outputMemory->commit();
         // Filter out don't cares
         MixedTyped filtered_golden;
         MixedTyped filtered_test;
-        filter<float>(golden, &filtered_golden, is_ignored);
-        filter<float>(test, &filtered_test, is_ignored);
-        filter<int32_t>(golden, &filtered_golden, is_ignored);
-        filter<int32_t>(test, &filtered_test, is_ignored);
-        filter<uint8_t>(golden, &filtered_golden, is_ignored);
-        filter<uint8_t>(test, &filtered_test, is_ignored);
+        filter(golden, &filtered_golden, is_ignored);
+        filter(test, &filtered_test, is_ignored);
 
         // We want "close-enough" results for float
-        for_each<float>(filtered_golden, [&filtered_test](int index, auto& golden_float) {
-            auto& test_float_operands = std::get<Float32Operands>(filtered_test);
-            auto& test_float = test_float_operands[index];
-            for (unsigned int i = 0; i < golden_float.size(); i++) {
-                SCOPED_TRACE(i);
-                EXPECT_NEAR(golden_float[i], test_float[i], 1.e-5);
-            }
-        });
-        EXPECT_EQ(std::get<Int32Operands>(filtered_golden), std::get<Int32Operands>(filtered_test));
-        EXPECT_EQ(std::get<Quant8Operands>(filtered_golden),
-                  std::get<Quant8Operands>(filtered_test));
+        compare(filtered_golden, filtered_test);
     }
 }
 
diff --git a/neuralnetworks/1.0/vts/functional/Models.cpp b/neuralnetworks/1.0/vts/functional/Models.cpp
new file mode 100644
index 0000000..8ce4f25
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/Models.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2017 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 "Models.h"
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+#include <vector>
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace vts {
+namespace functional {
+
+// create a valid model
+Model createValidTestModel() {
+    const std::vector<float> operand2Data = {5.0f, 6.0f, 7.0f, 8.0f};
+    const uint32_t size = operand2Data.size() * sizeof(float);
+
+    const uint32_t operand1 = 0;
+    const uint32_t operand2 = 1;
+    const uint32_t operand3 = 2;
+    const uint32_t operand4 = 3;
+
+    const std::vector<Operand> operands = {
+        {
+            .type = OperandType::TENSOR_FLOAT32,
+            .dimensions = {1, 2, 2, 1},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT32,
+            .dimensions = {1, 2, 2, 1},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = 0, .length = size},
+        },
+        {
+            .type = OperandType::INT32,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::CONSTANT_COPY,
+            .location = {.poolIndex = 0, .offset = size, .length = sizeof(int32_t)},
+        },
+        {
+            .type = OperandType::TENSOR_FLOAT32,
+            .dimensions = {1, 2, 2, 1},
+            .numberOfConsumers = 0,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::MODEL_OUTPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+        },
+    };
+
+    const std::vector<Operation> operations = {{
+        .type = OperationType::ADD, .inputs = {operand1, operand2, operand3}, .outputs = {operand4},
+    }};
+
+    const std::vector<uint32_t> inputIndexes = {operand1};
+    const std::vector<uint32_t> outputIndexes = {operand4};
+    std::vector<uint8_t> operandValues(
+        reinterpret_cast<const uint8_t*>(operand2Data.data()),
+        reinterpret_cast<const uint8_t*>(operand2Data.data()) + size);
+    int32_t activation[1] = {static_cast<int32_t>(FusedActivationFunc::NONE)};
+    operandValues.insert(operandValues.end(), reinterpret_cast<const uint8_t*>(&activation[0]),
+                         reinterpret_cast<const uint8_t*>(&activation[1]));
+
+    const std::vector<hidl_memory> pools = {};
+
+    return {
+        .operands = operands,
+        .operations = operations,
+        .inputIndexes = inputIndexes,
+        .outputIndexes = outputIndexes,
+        .operandValues = operandValues,
+        .pools = pools,
+    };
+}
+
+// create first invalid model
+Model createInvalidTestModel1() {
+    Model model = createValidTestModel();
+    model.operations[0].type = static_cast<OperationType>(0xDEADBEEF); /* INVALID */
+    return model;
+}
+
+// create second invalid model
+Model createInvalidTestModel2() {
+    Model model = createValidTestModel();
+    const uint32_t operand1 = 0;
+    const uint32_t operand5 = 4;  // INVALID OPERAND
+    model.inputIndexes = std::vector<uint32_t>({operand1, operand5 /* INVALID OPERAND */});
+    return model;
+}
+
+// allocator helper
+hidl_memory allocateSharedMemory(int64_t size, const std::string& type = "ashmem") {
+    hidl_memory memory;
+
+    sp<IAllocator> allocator = IAllocator::getService(type);
+    if (!allocator.get()) {
+        return {};
+    }
+
+    Return<void> ret = allocator->allocate(size, [&](bool success, const hidl_memory& mem) {
+        ASSERT_TRUE(success);
+        memory = mem;
+    });
+    if (!ret.isOk()) {
+        return {};
+    }
+
+    return memory;
+}
+
+// create a valid request
+Request createValidTestRequest() {
+    std::vector<float> inputData = {1.0f, 2.0f, 3.0f, 4.0f};
+    std::vector<float> outputData = {-1.0f, -1.0f, -1.0f, -1.0f};
+    const uint32_t INPUT = 0;
+    const uint32_t OUTPUT = 1;
+
+    // prepare inputs
+    uint32_t inputSize = static_cast<uint32_t>(inputData.size() * sizeof(float));
+    uint32_t outputSize = static_cast<uint32_t>(outputData.size() * sizeof(float));
+    std::vector<RequestArgument> inputs = {{
+        .location = {.poolIndex = INPUT, .offset = 0, .length = inputSize}, .dimensions = {},
+    }};
+    std::vector<RequestArgument> outputs = {{
+        .location = {.poolIndex = OUTPUT, .offset = 0, .length = outputSize}, .dimensions = {},
+    }};
+    std::vector<hidl_memory> pools = {allocateSharedMemory(inputSize),
+                                      allocateSharedMemory(outputSize)};
+    if (pools[INPUT].size() == 0 || pools[OUTPUT].size() == 0) {
+        return {};
+    }
+
+    // load data
+    sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
+    sp<IMemory> outputMemory = mapMemory(pools[OUTPUT]);
+    if (inputMemory.get() == nullptr || outputMemory.get() == nullptr) {
+        return {};
+    }
+    float* inputPtr = reinterpret_cast<float*>(static_cast<void*>(inputMemory->getPointer()));
+    float* outputPtr = reinterpret_cast<float*>(static_cast<void*>(outputMemory->getPointer()));
+    if (inputPtr == nullptr || outputPtr == nullptr) {
+        return {};
+    }
+    inputMemory->update();
+    outputMemory->update();
+    std::copy(inputData.begin(), inputData.end(), inputPtr);
+    std::copy(outputData.begin(), outputData.end(), outputPtr);
+    inputMemory->commit();
+    outputMemory->commit();
+
+    return {.inputs = inputs, .outputs = outputs, .pools = pools};
+}
+
+// create first invalid request
+Request createInvalidTestRequest1() {
+    Request request = createValidTestRequest();
+    const uint32_t INVALID = 2;
+    std::vector<float> inputData = {1.0f, 2.0f, 3.0f, 4.0f};
+    uint32_t inputSize = static_cast<uint32_t>(inputData.size() * sizeof(float));
+    request.inputs[0].location = {
+        .poolIndex = INVALID /* INVALID */, .offset = 0, .length = inputSize};
+    return request;
+}
+
+// create second invalid request
+Request createInvalidTestRequest2() {
+    Request request = createValidTestRequest();
+    request.inputs[0].dimensions = std::vector<uint32_t>({1, 2, 3, 4, 5, 6, 7, 8} /* INVALID */);
+    return request;
+}
+
+}  // namespace functional
+}  // namespace vts
+}  // namespace V1_0
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
diff --git a/neuralnetworks/1.0/vts/functional/Models.h b/neuralnetworks/1.0/vts/functional/Models.h
new file mode 100644
index 0000000..e0d57d5
--- /dev/null
+++ b/neuralnetworks/1.0/vts/functional/Models.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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 "VtsHalNeuralnetworksV1_0TargetTest.h"
+
+namespace android {
+namespace hardware {
+namespace neuralnetworks {
+namespace V1_0 {
+namespace vts {
+namespace functional {
+
+// create the model
+Model createValidTestModel();
+Model createInvalidTestModel1();
+Model createInvalidTestModel2();
+
+// create the request
+Request createValidTestRequest();
+Request createInvalidTestRequest1();
+Request createInvalidTestRequest2();
+
+}  // namespace functional
+}  // namespace vts
+}  // namespace V1_0
+}  // namespace neuralnetworks
+}  // namespace hardware
+}  // namespace android
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.cpp b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.cpp
index 453e3e5..0f354d1 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.cpp
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.cpp
@@ -18,6 +18,7 @@
 
 #include "VtsHalNeuralnetworksV1_0TargetTest.h"
 #include "Event.h"
+#include "Models.h"
 #include "TestHarness.h"
 
 #include <android-base/logging.h>
@@ -65,6 +66,32 @@
 
 void NeuralnetworksHidlTest::TearDown() {}
 
+sp<IPreparedModel> NeuralnetworksHidlTest::doPrepareModelShortcut(const Model& model) {
+    sp<IPreparedModel> preparedModel;
+    ErrorStatus prepareStatus;
+    sp<Event> preparationEvent = new Event();
+    if (preparationEvent.get() == nullptr) {
+        return nullptr;
+    }
+
+    Return<void> prepareRet = device->prepareModel(
+        model, preparationEvent, [&](ErrorStatus status, const sp<IPreparedModel>& prepared) {
+            prepareStatus = status;
+            preparedModel = prepared;
+        });
+
+    if (!prepareRet.isOk() || prepareStatus != ErrorStatus::NONE ||
+        preparedModel.get() == nullptr) {
+        return nullptr;
+    }
+    Event::Status eventStatus = preparationEvent->wait();
+    if (eventStatus != Event::Status::SUCCESS) {
+        return nullptr;
+    }
+
+    return preparedModel;
+}
+
 // create device test
 TEST_F(NeuralnetworksHidlTest, CreateDevice) {}
 
@@ -80,9 +107,6 @@
     Return<void> ret =
         device->getCapabilities([](ErrorStatus status, const Capabilities& capabilities) {
             EXPECT_EQ(ErrorStatus::NONE, status);
-            EXPECT_NE(nullptr, capabilities.supportedOperationTuples.data());
-            EXPECT_NE(0ull, capabilities.supportedOperationTuples.size());
-            EXPECT_EQ(0u, static_cast<uint32_t>(capabilities.cachesCompilation) & ~0x1);
             EXPECT_LT(0.0f, capabilities.float32Performance.execTime);
             EXPECT_LT(0.0f, capabilities.float32Performance.powerUsage);
             EXPECT_LT(0.0f, capabilities.quantized8Performance.execTime);
@@ -91,107 +115,9 @@
     EXPECT_TRUE(ret.isOk());
 }
 
-namespace {
-// create the model
-Model createTestModel() {
-    const std::vector<float> operand2Data = {5.0f, 6.0f, 7.0f, 8.0f};
-    const uint32_t size = operand2Data.size() * sizeof(float);
-
-    const uint32_t operand1 = 0;
-    const uint32_t operand2 = 1;
-    const uint32_t operand3 = 2;
-    const uint32_t operand4 = 3;
-
-    const std::vector<Operand> operands = {
-        {
-            .type = OperandType::TENSOR_FLOAT32,
-            .dimensions = {1, 2, 2, 1},
-            .numberOfConsumers = 1,
-            .scale = 0.0f,
-            .zeroPoint = 0,
-            .lifetime = OperandLifeTime::MODEL_INPUT,
-            .location = {.poolIndex = 0, .offset = 0, .length = 0},
-        },
-        {
-            .type = OperandType::TENSOR_FLOAT32,
-            .dimensions = {1, 2, 2, 1},
-            .numberOfConsumers = 1,
-            .scale = 0.0f,
-            .zeroPoint = 0,
-            .lifetime = OperandLifeTime::CONSTANT_COPY,
-            .location = {.poolIndex = 0, .offset = 0, .length = size},
-        },
-        {
-            .type = OperandType::INT32,
-            .dimensions = {},
-            .numberOfConsumers = 1,
-            .scale = 0.0f,
-            .zeroPoint = 0,
-            .lifetime = OperandLifeTime::CONSTANT_COPY,
-            .location = {.poolIndex = 0, .offset = size, .length = sizeof(int32_t)},
-        },
-        {
-            .type = OperandType::TENSOR_FLOAT32,
-            .dimensions = {1, 2, 2, 1},
-            .numberOfConsumers = 0,
-            .scale = 0.0f,
-            .zeroPoint = 0,
-            .lifetime = OperandLifeTime::MODEL_OUTPUT,
-            .location = {.poolIndex = 0, .offset = 0, .length = 0},
-        },
-    };
-
-    const std::vector<Operation> operations = {{
-        .opTuple = {OperationType::ADD, OperandType::TENSOR_FLOAT32},
-        .inputs = {operand1, operand2, operand3},
-        .outputs = {operand4},
-    }};
-
-    const std::vector<uint32_t> inputIndexes = {operand1};
-    const std::vector<uint32_t> outputIndexes = {operand4};
-    std::vector<uint8_t> operandValues(
-        reinterpret_cast<const uint8_t*>(operand2Data.data()),
-        reinterpret_cast<const uint8_t*>(operand2Data.data()) + size);
-    int32_t activation[1] = {static_cast<int32_t>(FusedActivationFunc::NONE)};
-    operandValues.insert(operandValues.end(), reinterpret_cast<const uint8_t*>(&activation[0]),
-                         reinterpret_cast<const uint8_t*>(&activation[1]));
-
-    const std::vector<hidl_memory> pools = {};
-
-    return {
-        .operands = operands,
-        .operations = operations,
-        .inputIndexes = inputIndexes,
-        .outputIndexes = outputIndexes,
-        .operandValues = operandValues,
-        .pools = pools,
-    };
-}
-}  // anonymous namespace
-
-// allocator helper
-hidl_memory allocateSharedMemory(int64_t size, const std::string& type = "ashmem") {
-    hidl_memory memory;
-
-    sp<IAllocator> allocator = IAllocator::getService(type);
-    if (!allocator.get()) {
-        return {};
-    }
-
-    Return<void> ret = allocator->allocate(size, [&](bool success, const hidl_memory& mem) {
-        ASSERT_TRUE(success);
-        memory = mem;
-    });
-    if (!ret.isOk()) {
-        return {};
-    }
-
-    return memory;
-}
-
-// supported subgraph test
-TEST_F(NeuralnetworksHidlTest, SupportedOperationsTest) {
-    Model model = createTestModel();
+// supported operations positive test
+TEST_F(NeuralnetworksHidlTest, SupportedOperationsPositiveTest) {
+    Model model = createValidTestModel();
     Return<void> ret = device->getSupportedOperations(
         model, [&](ErrorStatus status, const hidl_vec<bool>& supported) {
             EXPECT_EQ(ErrorStatus::NONE, status);
@@ -200,76 +126,126 @@
     EXPECT_TRUE(ret.isOk());
 }
 
-// execute simple graph
-TEST_F(NeuralnetworksHidlTest, SimpleExecuteGraphTest) {
-    std::vector<float> inputData = {1.0f, 2.0f, 3.0f, 4.0f};
-    std::vector<float> outputData = {-1.0f, -1.0f, -1.0f, -1.0f};
-    std::vector<float> expectedData = {6.0f, 8.0f, 10.0f, 12.0f};
-    const uint32_t INPUT = 0;
-    const uint32_t OUTPUT = 1;
+// supported operations negative test 1
+TEST_F(NeuralnetworksHidlTest, SupportedOperationsNegativeTest1) {
+    Model model = createInvalidTestModel1();
+    Return<void> ret = device->getSupportedOperations(
+        model, [&](ErrorStatus status, const hidl_vec<bool>& supported) {
+            EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
+            (void)supported;
+        });
+    EXPECT_TRUE(ret.isOk());
+}
 
-    // prepare request
-    Model model = createTestModel();
-    sp<IPreparedModel> preparedModel;
+// supported operations negative test 2
+TEST_F(NeuralnetworksHidlTest, SupportedOperationsNegativeTest2) {
+    Model model = createInvalidTestModel2();
+    Return<void> ret = device->getSupportedOperations(
+        model, [&](ErrorStatus status, const hidl_vec<bool>& supported) {
+            EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
+            (void)supported;
+        });
+    EXPECT_TRUE(ret.isOk());
+}
+
+// prepare simple model positive test
+TEST_F(NeuralnetworksHidlTest, SimplePrepareModelPositiveTest) {
+    Model model = createValidTestModel();
     sp<Event> preparationEvent = new Event();
     ASSERT_NE(nullptr, preparationEvent.get());
     Return<void> prepareRet = device->prepareModel(
         model, preparationEvent, [&](ErrorStatus status, const sp<IPreparedModel>& prepared) {
             EXPECT_EQ(ErrorStatus::NONE, status);
-            preparedModel = prepared;
+            (void)prepared;
         });
     ASSERT_TRUE(prepareRet.isOk());
+}
+
+// prepare simple model negative test 1
+TEST_F(NeuralnetworksHidlTest, SimplePrepareModelNegativeTest1) {
+    Model model = createInvalidTestModel1();
+    sp<Event> preparationEvent = new Event();
+    ASSERT_NE(nullptr, preparationEvent.get());
+    Return<void> prepareRet = device->prepareModel(
+        model, preparationEvent, [&](ErrorStatus status, const sp<IPreparedModel>& prepared) {
+            EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
+            (void)prepared;
+        });
+    ASSERT_TRUE(prepareRet.isOk());
+}
+
+// prepare simple model negative test 2
+TEST_F(NeuralnetworksHidlTest, SimplePrepareModelNegativeTest2) {
+    Model model = createInvalidTestModel2();
+    sp<Event> preparationEvent = new Event();
+    ASSERT_NE(nullptr, preparationEvent.get());
+    Return<void> prepareRet = device->prepareModel(
+        model, preparationEvent, [&](ErrorStatus status, const sp<IPreparedModel>& prepared) {
+            EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
+            (void)prepared;
+        });
+    ASSERT_TRUE(prepareRet.isOk());
+}
+
+// execute simple graph positive test
+TEST_F(NeuralnetworksHidlTest, SimpleExecuteGraphPositiveTest) {
+    Model model = createValidTestModel();
+    sp<IPreparedModel> preparedModel = doPrepareModelShortcut(model);
     ASSERT_NE(nullptr, preparedModel.get());
-    Event::Status preparationStatus = preparationEvent->wait();
-    EXPECT_EQ(Event::Status::SUCCESS, preparationStatus);
+    Request request = createValidTestRequest();
 
-    // prepare inputs
-    uint32_t inputSize = static_cast<uint32_t>(inputData.size() * sizeof(float));
-    uint32_t outputSize = static_cast<uint32_t>(outputData.size() * sizeof(float));
-    std::vector<RequestArgument> inputs = {{
-        .location = {.poolIndex = INPUT, .offset = 0, .length = inputSize}, .dimensions = {},
-    }};
-    std::vector<RequestArgument> outputs = {{
-        .location = {.poolIndex = OUTPUT, .offset = 0, .length = outputSize}, .dimensions = {},
-    }};
-    std::vector<hidl_memory> pools = {allocateSharedMemory(inputSize),
-                                      allocateSharedMemory(outputSize)};
-    ASSERT_NE(0ull, pools[INPUT].size());
-    ASSERT_NE(0ull, pools[OUTPUT].size());
-
-    // load data
-    sp<IMemory> inputMemory = mapMemory(pools[INPUT]);
-    sp<IMemory> outputMemory = mapMemory(pools[OUTPUT]);
-    ASSERT_NE(nullptr, inputMemory.get());
-    ASSERT_NE(nullptr, outputMemory.get());
-    float* inputPtr = reinterpret_cast<float*>(static_cast<void*>(inputMemory->getPointer()));
-    float* outputPtr = reinterpret_cast<float*>(static_cast<void*>(outputMemory->getPointer()));
-    ASSERT_NE(nullptr, inputPtr);
-    ASSERT_NE(nullptr, outputPtr);
-    inputMemory->update();
-    outputMemory->update();
-    std::copy(inputData.begin(), inputData.end(), inputPtr);
-    std::copy(outputData.begin(), outputData.end(), outputPtr);
-    inputMemory->commit();
-    outputMemory->commit();
-
-    // execute request
     sp<Event> executionEvent = new Event();
     ASSERT_NE(nullptr, executionEvent.get());
-    Return<ErrorStatus> executeStatus = preparedModel->execute(
-        {.inputs = inputs, .outputs = outputs, .pools = pools}, executionEvent);
+    Return<ErrorStatus> executeStatus = preparedModel->execute(request, executionEvent);
     ASSERT_TRUE(executeStatus.isOk());
     EXPECT_EQ(ErrorStatus::NONE, static_cast<ErrorStatus>(executeStatus));
     Event::Status eventStatus = executionEvent->wait();
     EXPECT_EQ(Event::Status::SUCCESS, eventStatus);
 
-    // validate results { 1+5, 2+6, 3+7, 4+8 }
+    std::vector<float> outputData = {-1.0f, -1.0f, -1.0f, -1.0f};
+    std::vector<float> expectedData = {6.0f, 8.0f, 10.0f, 12.0f};
+    const uint32_t OUTPUT = 1;
+
+    sp<IMemory> outputMemory = mapMemory(request.pools[OUTPUT]);
+    ASSERT_NE(nullptr, outputMemory.get());
+    float* outputPtr = reinterpret_cast<float*>(static_cast<void*>(outputMemory->getPointer()));
+    ASSERT_NE(nullptr, outputPtr);
     outputMemory->read();
     std::copy(outputPtr, outputPtr + outputData.size(), outputData.begin());
     outputMemory->commit();
     EXPECT_EQ(expectedData, outputData);
 }
 
+// execute simple graph negative test 1
+TEST_F(NeuralnetworksHidlTest, SimpleExecuteGraphNegativeTest1) {
+    Model model = createValidTestModel();
+    sp<IPreparedModel> preparedModel = doPrepareModelShortcut(model);
+    ASSERT_NE(nullptr, preparedModel.get());
+    Request request = createInvalidTestRequest1();
+
+    sp<Event> executionEvent = new Event();
+    ASSERT_NE(nullptr, executionEvent.get());
+    Return<ErrorStatus> executeStatus = preparedModel->execute(request, executionEvent);
+    ASSERT_TRUE(executeStatus.isOk());
+    EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeStatus));
+    executionEvent->wait();
+}
+
+// execute simple graph negative test 2
+TEST_F(NeuralnetworksHidlTest, SimpleExecuteGraphNegativeTest2) {
+    Model model = createValidTestModel();
+    sp<IPreparedModel> preparedModel = doPrepareModelShortcut(model);
+    ASSERT_NE(nullptr, preparedModel.get());
+    Request request = createInvalidTestRequest2();
+
+    sp<Event> executionEvent = new Event();
+    ASSERT_NE(nullptr, executionEvent.get());
+    Return<ErrorStatus> executeStatus = preparedModel->execute(request, executionEvent);
+    ASSERT_TRUE(executeStatus.isOk());
+    EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, static_cast<ErrorStatus>(executeStatus));
+    executionEvent->wait();
+}
+
 // Mixed-typed examples
 typedef MixedTypedExampleType MixedTypedExample;
 
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.h b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.h
index 9c56e6a..1b3b334 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.h
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworksV1_0TargetTest.h
@@ -72,6 +72,8 @@
     void SetUp() override;
     void TearDown() override;
 
+    sp<IPreparedModel> doPrepareModelShortcut(const Model& model);
+
     sp<IDevice> device;
 };
 
diff --git a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
index fc61e1c..2cbe479 100644
--- a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
+++ b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
@@ -91,35 +91,42 @@
 
 // Ensure handles can be set with correct socket options.
 TEST_F(OffloadConfigHidlTest, TestSetHandles) {
-    unique_fd fd1(netlinkSocket(kFd1Groups));
-    if (fd1.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        FAIL();
-    }
-    native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
-    nativeHandle1->data[0] = fd1.release();
-    const hidl_handle h1 = hidl_handle(nativeHandle1);
+    // Try multiple times in a row to see if it provokes file descriptor leaks.
+    for (int i = 0; i < 1024; i++) {
+        unique_fd fd1(netlinkSocket(kFd1Groups));
+        if (fd1.get() < 0) {
+            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+            FAIL();
+        }
+        native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
+        nativeHandle1->data[0] = fd1.release();
+        hidl_handle h1;
+        h1.setTo(nativeHandle1, true);
 
-    unique_fd fd2(netlinkSocket(kFd2Groups));
-    if (fd2.get() < 0) {
-        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-        FAIL();
-    }
-    native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
-    nativeHandle2->data[0] = fd2.release();
-    const hidl_handle h2 = hidl_handle(nativeHandle2);
+        unique_fd fd2(netlinkSocket(kFd2Groups));
+        if (fd2.get() < 0) {
+            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+            FAIL();
+        }
+        native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
+        nativeHandle2->data[0] = fd2.release();
+        hidl_handle h2;
+        h2.setTo(nativeHandle2, true);
 
-    const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
-    ASSERT_TRUE(ret.isOk());
+        const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
+        ASSERT_TRUE(ret.isOk());
+    }
 }
 
 // Passing a handle without an associated file descriptor should return an error
 // (e.g. "Failed Input Checks"). Check that this occurs when both FDs are empty.
 TEST_F(OffloadConfigHidlTest, TestSetHandleNone) {
     native_handle_t* const nativeHandle1 = native_handle_create(0, 0);
-    const hidl_handle h1 = hidl_handle(nativeHandle1);
+    hidl_handle h1;
+    h1.setTo(nativeHandle1, true);
     native_handle_t* const nativeHandle2 = native_handle_create(0, 0);
-    const hidl_handle h2 = hidl_handle(nativeHandle2);
+    hidl_handle h2;
+    h2.setTo(nativeHandle2, true);
 
     const Return<void> ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK);
     ASSERT_TRUE(ret.isOk());
@@ -135,10 +142,12 @@
     }
     native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
     nativeHandle1->data[0] = fd1.release();
-    const hidl_handle h1 = hidl_handle(nativeHandle1);
+    hidl_handle h1;
+    h1.setTo(nativeHandle1, true);
 
     native_handle_t* const nativeHandle2 = native_handle_create(0, 0);
-    const hidl_handle h2 = hidl_handle(nativeHandle2);
+    hidl_handle h2;
+    h2.setTo(nativeHandle2, true);
 
     const Return<void> ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK);
     ASSERT_TRUE(ret.isOk());
@@ -148,7 +157,8 @@
 // (e.g. "Failed Input Checks"). Check that this occurs when FD1 is empty.
 TEST_F(OffloadConfigHidlTest, TestSetHandle2OnlyNotOk) {
     native_handle_t* const nativeHandle1 = native_handle_create(0, 0);
-    const hidl_handle h1 = hidl_handle(nativeHandle1);
+    hidl_handle h1;
+    h1.setTo(nativeHandle1, true);
 
     unique_fd fd2(netlinkSocket(kFd2Groups));
     if (fd2.get() < 0) {
@@ -157,7 +167,8 @@
     }
     native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
     nativeHandle2->data[0] = fd2.release();
-    const hidl_handle h2 = hidl_handle(nativeHandle2);
+    hidl_handle h2;
+    h2.setTo(nativeHandle2, true);
 
     const Return<void> ret = config->setHandles(h1, h2, ASSERT_FALSE_CALLBACK);
     ASSERT_TRUE(ret.isOk());
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index 3059eac..52dd026 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -46,6 +46,12 @@
 using android::hardware::Void;
 using android::sp;
 
+enum class ExpectBoolean {
+    Ignored = -1,
+    False = 0,
+    True = 1,
+};
+
 // We use #defines here so as to get local lamba captures and error message line numbers
 #define ASSERT_TRUE_CALLBACK                            \
     [&](bool success, std::string errMsg) {             \
@@ -112,7 +118,12 @@
         prepareControlHal();
     }
 
-    virtual void TearDown() override { stopOffload(false); }
+    virtual void TearDown() override {
+        // For good measure, we should try stopOffload() once more. Since we
+        // don't know where we are in HAL call test cycle we don't know what
+        // return code to actually expect, so we just ignore it.
+        stopOffload(ExpectBoolean::Ignored);
+    }
 
     // The IOffloadConfig HAL is tested more thoroughly elsewhere. He we just
     // setup everything correctly and verify basic readiness.
@@ -127,7 +138,8 @@
         }
         native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
         nativeHandle1->data[0] = fd1.release();
-        hidl_handle h1 = hidl_handle(nativeHandle1);
+        hidl_handle h1;
+        h1.setTo(nativeHandle1, true);
 
         unique_fd fd2(conntrackSocket(NFNLGRP_CONNTRACK_UPDATE | NFNLGRP_CONNTRACK_DESTROY));
         if (fd2.get() < 0) {
@@ -136,7 +148,8 @@
         }
         native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
         nativeHandle2->data[0] = fd2.release();
-        hidl_handle h2 = hidl_handle(nativeHandle2);
+        hidl_handle h2;
+        h2.setTo(nativeHandle2, true);
 
         const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
         ASSERT_TRUE(ret.isOk());
@@ -166,12 +179,21 @@
         initOffload(true);
     }
 
-    void stopOffload(const bool expected_result) {
+    void stopOffload(const ExpectBoolean value) {
         auto cb = [&](bool success, const hidl_string& errMsg) {
             if (!success) {
                 ALOGI("Error message: %s", errMsg.c_str());
             }
-            ASSERT_EQ(expected_result, success);
+            switch (value) {
+                case ExpectBoolean::False:
+                    ASSERT_EQ(false, success);
+                    break;
+                case ExpectBoolean::True:
+                    ASSERT_EQ(true, success);
+                    break;
+                case ExpectBoolean::Ignored:
+                    break;
+            }
         };
         const Return<void> ret = control->stopOffload(cb);
         ASSERT_TRUE(ret.isOk());
@@ -209,22 +231,22 @@
     initOffload(false);
     initOffload(false);
     initOffload(false);
-    stopOffload(true);  // balance out initOffload(true)
+    stopOffload(ExpectBoolean::True);  // balance out initOffload(true)
 }
 
 // Check that calling stopOffload() without first having called initOffload() returns false.
 TEST_F(OffloadControlHidlTestBase, MultipleStopsWithoutInitReturnFalse) {
-    stopOffload(false);
-    stopOffload(false);
-    stopOffload(false);
+    stopOffload(ExpectBoolean::False);
+    stopOffload(ExpectBoolean::False);
+    stopOffload(ExpectBoolean::False);
 }
 
 // Check that calling stopOffload() after a complete init/stop cycle returns false.
 TEST_F(OffloadControlHidlTestBase, AdditionalStopsWithInitReturnFalse) {
     initOffload(true);
-    stopOffload(true);  // balance out initOffload(true)
-    stopOffload(false);
-    stopOffload(false);
+    stopOffload(ExpectBoolean::True);  // balance out initOffload(true)
+    stopOffload(ExpectBoolean::False);
+    stopOffload(ExpectBoolean::False);
 }
 
 // Check that calling setLocalPrefixes() without first having called initOffload() returns false.
@@ -305,7 +327,12 @@
         setupControlHal();
     }
 
-    virtual void TearDown() override { stopOffload(true); }
+    virtual void TearDown() override {
+        // For good measure, we should try stopOffload() once more. Since we
+        // don't know where we are in HAL call test cycle we don't know what
+        // return code to actually expect, so we just ignore it.
+        stopOffload(ExpectBoolean::Ignored);
+    }
 };
 
 /*
@@ -575,16 +602,24 @@
 TEST_F(OffloadControlHidlTest, RemoveDownstreamIPv4) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("192.0.2.0/24");
-    const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
-    EXPECT_TRUE(ret.isOk());
+    // First add the downstream, otherwise removeDownstream logic can reasonably
+    // return false for downstreams not previously added.
+    const Return<void> add = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(add.isOk());
+    const Return<void> del = control->removeDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(del.isOk());
 }
 
 // Test removeDownstream() works given an IPv6 prefix.
 TEST_F(OffloadControlHidlTest, RemoveDownstreamIPv6) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("2001:db8::/64");
-    const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
-    EXPECT_TRUE(ret.isOk());
+    // First add the downstream, otherwise removeDownstream logic can reasonably
+    // return false for downstreams not previously added.
+    const Return<void> add = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(add.isOk());
+    const Return<void> del = control->removeDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(del.isOk());
 }
 
 // Test removeDownstream() fails given all empty parameters.