Merge "Separates VTS tests by HAL version."
diff --git a/camera/metadata/3.4/types.hal b/camera/metadata/3.4/types.hal
index b228de8..4eb6929 100644
--- a/camera/metadata/3.4/types.hal
+++ b/camera/metadata/3.4/types.hal
@@ -44,6 +44,21 @@
 
     ANDROID_REQUEST_END_3_4,
 
+    /** android.scaler.availableRecommendedStreamConfigurations [static, enum[], ndk_public]
+     *
+     * <p>Recommended stream configurations for common client use cases.</p>
+     */
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_SCALER_END,
+
+    /** android.scaler.availableRecommendedInputOutputFormatsMap [static, int32, ndk_public]
+     *
+     * <p>Recommended mappings of image formats that are supported by this
+     * camera device for input streams, to their corresponding output formats.</p>
+     */
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP,
+
+    ANDROID_SCALER_END_3_4,
+
     /** android.info.supportedBufferManagementVersion [static, enum, system]
      *
      * <p>The version of buffer management API this camera device supports and opts into.</p>
@@ -52,12 +67,52 @@
 
     ANDROID_INFO_END_3_4,
 
+    /** android.depth.availableRecommendedDepthStreamConfigurations [static, int32[], ndk_public]
+     *
+     * <p>Recommended depth stream configurations for common client use cases.</p>
+     */
+    ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS = android.hardware.camera.metadata@3.2::CameraMetadataTag:ANDROID_DEPTH_END,
+
+    ANDROID_DEPTH_END_3_4,
+
 };
 
 /*
  * Enumeration definitions for the various entries that need them
  */
 
+/** android.scaler.availableFormats enumeration values added since v3.2
+ * @see ANDROID_SCALER_AVAILABLE_FORMATS
+ */
+enum CameraMetadataEnumAndroidScalerAvailableFormats :
+        @3.2::CameraMetadataEnumAndroidScalerAvailableFormats {
+    ANDROID_SCALER_AVAILABLE_FORMATS_RAW10                      = 0x25,
+    ANDROID_SCALER_AVAILABLE_FORMATS_RAW12                      = 0x26,
+    ANDROID_SCALER_AVAILABLE_FORMATS_Y8                         = 0x20203859,
+};
+
+/** android.scaler.availableRecommendedStreamConfigurations enumeration values
+ * @see ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS
+ */
+enum CameraMetadataEnumAndroidScalerAvailableRecommendedStreamConfigurations : uint32_t {
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PREVIEW
+                                                                 = 0x0,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RECORD
+                                                                 = 0x1,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VIDEO_SNAPSHOT
+                                                                 = 0x2,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_SNAPSHOT
+                                                                 = 0x3,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_ZSL
+                                                                 = 0x4,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_RAW
+                                                                 = 0x5,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END
+                                                                 = 0x6,
+    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START
+                                                                 = 0x18,
+};
+
 /** android.info.supportedBufferManagementVersion enumeration values
  * @see ANDROID_INFO_SUPPORTED_BUFFER_MANAGEMENT_VERSION
  */
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 94d06e8..837905c 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -681,6 +681,7 @@
             const CameraMetadata& chars, int deviceVersion,
             const hidl_vec<hidl_string>& deviceNames);
     void verifyCameraCharacteristics(Status status, const CameraMetadata& chars);
+    void verifyRecommendedConfigs(const CameraMetadata& metadata);
 
     static Status getAvailableOutputStreams(camera_metadata_t *staticMeta,
             std::vector<AvailableStream> &outputStreams,
@@ -698,7 +699,7 @@
             /*out*/);
     static Status pickConstrainedModeSize(camera_metadata_t *staticMeta,
             AvailableStream &hfrStream);
-    static Status isZSLModeAvailable(camera_metadata_t *staticMeta);
+    static Status isZSLModeAvailable(const camera_metadata_t *staticMeta);
     static Status getZSLInputOutputMap(camera_metadata_t *staticMeta,
             std::vector<AvailableZSLInputOutput> &inputOutputMap);
     static Status findLargestSize(
@@ -2079,7 +2080,7 @@
 
                 ret = device3_x->getCameraCharacteristics([&](auto status, const auto& chars) {
                     verifyCameraCharacteristics(status, chars);
-
+                    verifyRecommendedConfigs(chars);
                     verifyLogicalCameraMetadata(name, device3_x, chars, deviceVersion,
                             cameraDeviceNames);
                 });
@@ -4323,7 +4324,7 @@
 
 // Check whether ZSL is available using the static camera
 // characteristics.
-Status CameraHidlTest::isZSLModeAvailable(camera_metadata_t *staticMeta) {
+Status CameraHidlTest::isZSLModeAvailable(const camera_metadata_t *staticMeta) {
     Status ret = Status::METHOD_NOT_SUPPORTED;
     if (nullptr == staticMeta) {
         return Status::ILLEGAL_ARGUMENT;
@@ -4978,6 +4979,94 @@
     ASSERT_TRUE(ret.isOk());
 }
 
+void CameraHidlTest::verifyRecommendedConfigs(const CameraMetadata& chars) {
+    size_t CONFIG_ENTRY_SIZE = 5;
+    size_t CONFIG_ENTRY_TYPE_OFFSET = 3;
+    size_t CONFIG_ENTRY_BITFIELD_OFFSET = 4;
+    uint32_t maxPublicUsecase =
+            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_PUBLIC_END;
+    uint32_t vendorUsecaseStart =
+            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS_VENDOR_START;
+    uint32_t usecaseMask = (1 << vendorUsecaseStart) - 1;
+    usecaseMask &= ~((1 << maxPublicUsecase) - 1);
+
+    const camera_metadata_t* metadata = reinterpret_cast<const camera_metadata_t*> (chars.data());
+
+    camera_metadata_ro_entry recommendedConfigsEntry, recommendedDepthConfigsEntry, ioMapEntry;
+    recommendedConfigsEntry.count = recommendedDepthConfigsEntry.count = ioMapEntry.count = 0;
+    int retCode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS, &recommendedConfigsEntry);
+    int depthRetCode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS,
+            &recommendedDepthConfigsEntry);
+    int ioRetCode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP, &ioMapEntry);
+    if ((0 != retCode) && (0 != depthRetCode)) {
+        //In case both regular and depth recommended configurations are absent,
+        //I/O should be absent as well.
+        ASSERT_NE(ioRetCode, 0);
+        return;
+    }
+
+    camera_metadata_ro_entry availableKeysEntry;
+    retCode = find_camera_metadata_ro_entry(metadata,
+            ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, &availableKeysEntry);
+    ASSERT_TRUE((0 == retCode) && (availableKeysEntry.count > 0));
+    std::vector<int32_t> availableKeys;
+    availableKeys.reserve(availableKeysEntry.count);
+    availableKeys.insert(availableKeys.end(), availableKeysEntry.data.i32,
+            availableKeysEntry.data.i32 + availableKeysEntry.count);
+
+    if (recommendedConfigsEntry.count > 0) {
+        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
+                    ANDROID_SCALER_AVAILABLE_RECOMMENDED_STREAM_CONFIGURATIONS),
+                availableKeys.end());
+        ASSERT_EQ((recommendedConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
+        for (size_t i = 0; i < recommendedConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
+            int32_t entryType =
+                recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
+            uint32_t bitfield =
+                recommendedConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
+            ASSERT_TRUE((entryType ==
+                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
+                    (entryType ==
+                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
+            ASSERT_TRUE((bitfield & usecaseMask) == 0);
+        }
+    }
+
+    if (recommendedDepthConfigsEntry.count > 0) {
+        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
+                    ANDROID_DEPTH_AVAILABLE_RECOMMENDED_DEPTH_STREAM_CONFIGURATIONS),
+                availableKeys.end());
+        ASSERT_EQ((recommendedDepthConfigsEntry.count % CONFIG_ENTRY_SIZE), 0);
+        for (size_t i = 0; i < recommendedDepthConfigsEntry.count; i += CONFIG_ENTRY_SIZE) {
+            int32_t entryType =
+                recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_TYPE_OFFSET];
+            uint32_t bitfield =
+                recommendedDepthConfigsEntry.data.i32[i + CONFIG_ENTRY_BITFIELD_OFFSET];
+            ASSERT_TRUE((entryType ==
+                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) ||
+                    (entryType ==
+                     ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_INPUT));
+            ASSERT_TRUE((bitfield & usecaseMask) == 0);
+        }
+
+        if (recommendedConfigsEntry.count == 0) {
+            //In case regular recommended configurations are absent but suggested depth
+            //configurations are present, I/O should be absent.
+            ASSERT_NE(ioRetCode, 0);
+        }
+    }
+
+    if ((ioRetCode == 0) && (ioMapEntry.count > 0)) {
+        ASSERT_NE(std::find(availableKeys.begin(), availableKeys.end(),
+                    ANDROID_SCALER_AVAILABLE_RECOMMENDED_INPUT_OUTPUT_FORMATS_MAP),
+                availableKeys.end());
+        ASSERT_EQ(isZSLModeAvailable(metadata), Status::OK);
+    }
+}
+
 int main(int argc, char **argv) {
   ::testing::AddGlobalTestEnvironment(CameraHidlEnvironment::Instance());
   ::testing::InitGoogleTest(&argc, argv);
diff --git a/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp b/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
index a1676be..70b5830 100644
--- a/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
+++ b/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
@@ -131,6 +131,27 @@
     }
 }
 
+/**
+ * Make sure the constrains of hasWideColorDisplay, hasHDRDisplay
+ * are enforced.
+ */
+TEST_F(ConfigstoreHidlTest, TestColorConstrainsBasic) {
+    bool hasWideColorDisplay;
+    bool hasHDRDisplay;
+
+    Return<void> status = sfConfigs->hasWideColorDisplay(
+        [&](OptionalBool arg) { hasWideColorDisplay = arg.specified; });
+    EXPECT_OK(status);
+
+    status = sfConfigs->hasHDRDisplay([&](OptionalBool arg) { hasHDRDisplay = arg.specified; });
+    EXPECT_OK(status);
+
+    // When hasHDRDisplay returns true, hasWideColorDisplay must also return true.
+    if (hasHDRDisplay) {
+        ASSERT_TRUE(hasWideColorDisplay);
+    }
+}
+
 int main(int argc, char** argv) {
     ::testing::AddGlobalTestEnvironment(ConfigstoreHidlEnvironment::Instance());
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/configstore/1.2/vts/functional/Android.bp b/configstore/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..5f1eca6
--- /dev/null
+++ b/configstore/1.2/vts/functional/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2018 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: "VtsHalConfigstoreV1_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalConfigstoreV1_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.configstore@1.0",
+        "android.hardware.configstore@1.1",
+        "android.hardware.configstore@1.2",
+    ],
+}
+
diff --git a/configstore/1.2/vts/functional/OWNERS b/configstore/1.2/vts/functional/OWNERS
new file mode 100644
index 0000000..2b4fb8c
--- /dev/null
+++ b/configstore/1.2/vts/functional/OWNERS
@@ -0,0 +1,7 @@
+# Graphics team
+lpy@google.com
+olv@google.com
+stoza@google.com
+
+# VTS team
+yim@google.com
diff --git a/configstore/1.2/vts/functional/VtsHalConfigstoreV1_2TargetTest.cpp b/configstore/1.2/vts/functional/VtsHalConfigstoreV1_2TargetTest.cpp
new file mode 100644
index 0000000..dc5fec5
--- /dev/null
+++ b/configstore/1.2/vts/functional/VtsHalConfigstoreV1_2TargetTest.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 "ConfigstoreHidlHalTest"
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <VtsHalHidlTargetTestEnvBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/configstore/1.0/types.h>
+#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h>
+#include <unistd.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::configstore::V1_0::OptionalBool;
+using ::android::hardware::configstore::V1_0::OptionalInt64;
+using ::android::hardware::configstore::V1_0::OptionalUInt64;
+using ::android::hardware::configstore::V1_2::ISurfaceFlingerConfigs;
+using ::android::hardware::graphics::common::V1_1::PixelFormat;
+using ::android::hardware::graphics::common::V1_2::Dataspace;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk())
+
+// Test environment for Configstore HIDL HAL.
+class ConfigstoreHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
+   public:
+    // get the test environment singleton
+    static ConfigstoreHidlEnvironment* Instance() {
+        static ConfigstoreHidlEnvironment* instance = new ConfigstoreHidlEnvironment;
+        return instance;
+    }
+
+    virtual void registerTestServices() override { registerTestService<ISurfaceFlingerConfigs>(); }
+};
+
+class ConfigstoreHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+   public:
+    sp<ISurfaceFlingerConfigs> sfConfigs;
+
+    virtual void SetUp() override {
+        sfConfigs = ::testing::VtsHalHidlTargetTestBase::getService<ISurfaceFlingerConfigs>(
+            ConfigstoreHidlEnvironment::Instance()->getServiceName<ISurfaceFlingerConfigs>());
+        ASSERT_NE(sfConfigs, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    bool isSupportedWideColorGamut(Dataspace dataspace) {
+        Dataspace standard = static_cast<Dataspace>(dataspace & Dataspace::STANDARD_MASK);
+        return standard == Dataspace::STANDARD_DCI_P3 || standard == Dataspace::STANDARD_BT2020;
+    }
+};
+
+/**
+ * Make sure the constrains of hasWideColorDisplay, hasHDRDisplay
+ * and useColorManagement are enforced.
+ */
+TEST_F(ConfigstoreHidlTest, TestColorConstrainsWithColorManagement) {
+    bool hasWideColorDisplay;
+    bool hasHDRDisplay;
+    bool useColorManagement;
+
+    Return<void> status = sfConfigs->hasWideColorDisplay(
+        [&](OptionalBool arg) { hasWideColorDisplay = arg.specified; });
+    EXPECT_OK(status);
+
+    status = sfConfigs->hasHDRDisplay([&](OptionalBool arg) { hasHDRDisplay = arg.specified; });
+    EXPECT_OK(status);
+
+    status = sfConfigs->useColorManagement(
+        [&](OptionalBool arg) { useColorManagement = arg.specified; });
+    EXPECT_OK(status);
+
+    // When hasHDRDisplay returns true, hasWideColorDisplay must also return true.
+    if (hasHDRDisplay) {
+        ASSERT_TRUE(hasWideColorDisplay);
+    }
+
+    // When hasWideColorDisplay returns true, useColorManagement
+    // must also return true.
+    if (hasWideColorDisplay) {
+        ASSERT_TRUE(useColorManagement);
+    }
+}
+
+TEST_F(ConfigstoreHidlTest, TestGetCompositionPreference) {
+    bool hasWideColorDisplay;
+
+    Return<void> status = sfConfigs->hasWideColorDisplay(
+        [&](OptionalBool arg) { hasWideColorDisplay = arg.specified; });
+    EXPECT_OK(status);
+
+    Dataspace defaultDataspace, wcgDataspace;
+
+    status = sfConfigs->getCompositionPreference(
+        [&](auto tmpDefaultDataspace, PixelFormat, auto tmpWcgDataspace, PixelFormat) {
+            defaultDataspace = tmpDefaultDataspace;
+            wcgDataspace = tmpWcgDataspace;
+        });
+    EXPECT_OK(status);
+
+    // Default data space and wide color gamut data space must not be UNKNOWN.
+    ASSERT_TRUE(defaultDataspace != Dataspace::UNKNOWN && wcgDataspace != Dataspace::UNKNOWN);
+
+    // If hasWideColorDisplay returns true, the wide color gamut data space must be a valid wide
+    // color gamut.
+    if (hasWideColorDisplay) {
+        ASSERT_TRUE(isSupportedWideColorGamut(wcgDataspace));
+    }
+}
+
+int main(int argc, char** argv) {
+    ::testing::AddGlobalTestEnvironment(ConfigstoreHidlEnvironment::Instance());
+    ::testing::InitGoogleTest(&argc, argv);
+    ConfigstoreHidlEnvironment::Instance()->init(&argc, argv);
+    int status = RUN_ALL_TESTS();
+    LOG(INFO) << "Test result = " << status;
+    return status;
+}
diff --git a/graphics/composer/2.3/IComposerClient.hal b/graphics/composer/2.3/IComposerClient.hal
index dec3b25..7856658 100644
--- a/graphics/composer/2.3/IComposerClient.hal
+++ b/graphics/composer/2.3/IComposerClient.hal
@@ -161,6 +161,116 @@
                                Dataspace dataspace)
                     generates (Error error);
 
+    enum FormatColorComponent : uint8_t {
+        /* The first component  (eg, for RGBA_8888, this is R) */
+        FORMAT_COMPONENT_0 = 1 << 0,
+        /* The second component (eg, for RGBA_8888, this is G) */
+        FORMAT_COMPONENT_1 = 1 << 1,
+        /* The third component  (eg, for RGBA_8888, this is B) */
+        FORMAT_COMPONENT_2 = 1 << 2,
+        /* The fourth component (eg, for RGBA_8888, this is A) */
+        FORMAT_COMPONENT_3 = 1 << 3,
+    };
+
+    /**
+     * Query for what types of color sampling the hardware supports.
+     *
+     * @param  display        is the display where the samples are collected.
+     * @return error          is NONE upon success. Otherwise,
+     *                        BAD_DISPLAY when an invalid display was passed in, or
+     *                        UNSUPPORTED when there is no efficient way to sample.
+     * @return format         The format of the sampled pixels.
+     * @return dataspace      The dataspace of the sampled pixels.
+     * @return componentMask  The mask of which components can be sampled.
+     */
+    getDisplayedContentSamplingAttributes(Display display)
+               generates (Error error,
+                          PixelFormat format,
+                          Dataspace dataspace,
+                          bitfield<FormatColorComponent> componentMask);
+
+    /** DisplayedContentSampling values passed to setDisplayedContentSamplingEnabled. */
+    enum DisplayedContentSampling : int32_t {
+        INVALID = 0,
+
+        /** Enable content sampling. */
+        ENABLE = 1,
+
+        /** Disable content sampling. */
+        DISABLE = 2,
+    };
+
+    /**
+     * Enables or disables the collection of color content statistics
+     * on this display.
+     *
+     * Sampling occurs on the contents of the final composition on this display
+     * (i.e., the contents presented on screen). Samples should be collected after all
+     * color transforms have been applied.
+     *
+     * Sampling support is optional, and is set to DISABLE by default.
+     * On each call to ENABLE, all collected statistics must be reset.
+     *
+     * Sample data can be queried via getDisplayedContentSample().
+     *
+     * @param display        is the display to which the sampling mode is set.
+     * @param enabled        indicates whether to enable or disable sampling.
+     * @param componentMask  The mask of which components should be sampled. If zero, all supported
+     *                       components are to be enabled.
+     * @param maxFrames      is the maximum number of frames that should be stored before discard.
+     *                       The sample represents the most-recently posted frames.
+     * @return error      is NONE upon success. Otherwise,
+     *                    BAD_DISPLAY when an invalid display handle was passed in,
+     *                    BAD_PARAMETER when enabled was an invalid value, or
+     *                    NO_RESOURCES when the requested ringbuffer size via maxFrames was
+     *                                 not available.
+     *                    UNSUPPORTED when there is no efficient way to sample.
+     */
+    setDisplayedContentSamplingEnabled(
+        Display display, DisplayedContentSampling enable,
+        bitfield<FormatColorComponent> componentMask, uint64_t maxFrames)
+        generates (Error error);
+
+    /**
+     * Collects the results of display content color sampling for display.
+     *
+     * Collection of data can occur whether the sampling is in ENABLE or
+     * DISABLE state.
+     *
+     * @param  display     is the display to which the sampling is collected.
+     * @param  maxFrames   is the maximum number of frames that should be represented in the sample.
+     *                     The sample represents the most-recently posted frames.
+     *                     If maxFrames is 0, all frames are to be represented by the sample.
+     * @param  timestamp   is the timestamp after which any frames were posted that should be
+     *                     included in the sample. Timestamp is CLOCK_MONOTONIC.
+     *                     If timestamp is 0, do not filter from the sample by time.
+     * @return error       is NONE upon success. Otherwise,
+     *                     BAD_DISPLAY   when an invalid display was passed in, or
+     *                     UNSUPPORTED   when there is no efficient way to sample, or
+     *                     BAD_PARAMETER when the component is not supported by the hardware.
+     * @return frameCount  The number of frames represented by this sample.
+     * @return sampleComponent0 is a histogram counting how many times a pixel of a given value
+     *                     was displayed onscreen for FORMAT_COMPONENT_0.
+     *                     The buckets of the histogram are evenly weighted, the number of buckets
+     *                     is device specific.
+     *                     eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that
+     *                     10 red pixels were displayed onscreen in range 0x00->0x3F, 6 red pixels
+     *                     were displayed onscreen in range 0x40->0x7F, etc.
+     * @return sampleComponent1 is the same sample definition as sampleComponent0,
+     *                     but for FORMAT_COMPONENT_1.
+     * @return sampleComponent2 is the same sample definition as sampleComponent0,
+     *                     but for FORMAT_COMPONENT_2.
+     * @return sampleComponent3 is the same sample definition as sampleComponent0,
+     *                     but for FORMAT_COMPONENT_3.
+     */
+    getDisplayedContentSample(Display display, uint64_t maxFrames, uint64_t timestamp)
+               generates (Error error,
+                          uint64_t frameCount,
+                          vec<uint64_t> sampleComponent0,
+                          vec<uint64_t> sampleComponent1,
+                          vec<uint64_t> sampleComponent2,
+                          vec<uint64_t> sampleComponent3);
+
     /**
      * Executes commands from the input command message queue. Return values
      * generated by the input commands are written to the output command
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
index f9d6774..be0ef4c 100644
--- a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerClient.h
@@ -91,6 +91,42 @@
         return Void();
     }
 
+    Return<void> getDisplayedContentSamplingAttributes(
+        uint64_t display,
+        IComposerClient::getDisplayedContentSamplingAttributes_cb hidl_cb) override {
+        common::V1_1::PixelFormat format;
+        common::V1_2::Dataspace dataspace;
+        hidl_bitfield<IComposerClient::FormatColorComponent> componentMask;
+        Error error =
+            mHal->getDisplayedContentSamplingAttributes(display, format, dataspace, componentMask);
+        hidl_cb(error, format, dataspace, componentMask);
+        return Void();
+    }
+
+    Return<Error> setDisplayedContentSamplingEnabled(
+        uint64_t display, IComposerClient::DisplayedContentSampling enable,
+        hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+        uint64_t maxFrames) override {
+        return mHal->setDisplayedContentSamplingEnabled(display, enable, componentMask, maxFrames);
+    }
+
+    Return<void> getDisplayedContentSample(
+        uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+        IComposerClient::getDisplayedContentSample_cb hidl_cb) override {
+        uint64_t frameCount;
+        hidl_vec<uint64_t> sampleComponent0;
+        hidl_vec<uint64_t> sampleComponent1;
+        hidl_vec<uint64_t> sampleComponent2;
+        hidl_vec<uint64_t> sampleComponent3;
+
+        Error error = mHal->getDisplayedContentSample(display, maxFrames, timestamp, frameCount,
+                                                      sampleComponent0, sampleComponent1,
+                                                      sampleComponent2, sampleComponent3);
+        hidl_cb(error, frameCount, sampleComponent0, sampleComponent1, sampleComponent2,
+                sampleComponent3);
+        return Void();
+    }
+
     Return<void> executeCommands_2_3(uint32_t inLength, const hidl_vec<hidl_handle>& inHandles,
                                      IComposerClient::executeCommands_2_2_cb hidl_cb) override {
         std::lock_guard<std::mutex> lock(mCommandEngineMutex);
diff --git a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h
index 27efc2b..8ca5d75 100644
--- a/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h
+++ b/graphics/composer/2.3/utils/hal/include/composer-hal/2.3/ComposerHal.h
@@ -72,6 +72,18 @@
     virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort,
                                                std::vector<uint8_t>* outData) = 0;
     virtual Error setLayerColorTransform(Display display, Layer layer, const float* matrix) = 0;
+    virtual Error getDisplayedContentSamplingAttributes(
+        uint64_t display, PixelFormat& format, Dataspace& dataspace,
+        hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) = 0;
+    virtual Error setDisplayedContentSamplingEnabled(
+        uint64_t display, IComposerClient::DisplayedContentSampling enable,
+        hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, uint64_t maxFrames) = 0;
+    virtual Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames,
+                                            uint64_t timestamp, uint64_t& frameCount,
+                                            hidl_vec<uint64_t>& sampleComponent0,
+                                            hidl_vec<uint64_t>& sampleComponent1,
+                                            hidl_vec<uint64_t>& sampleComponent2,
+                                            hidl_vec<uint64_t>& sampleComponent3) = 0;
 };
 
 }  // namespace hal
diff --git a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
index df68f58..8d444c8 100644
--- a/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
+++ b/graphics/composer/2.3/utils/passthrough/include/composer-passthrough/2.3/HwcHal.h
@@ -104,6 +104,65 @@
         return static_cast<Error>(err);
     }
 
+    Error getDisplayedContentSamplingAttributes(
+        uint64_t display, PixelFormat& format, Dataspace& dataspace,
+        hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) override {
+        if (!mDispatch.getDisplayedContentSamplingAttributes) {
+            return Error::UNSUPPORTED;
+        }
+        int32_t formatRaw = 0;
+        int32_t dataspaceRaw = 0;
+        uint8_t componentMaskRaw = 0;
+        int32_t errorRaw = mDispatch.getDisplayedContentSamplingAttributes(
+            mDevice, display, &formatRaw, &dataspaceRaw, &componentMaskRaw);
+        auto error = static_cast<Error>(errorRaw);
+        if (error == Error::NONE) {
+            format = static_cast<PixelFormat>(formatRaw);
+            dataspace = static_cast<Dataspace>(dataspaceRaw);
+            componentMask =
+                static_cast<hidl_bitfield<IComposerClient::FormatColorComponent>>(componentMaskRaw);
+        }
+        return error;
+    };
+
+    Error setDisplayedContentSamplingEnabled(
+        uint64_t display, IComposerClient::DisplayedContentSampling enable,
+        hidl_bitfield<IComposerClient::FormatColorComponent> componentMask,
+        uint64_t maxFrames) override {
+        if (!mDispatch.setDisplayedContentSamplingEnabled) {
+            return Error::UNSUPPORTED;
+        }
+        return static_cast<Error>(mDispatch.setDisplayedContentSamplingEnabled(
+            mDevice, display, static_cast<int32_t>(enable), componentMask, maxFrames));
+    }
+
+    Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+                                    uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
+                                    hidl_vec<uint64_t>& sampleComponent1,
+                                    hidl_vec<uint64_t>& sampleComponent2,
+                                    hidl_vec<uint64_t>& sampleComponent3) override {
+        if (!mDispatch.getDisplayedContentSample) {
+            return Error::UNSUPPORTED;
+        }
+
+        int32_t size[4] = {0};
+        auto errorRaw = mDispatch.getDisplayedContentSample(mDevice, display, maxFrames, timestamp,
+                                                            &frameCount, size, nullptr);
+        if (errorRaw != HWC2_ERROR_NONE) {
+            return static_cast<Error>(errorRaw);
+        }
+
+        sampleComponent0.resize(size[0]);
+        sampleComponent1.resize(size[1]);
+        sampleComponent2.resize(size[2]);
+        sampleComponent3.resize(size[3]);
+        uint64_t* samples[] = {sampleComponent0.data(), sampleComponent1.data(),
+                               sampleComponent2.data(), sampleComponent3.data()};
+        errorRaw = mDispatch.getDisplayedContentSample(mDevice, display, maxFrames, timestamp,
+                                                       &frameCount, size, samples);
+        return static_cast<Error>(errorRaw);
+    }
+
    protected:
     bool initDispatch() override {
         if (!BaseType2_2::initDispatch()) {
@@ -114,6 +173,12 @@
                                    &mDispatch.getDisplayIdentificationData);
         this->initOptionalDispatch(HWC2_FUNCTION_SET_LAYER_COLOR_TRANSFORM,
                                    &mDispatch.setLayerColorTransform);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES,
+                                   &mDispatch.getDisplayedContentSamplingAttributes);
+        this->initOptionalDispatch(HWC2_FUNCTION_SET_DISPLAYED_CONTENT_SAMPLING_ENABLED,
+                                   &mDispatch.setDisplayedContentSamplingEnabled);
+        this->initOptionalDispatch(HWC2_FUNCTION_GET_DISPLAYED_CONTENT_SAMPLE,
+                                   &mDispatch.getDisplayedContentSample);
         return true;
     }
 
@@ -121,6 +186,9 @@
     struct {
         HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA getDisplayIdentificationData;
         HWC2_PFN_SET_LAYER_COLOR_TRANSFORM setLayerColorTransform;
+        HWC2_PFN_GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES getDisplayedContentSamplingAttributes;
+        HWC2_PFN_SET_DISPLAYED_CONTENT_SAMPLING_ENABLED setDisplayedContentSamplingEnabled;
+        HWC2_PFN_GET_DISPLAYED_CONTENT_SAMPLE getDisplayedContentSample;
     } mDispatch = {};
 
     using BaseType2_2 = V2_2::passthrough::detail::HwcHalImpl<Hal>;
diff --git a/graphics/composer/2.3/utils/vts/ComposerVts.cpp b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
index 656c8c4..9304992 100644
--- a/graphics/composer/2.3/utils/vts/ComposerVts.cpp
+++ b/graphics/composer/2.3/utils/vts/ComposerVts.cpp
@@ -108,6 +108,48 @@
     return error == Error::NONE;
 }
 
+Error ComposerClient::getDisplayedContentSamplingAttributes(
+    uint64_t display, PixelFormat& format, Dataspace& dataspace,
+    hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask) {
+    auto error = Error::BAD_PARAMETER;
+    mClient->getDisplayedContentSamplingAttributes(
+        display, [&](const auto& tmpError, const auto& tmpFormat, const auto& tmpDataspace,
+                     const auto& tmpComponentMask) {
+            error = tmpError;
+            format = tmpFormat;
+            dataspace = tmpDataspace;
+            componentMask = tmpComponentMask;
+        });
+    return error;
+}
+
+Error ComposerClient::setDisplayedContentSamplingEnabled(
+    uint64_t display, IComposerClient::DisplayedContentSampling enable,
+    hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, uint64_t maxFrames) {
+    return mClient->setDisplayedContentSamplingEnabled(display, enable, componentMask, maxFrames);
+}
+
+Error ComposerClient::getDisplayedContentSample(uint64_t display, uint64_t maxFrames,
+                                                uint64_t timestamp, uint64_t& frameCount,
+                                                hidl_vec<uint64_t>& sampleComponent0,
+                                                hidl_vec<uint64_t>& sampleComponent1,
+                                                hidl_vec<uint64_t>& sampleComponent2,
+                                                hidl_vec<uint64_t>& sampleComponent3) {
+    auto error = Error::BAD_PARAMETER;
+    mClient->getDisplayedContentSample(
+        display, maxFrames, timestamp,
+        [&](const auto& tmpError, const auto& tmpFrameCount, const auto& tmpSamples0,
+            const auto& tmpSamples1, const auto& tmpSamples2, const auto& tmpSamples3) {
+            error = tmpError;
+            frameCount = tmpFrameCount;
+            sampleComponent0 = tmpSamples0;
+            sampleComponent1 = tmpSamples1;
+            sampleComponent2 = tmpSamples2;
+            sampleComponent3 = tmpSamples3;
+        });
+    return error;
+}
+
 }  // namespace vts
 }  // namespace V2_3
 }  // namespace composer
diff --git a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
index ec1a2a1..4b9c955 100644
--- a/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
+++ b/graphics/composer/2.3/utils/vts/include/composer-vts/2.3/ComposerVts.h
@@ -37,6 +37,7 @@
 using common::V1_2::ColorMode;
 using common::V1_2::Dataspace;
 using V2_1::Display;
+using V2_1::Error;
 using V2_3::IComposer;
 using V2_3::IComposerClient;
 
@@ -67,6 +68,17 @@
 
     bool getDisplayIdentificationData(Display display, uint8_t* outPort,
                                       std::vector<uint8_t>* outData);
+    Error getDisplayedContentSamplingAttributes(
+        uint64_t display, PixelFormat& format, Dataspace& dataspace,
+        hidl_bitfield<IComposerClient::FormatColorComponent>& componentMask);
+    Error setDisplayedContentSamplingEnabled(
+        uint64_t display, IComposerClient::DisplayedContentSampling enable,
+        hidl_bitfield<IComposerClient::FormatColorComponent> componentMask, uint64_t maxFrames);
+    Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+                                    uint64_t& frameCount, hidl_vec<uint64_t>& sampleComponent0,
+                                    hidl_vec<uint64_t>& sampleComponent1,
+                                    hidl_vec<uint64_t>& sampleComponent2,
+                                    hidl_vec<uint64_t>& sampleComponent3);
 
     std::vector<ColorMode> getColorModes_2_3(Display display);
 
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
index 3ca21aa..a294825 100644
--- a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -355,6 +355,78 @@
     execute();
 }
 
+TEST_F(GraphicsComposerHidlTest, GetDisplayedContentSamplingAttributes) {
+    using common::V1_1::PixelFormat;
+    using common::V1_2::Dataspace;
+
+    int constexpr invalid = -1;
+    auto format = static_cast<PixelFormat>(invalid);
+    auto dataspace = static_cast<Dataspace>(invalid);
+    auto componentMask = static_cast<hidl_bitfield<IComposerClient::FormatColorComponent>>(invalid);
+    auto error = mComposerClient->getDisplayedContentSamplingAttributes(mPrimaryDisplay, format,
+                                                                        dataspace, componentMask);
+
+    if (error == Error::UNSUPPORTED) {
+        SUCCEED() << "Device does not support optional extension. Test skipped";
+        return;
+    }
+
+    EXPECT_EQ(error, Error::NONE);
+    EXPECT_NE(format, static_cast<PixelFormat>(invalid));
+    EXPECT_NE(dataspace, static_cast<Dataspace>(invalid));
+    EXPECT_NE(componentMask,
+              static_cast<hidl_bitfield<IComposerClient::FormatColorComponent>>(invalid));
+};
+
+TEST_F(GraphicsComposerHidlTest, SetDisplayedContentSamplingEnabled) {
+    auto const maxFrames = 10;
+    auto const enableAllComponents = 0;
+    auto error = mComposerClient->setDisplayedContentSamplingEnabled(
+        mPrimaryDisplay, IComposerClient::DisplayedContentSampling::ENABLE, enableAllComponents,
+        maxFrames);
+    if (error == Error::UNSUPPORTED) {
+        SUCCEED() << "Device does not support optional extension. Test skipped";
+        return;
+    }
+    EXPECT_EQ(error, Error::NONE);
+
+    error = mComposerClient->setDisplayedContentSamplingEnabled(
+        mPrimaryDisplay, IComposerClient::DisplayedContentSampling::DISABLE, enableAllComponents,
+        maxFrames);
+    EXPECT_EQ(error, Error::NONE);
+}
+
+TEST_F(GraphicsComposerHidlTest, GetDisplayedContentSample) {
+    int constexpr invalid = -1;
+    auto format = static_cast<PixelFormat>(invalid);
+    auto dataspace = static_cast<Dataspace>(invalid);
+    auto componentMask = static_cast<hidl_bitfield<IComposerClient::FormatColorComponent>>(invalid);
+    auto error = mComposerClient->getDisplayedContentSamplingAttributes(mPrimaryDisplay, format,
+                                                                        dataspace, componentMask);
+
+    uint64_t maxFrames = 10;
+    uint64_t timestamp = 0;
+    uint64_t frameCount = 0;
+    hidl_array<hidl_vec<uint64_t>, 4> histogram;
+    error = mComposerClient->getDisplayedContentSample(mPrimaryDisplay, maxFrames, timestamp,
+                                                       frameCount, histogram[0], histogram[1],
+                                                       histogram[2], histogram[3]);
+    if (error == Error::UNSUPPORTED) {
+        SUCCEED() << "Device does not support optional extension. Test skipped";
+        return;
+    }
+
+    EXPECT_EQ(error, Error::NONE);
+    EXPECT_LE(frameCount, maxFrames);
+    for (auto i = 0; i < histogram.size(); i++) {
+        if (componentMask & (1 << i)) {
+            EXPECT_NE(histogram[i].size(), 0);
+        } else {
+            EXPECT_EQ(histogram[i].size(), 0);
+        }
+    }
+}
+
 }  // namespace
 }  // namespace vts
 }  // namespace V2_3
diff --git a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.cpp b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.cpp
index f8b021e..00207b1 100644
--- a/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.cpp
+++ b/sensors/1.0/vts/functional/SensorsHidlEnvironmentV1_0.cpp
@@ -94,9 +94,9 @@
 }
 
 void SensorsHidlEnvironmentV1_0::startPollingThread() {
-    stopThread = false;
-    pollThread = std::thread(pollingThread, this, std::ref(stopThread));
-    events.reserve(128);
+    mStopThread = false;
+    mPollThread = std::thread(pollingThread, this, std::ref(mStopThread));
+    mEvents.reserve(128);
 }
 
 void SensorsHidlEnvironmentV1_0::pollingThread(SensorsHidlEnvironmentV1_0* env,
diff --git a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp b/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
index 2fb67a6..37b7349 100644
--- a/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
+++ b/sensors/2.0/vts/functional/SensorsHidlEnvironmentV2_0.cpp
@@ -95,19 +95,19 @@
 }
 
 void SensorsHidlEnvironmentV2_0::HidlTearDown() {
-    stopThread = true;
+    mStopThread = true;
 
     // Wake up the event queue so the poll thread can exit
     mEventQueueFlag->wake(asBaseType(EventQueueFlagBits::READ_AND_PROCESS));
-    pollThread.join();
+    mPollThread.join();
 
     EventFlag::deleteEventFlag(&mEventQueueFlag);
 }
 
 void SensorsHidlEnvironmentV2_0::startPollingThread() {
-    stopThread = false;
-    pollThread = std::thread(pollingThread, this);
-    events.reserve(MAX_RECEIVE_BUFFER_EVENT_COUNT);
+    mStopThread = false;
+    mPollThread = std::thread(pollingThread, this);
+    mEvents.reserve(MAX_RECEIVE_BUFFER_EVENT_COUNT);
 }
 
 void SensorsHidlEnvironmentV2_0::readEvents() {
@@ -133,7 +133,7 @@
 void SensorsHidlEnvironmentV2_0::pollingThread(SensorsHidlEnvironmentV2_0* env) {
     ALOGD("polling thread start");
 
-    while (!env->stopThread.load()) {
+    while (!env->mStopThread.load()) {
         env->readEvents();
     }
 
diff --git a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
index 94cd6e5..3e5837b 100644
--- a/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
+++ b/sensors/2.0/vts/functional/VtsHalSensorsV2_0TargetTest.cpp
@@ -25,15 +25,89 @@
 #include <utils/SystemClock.h>
 
 #include <cinttypes>
+#include <condition_variable>
+#include <map>
 #include <vector>
 
 using ::android::sp;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+using ::android::hardware::sensors::V1_0::MetaDataEventType;
 using ::android::hardware::sensors::V1_0::OperationMode;
 using ::android::hardware::sensors::V1_0::SensorStatus;
 using ::android::hardware::sensors::V1_0::Vec3;
 
+class EventCallback : public IEventCallback {
+   public:
+    void reset() {
+        mFlushMap.clear();
+        mEventMap.clear();
+    }
+
+    void onEvent(const ::android::hardware::sensors::V1_0::Event& event) override {
+        if (event.sensorType == SensorType::ADDITIONAL_INFO &&
+            event.u.meta.what == MetaDataEventType::META_DATA_FLUSH_COMPLETE) {
+            std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+            mFlushMap[event.sensorHandle]++;
+            mFlushCV.notify_all();
+        } else if (event.sensorType != SensorType::ADDITIONAL_INFO) {
+            std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+            mEventMap[event.sensorHandle].push_back(event);
+            mEventCV.notify_all();
+        }
+    }
+
+    int32_t getFlushCount(int32_t sensorHandle) {
+        std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+        return mFlushMap[sensorHandle];
+    }
+
+    void waitForFlushEvents(const std::vector<SensorInfo>& sensorsToWaitFor,
+                            int32_t numCallsToFlush, int64_t timeoutMs) {
+        std::unique_lock<std::recursive_mutex> lock(mFlushMutex);
+        mFlushCV.wait_for(lock, std::chrono::milliseconds(timeoutMs),
+                          [&] { return flushesReceived(sensorsToWaitFor, numCallsToFlush); });
+    }
+
+    const std::vector<Event> getEvents(int32_t sensorHandle) {
+        std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+        return mEventMap[sensorHandle];
+    }
+
+    void waitForEvents(const std::vector<SensorInfo>& sensorsToWaitFor, int32_t timeoutMs) {
+        std::unique_lock<std::recursive_mutex> lock(mEventMutex);
+        mEventCV.wait_for(lock, std::chrono::milliseconds(timeoutMs),
+                          [&] { return eventsReceived(sensorsToWaitFor); });
+    }
+
+   protected:
+    bool flushesReceived(const std::vector<SensorInfo>& sensorsToWaitFor, int32_t numCallsToFlush) {
+        for (const SensorInfo& sensor : sensorsToWaitFor) {
+            if (getFlushCount(sensor.sensorHandle) < numCallsToFlush) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    bool eventsReceived(const std::vector<SensorInfo>& sensorsToWaitFor) {
+        for (const SensorInfo& sensor : sensorsToWaitFor) {
+            if (getEvents(sensor.sensorHandle).size() == 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    std::map<int32_t, int32_t> mFlushMap;
+    std::recursive_mutex mFlushMutex;
+    std::condition_variable_any mFlushCV;
+
+    std::map<int32_t, std::vector<Event>> mEventMap;
+    std::recursive_mutex mEventMutex;
+    std::condition_variable_any mEventCV;
+};
+
 // The main test class for SENSORS HIDL HAL.
 
 class SensorsHidlTest : public SensorsHidlTestBase {
@@ -80,8 +154,17 @@
         return SensorsHidlEnvironmentV2_0::Instance();
     }
 
+    // Test helpers
+    void runSingleFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
+                            int32_t expectedFlushCount, Result expectedResponse);
+    void runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
+                      int32_t flushCalls, int32_t expectedFlushCount, Result expectedResponse);
+
     // Helper functions
     void activateAllSensors(bool enable);
+    std::vector<SensorInfo> getNonOneShotSensors();
+    std::vector<SensorInfo> getOneShotSensors();
+    int32_t getInvalidSensorHandle();
 };
 
 Return<Result> SensorsHidlTest::activate(int32_t sensorHandle, bool enabled) {
@@ -140,6 +223,35 @@
     return ret;
 }
 
+std::vector<SensorInfo> SensorsHidlTest::getNonOneShotSensors() {
+    std::vector<SensorInfo> sensors;
+    for (const SensorInfo& info : getSensorsList()) {
+        if (extractReportMode(info.flags) != SensorFlagBits::ONE_SHOT_MODE) {
+            sensors.push_back(info);
+        }
+    }
+    return sensors;
+}
+
+std::vector<SensorInfo> SensorsHidlTest::getOneShotSensors() {
+    std::vector<SensorInfo> sensors;
+    for (const SensorInfo& info : getSensorsList()) {
+        if (extractReportMode(info.flags) == SensorFlagBits::ONE_SHOT_MODE) {
+            sensors.push_back(info);
+        }
+    }
+    return sensors;
+}
+
+int32_t SensorsHidlTest::getInvalidSensorHandle() {
+    // Find a sensor handle that does not exist in the sensor list
+    int32_t maxHandle = 0;
+    for (const SensorInfo& sensor : getSensorsList()) {
+        maxHandle = max(maxHandle, sensor.sensorHandle);
+    }
+    return maxHandle + 1;
+}
+
 // Test if sensor list returned is valid
 TEST_F(SensorsHidlTest, SensorListValid) {
     getSensors()->getSensorsList([&](const auto& list) {
@@ -488,6 +600,195 @@
     activateAllSensors(false);
 }
 
+void SensorsHidlTest::runSingleFlushTest(const std::vector<SensorInfo>& sensors,
+                                         bool activateSensor, int32_t expectedFlushCount,
+                                         Result expectedResponse) {
+    runFlushTest(sensors, activateSensor, 1 /* flushCalls */, expectedFlushCount, expectedResponse);
+}
+
+void SensorsHidlTest::runFlushTest(const std::vector<SensorInfo>& sensors, bool activateSensor,
+                                   int32_t flushCalls, int32_t expectedFlushCount,
+                                   Result expectedResponse) {
+    EventCallback callback;
+    getEnvironment()->registerCallback(&callback);
+
+    for (const SensorInfo& sensor : sensors) {
+        // Configure and activate the sensor
+        batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */);
+        activate(sensor.sensorHandle, activateSensor);
+
+        // Flush the sensor
+        for (int32_t i = 0; i < flushCalls; i++) {
+            Result flushResult = flush(sensor.sensorHandle);
+            ASSERT_EQ(flushResult, expectedResponse);
+        }
+        activate(sensor.sensorHandle, false);
+    }
+
+    // Wait up to one second for the flush events
+    callback.waitForFlushEvents(sensors, flushCalls, 1000 /* timeoutMs */);
+    getEnvironment()->unregisterCallback();
+
+    // Check that the correct number of flushes are present for each sensor
+    for (const SensorInfo& sensor : sensors) {
+        ASSERT_EQ(callback.getFlushCount(sensor.sensorHandle), expectedFlushCount);
+    }
+}
+
+TEST_F(SensorsHidlTest, FlushSensor) {
+    // Find a sensor that is not a one-shot sensor
+    std::vector<SensorInfo> sensors = getNonOneShotSensors();
+    if (sensors.size() == 0) {
+        return;
+    }
+
+    constexpr int32_t kFlushes = 5;
+    runSingleFlushTest(sensors, true /* activateSensor */, 1 /* expectedFlushCount */, Result::OK);
+    runFlushTest(sensors, true /* activateSensor */, kFlushes, kFlushes, Result::OK);
+}
+
+TEST_F(SensorsHidlTest, FlushOneShotSensor) {
+    // Find a sensor that is a one-shot sensor
+    std::vector<SensorInfo> sensors = getOneShotSensors();
+    if (sensors.size() == 0) {
+        return;
+    }
+
+    runSingleFlushTest(sensors, true /* activateSensor */, 0 /* expectedFlushCount */,
+                       Result::BAD_VALUE);
+}
+
+TEST_F(SensorsHidlTest, FlushInactiveSensor) {
+    // Attempt to find a non-one shot sensor, then a one-shot sensor if necessary
+    std::vector<SensorInfo> sensors = getNonOneShotSensors();
+    if (sensors.size() == 0) {
+        sensors = getOneShotSensors();
+        if (sensors.size() == 0) {
+            return;
+        }
+    }
+
+    runSingleFlushTest(sensors, false /* activateSensor */, 0 /* expectedFlushCount */,
+                       Result::BAD_VALUE);
+}
+
+TEST_F(SensorsHidlTest, FlushNonexistentSensor) {
+    SensorInfo sensor;
+    std::vector<SensorInfo> sensors = getNonOneShotSensors();
+    if (sensors.size() == 0) {
+        sensors = getOneShotSensors();
+        if (sensors.size() == 0) {
+            return;
+        }
+    }
+    sensor = sensors.front();
+    sensor.sensorHandle = getInvalidSensorHandle();
+    runSingleFlushTest(std::vector<SensorInfo>{sensor}, false /* activateSensor */,
+                       0 /* expectedFlushCount */, Result::BAD_VALUE);
+}
+
+TEST_F(SensorsHidlTest, Batch) {
+    if (getSensorsList().size() == 0) {
+        return;
+    }
+
+    activateAllSensors(false /* enable */);
+    for (const SensorInfo& sensor : getSensorsList()) {
+        // Call batch on inactive sensor
+        ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
+                  Result::OK);
+
+        // Activate the sensor
+        activate(sensor.sensorHandle, true /* enabled */);
+
+        // Call batch on an active sensor
+        ASSERT_EQ(batch(sensor.sensorHandle, sensor.maxDelay, 0 /* maxReportLatencyNs */),
+                  Result::OK);
+    }
+    activateAllSensors(false /* enable */);
+
+    // Call batch on an invalid sensor
+    SensorInfo sensor = getSensorsList().front();
+    sensor.sensorHandle = getInvalidSensorHandle();
+    ASSERT_EQ(batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */),
+              Result::BAD_VALUE);
+}
+
+TEST_F(SensorsHidlTest, Activate) {
+    if (getSensorsList().size() == 0) {
+        return;
+    }
+
+    // Verify that sensor events are generated when activate is called
+    for (const SensorInfo& sensor : getSensorsList()) {
+        batch(sensor.sensorHandle, sensor.minDelay, 0 /* maxReportLatencyNs */);
+        ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+        // Call activate on a sensor that is already activated
+        ASSERT_EQ(activate(sensor.sensorHandle, true), Result::OK);
+
+        // Deactivate the sensor
+        ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+
+        // Call deactivate on a sensor that is already deactivated
+        ASSERT_EQ(activate(sensor.sensorHandle, false), Result::OK);
+    }
+
+    // Attempt to activate an invalid sensor
+    int32_t invalidHandle = getInvalidSensorHandle();
+    ASSERT_EQ(activate(invalidHandle, true), Result::BAD_VALUE);
+    ASSERT_EQ(activate(invalidHandle, false), Result::BAD_VALUE);
+}
+
+TEST_F(SensorsHidlTest, NoStaleEvents) {
+    constexpr int64_t kFiveHundredMilliseconds = 500 * 1000;
+    constexpr int64_t kOneSecond = 1000 * 1000;
+
+    // Register the callback to receive sensor events
+    EventCallback callback;
+    getEnvironment()->registerCallback(&callback);
+
+    const std::vector<SensorInfo> sensors = getSensorsList();
+    int32_t maxMinDelay = 0;
+    for (const SensorInfo& sensor : getSensorsList()) {
+        maxMinDelay = std::max(maxMinDelay, sensor.minDelay);
+    }
+
+    // Activate the sensors so that they start generating events
+    activateAllSensors(true);
+
+    // According to the CDD, the first sample must be generated within 400ms + 2 * sample_time
+    // and the maximum reporting latency is 100ms + 2 * sample_time. Wait a sufficient amount
+    // of time to guarantee that a sample has arrived.
+    callback.waitForEvents(sensors, kFiveHundredMilliseconds + (5 * maxMinDelay));
+    activateAllSensors(false);
+
+    // Save the last received event for each sensor
+    std::map<int32_t, int64_t> lastEventTimestampMap;
+    for (const SensorInfo& sensor : sensors) {
+        ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
+        lastEventTimestampMap[sensor.sensorHandle] =
+            callback.getEvents(sensor.sensorHandle).back().timestamp;
+    }
+
+    // Allow some time to pass, reset the callback, then reactivate the sensors
+    usleep(kOneSecond + (5 * maxMinDelay));
+    callback.reset();
+    activateAllSensors(true);
+    callback.waitForEvents(sensors, kFiveHundredMilliseconds + (5 * maxMinDelay));
+    activateAllSensors(false);
+
+    for (const SensorInfo& sensor : sensors) {
+        // Ensure that the first event received is not stale by ensuring that its timestamp is
+        // sufficiently different from the previous event
+        const Event newEvent = callback.getEvents(sensor.sensorHandle).front();
+        int64_t delta = newEvent.timestamp - lastEventTimestampMap[sensor.sensorHandle];
+        ASSERT_GE(delta, kFiveHundredMilliseconds + (3 * sensor.minDelay));
+    }
+
+    getEnvironment()->unregisterCallback();
+}
+
 int main(int argc, char** argv) {
     ::testing::AddGlobalTestEnvironment(SensorsHidlEnvironmentV2_0::Instance());
     ::testing::InitGoogleTest(&argc, argv);
diff --git a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp b/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
index 21c08d2..affdf8b 100644
--- a/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
+++ b/sensors/common/vts/utils/SensorsHidlEnvironmentBase.cpp
@@ -19,7 +19,7 @@
 void SensorsHidlEnvironmentBase::HidlSetUp() {
     ASSERT_TRUE(resetHal()) << "could not get hidl service";
 
-    collectionEnabled = false;
+    mCollectionEnabled = false;
     startPollingThread();
 
     // In case framework just stopped for test and there is sensor events in the pipe,
@@ -28,26 +28,40 @@
 }
 
 void SensorsHidlEnvironmentBase::HidlTearDown() {
-    stopThread = true;
-    pollThread.detach();
+    mStopThread = true;
+    mPollThread.detach();
 }
 
 void SensorsHidlEnvironmentBase::catEvents(std::vector<Event>* output) {
-    std::lock_guard<std::mutex> lock(events_mutex);
+    std::lock_guard<std::mutex> lock(mEventsMutex);
     if (output) {
-        output->insert(output->end(), events.begin(), events.end());
+        output->insert(output->end(), mEvents.begin(), mEvents.end());
     }
-    events.clear();
+    mEvents.clear();
 }
 
 void SensorsHidlEnvironmentBase::setCollection(bool enable) {
-    std::lock_guard<std::mutex> lock(events_mutex);
-    collectionEnabled = enable;
+    std::lock_guard<std::mutex> lock(mEventsMutex);
+    mCollectionEnabled = enable;
 }
 
 void SensorsHidlEnvironmentBase::addEvent(const Event& ev) {
-    std::lock_guard<std::mutex> lock(events_mutex);
-    if (collectionEnabled) {
-        events.push_back(ev);
+    std::lock_guard<std::mutex> lock(mEventsMutex);
+    if (mCollectionEnabled) {
+        mEvents.push_back(ev);
+    }
+
+    if (mCallback != nullptr) {
+        mCallback->onEvent(ev);
     }
 }
+
+void SensorsHidlEnvironmentBase::registerCallback(IEventCallback* callback) {
+    std::lock_guard<std::mutex> lock(mEventsMutex);
+    mCallback = callback;
+}
+
+void SensorsHidlEnvironmentBase::unregisterCallback() {
+    std::lock_guard<std::mutex> lock(mEventsMutex);
+    mCallback = nullptr;
+}
\ No newline at end of file
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
index 96e6085..6499fba 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlEnvironmentBase.h
@@ -27,6 +27,12 @@
 #include <thread>
 #include <vector>
 
+class IEventCallback {
+   public:
+    virtual ~IEventCallback() = default;
+    virtual void onEvent(const ::android::hardware::sensors::V1_0::Event& event) = 0;
+};
+
 class SensorsHidlEnvironmentBase : public ::testing::VtsHalHidlTargetTestEnvBase {
    public:
     using Event = ::android::hardware::sensors::V1_0::Event;
@@ -40,19 +46,24 @@
     // set sensor event collection status
     void setCollection(bool enable);
 
+    void registerCallback(IEventCallback* callback);
+    void unregisterCallback();
+
    protected:
-    SensorsHidlEnvironmentBase() {}
+    SensorsHidlEnvironmentBase() : mCollectionEnabled(false), mCallback(nullptr) {}
 
     void addEvent(const Event& ev);
 
     virtual void startPollingThread() = 0;
     virtual bool resetHal() = 0;
 
-    bool collectionEnabled;
-    std::atomic_bool stopThread;
-    std::thread pollThread;
-    std::vector<Event> events;
-    std::mutex events_mutex;
+    bool mCollectionEnabled;
+    std::atomic_bool mStopThread;
+    std::thread mPollThread;
+    std::vector<Event> mEvents;
+    std::mutex mEventsMutex;
+
+    IEventCallback* mCallback;
 
     GTEST_DISALLOW_COPY_AND_ASSIGN_(SensorsHidlEnvironmentBase);
 };
diff --git a/tests/expression/1.0/IExpression.hal b/tests/expression/1.0/IExpression.hal
index 2db9f61..36aed60 100644
--- a/tests/expression/1.0/IExpression.hal
+++ b/tests/expression/1.0/IExpression.hal
@@ -147,6 +147,28 @@
     logand4 = (0 && 1) == 0,
   };
 
+  // Tests for enum tags
+  enum NoElements : uint32_t {};
+  enum OneElement : uint32_t {A};
+  enum TwoElement : uint32_t {A,B};
+  enum TwoCollidingElements : uint32_t {A=1,B=1};
+  enum ThreeFromInheritance : TwoElement {C};
+  enum ThreeFromDoubleInheritance : ThreeFromInheritance {};
+  enum ThreeCollidingFromInheritance : TwoCollidingElements {C};
+
+  enum EnumTagTest : uint32_t {
+    a = NoElements#len == 0,
+    b = OneElement#len == 1,
+    c = TwoElement#len == 2,
+    d = TwoCollidingElements#len == 2,
+    e = ThreeFromInheritance#len == 3,
+    f = ThreeFromDoubleInheritance#len == 3,
+    g = ThreeCollidingFromInheritance#len == 3,
+
+    // fine to reference current enum as well
+    h = EnumTagTest#len == 8,
+  };
+
   enum Grayscale : int8_t {
     WHITE = 126,
     GRAY, // 127