Merge "CAN Configurator Service"
diff --git a/audio/6.0/IStreamOut.hal b/audio/6.0/IStreamOut.hal
index 941ba61..13a86ec 100644
--- a/audio/6.0/IStreamOut.hal
+++ b/audio/6.0/IStreamOut.hal
@@ -276,4 +276,90 @@
      */
     selectPresentation(int32_t presentationId, int32_t programId)
             generates (Result retval);
+
+    /**
+     * Returns the Dual Mono mode presentation setting.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return mode current setting of Dual Mono mode.
+     */
+    getDualMonoMode() generates (Result retval, DualMonoMode mode);
+
+    /**
+     * Sets the Dual Mono mode presentation on the output device.
+     *
+     * The Dual Mono mode is generally applied to stereo audio streams
+     * where the left and right channels come from separate sources.
+     *
+     * Optional method
+     *
+     * @param mode selected Dual Mono mode.
+     * @return retval operation completion status.
+     */
+    setDualMonoMode(DualMonoMode mode) generates (Result retval);
+
+    /**
+     * Returns the Audio Description Mix level in dB.
+     *
+     * The level is applied to streams incorporating a secondary Audio
+     * Description stream. It specifies the relative level of mixing for
+     * the Audio Description with a reference to the Main Audio.
+     *
+     * Optional method
+     *
+     * The value of the relative level is in the range from negative infinity
+     * to +48.
+     *
+     * @return retval operation completion status.
+     * @return leveldB the current Audio Description Mix Level in dB.
+     */
+    getAudioDescriptionMixLevel() generates (Result retval, float leveldB);
+
+    /**
+     * Sets the Audio Description Mix level in dB.
+     *
+     * For streams incorporating a secondary Audio Description stream
+     * the relative level of mixing of the Audio Description to the Main Audio
+     * is controlled by this method.
+     *
+     * Optional method
+     *
+     * The value of the relative level must be in the range from negative
+     * infinity to +48.
+     *
+     * @param leveldB Audio Description Mix Level in dB
+     * @return retval operation completion status.
+     */
+    setAudioDescriptionMixLevel(float leveldB) generates (Result retval);
+
+    /**
+     * Retrieves current playback rate parameters.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return playbackRate current playback parameters
+     */
+    getPlaybackRateParameters()
+            generates (Result retval, PlaybackRate playbackRate);
+
+    /**
+     * Sets the playback rate parameters that control playback behavior.
+     * This is normally used when playing encoded content and decoding
+     * is performed in hardware. Otherwise, the framework can apply
+     * necessary transformations.
+     *
+     * Optional method
+     *
+     * If the HAL supports setting the playback rate, it is recommended
+     * to support speed and pitch values at least in the range
+     * from 0.5f to 2.0f, inclusive (see the definition of PlaybackRate struct).
+     *
+     * @param playbackRate playback parameters
+     * @return retval operation completion status.
+     */
+    setPlaybackRateParameters(PlaybackRate playbackRate)
+            generates (Result retval);
 };
diff --git a/audio/6.0/types.hal b/audio/6.0/types.hal
index 1a704f8..8ff618e 100644
--- a/audio/6.0/types.hal
+++ b/audio/6.0/types.hal
@@ -247,3 +247,111 @@
      */
     EXTERNAL = 3,
 };
+
+
+/* Dual Mono handling is used when a stereo audio stream
+ * contains separate audio content on the left and right channels.
+ * Such information about the content of the stream may be found, for example,
+ * in ITU T-REC-J.94-201610 A.6.2.3 Component descriptor.
+ */
+@export(name="audio_dual_mono_mode_t", value_prefix="AUDIO_DUAL_MONO_MODE_")
+enum DualMonoMode : int32_t {
+    // Need to be in sync with DUAL_MONO_MODE* constants in
+    // frameworks/base/media/java/android/media/AudioTrack.java
+    /**
+     * Disable any Dual Mono presentation effect.
+     *
+     */
+    OFF = 0,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left and right audio channels blended together
+     * and delivered to both channels.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that the left-right stereo symmetric
+     * channels are pairwise blended, the other channels such as center
+     * are left alone.
+     */
+    LR = 1,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left audio channel replicated into the right audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the left channel position replicated
+     * into the right channel position. The center channels (with no
+     * left/right symmetry) or unbalanced channels are left alone.
+     */
+    LL = 2,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the right audio channel replicated into the left audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the right channel position replicated
+     * into the left channel position. The center channels (with no
+     * left/right symmetry) or unbalanced channels are left alone.
+     */
+    RR = 3,
+};
+
+/**
+ * Algorithms used for timestretching (preserving pitch while playing audio
+ * content at different speed).
+ */
+@export(name="audio_timestretch_stretch_mode_t", value_prefix="AUDIO_TIMESTRETCH_STRETCH_")
+enum TimestretchMode : int32_t {
+    // Need to be in sync with AUDIO_STRETCH_MODE_* constants in
+    // frameworks/base/media/java/android/media/PlaybackParams.java
+    DEFAULT = 0,
+    /** Selects timestretch algorithm best suitable for voice (speech) content. */
+    VOICE = 1,
+};
+
+/**
+ * Behavior when the values for speed and / or pitch are out
+ * of applicable range.
+ */
+@export(name="audio_timestretch_fallback_mode_t", value_prefix="AUDIO_TIMESTRETCH_FALLBACK_")
+enum TimestretchFallbackMode : int32_t {
+    // Need to be in sync with AUDIO_FALLBACK_MODE_* constants in
+    // frameworks/base/media/java/android/media/PlaybackParams.java
+    /** Play silence for parameter values that are out of range. */
+    MUTE = 1,
+    /** Return an error while trying to set the parameters. */
+    FAIL = 2,
+};
+
+/**
+ * Parameters determining playback behavior. They are used to speed up or
+ * slow down playback and / or change the tonal frequency of the audio content
+ * (pitch).
+ */
+struct PlaybackRate {
+    /**
+     * Speed factor (multiplier). Normal speed has the value of 1.0f.
+     * Values less than 1.0f slow down playback, value greater than 1.0f
+     * speed it up.
+     */
+    float speed;
+    /**
+     * Pitch factor (multiplier). Setting pitch value to 1.0f together
+     * with changing playback speed preserves the pitch, this is often
+     * called "timestretching." Setting the pitch value equal to speed produces
+     * the same effect as playing audio content at different sampling rate.
+     */
+    float pitch;
+    /**
+     * Selects the algorithm used for timestretching (preserving pitch while
+     * playing audio at different speed).
+     */
+    TimestretchMode timestretchMode;
+    /**
+     * Selects the behavior when the specified values for speed and / or pitch
+     * are out of applicable range.
+     */
+    TimestretchFallbackMode fallbackMode;
+};
diff --git a/audio/common/6.0/types.hal b/audio/common/6.0/types.hal
index b2806c7..67217ab 100644
--- a/audio/common/6.0/types.hal
+++ b/audio/common/6.0/types.hal
@@ -903,6 +903,25 @@
     SONIFICATION = 4,
 };
 
+/** Encapsulation mode used for sending audio compressed data. */
+@export(name="audio_encapsulation_mode_t", value_prefix="AUDIO_ENCAPSULATION_MODE_")
+enum AudioEncapsulationMode : int32_t {
+    // Do not change these values without updating their counterparts
+    // in frameworks/base/media/java/android/media/AudioTrack.java
+    /**
+     * No encapsulation mode for metadata.
+     */
+    NONE              = 0,
+    /**
+     * Elementary stream payload with metadata
+     */
+    ELEMENTARY_STREAM = 1,
+    /**
+     *  Handle-based payload with metadata
+     */
+    HANDLE            = 2,
+};
+
 /**
  * Additional information about the stream passed to hardware decoders.
  */
@@ -918,6 +937,9 @@
     uint32_t bitWidth;
     uint32_t bufferSize;
     AudioUsage usage;
+    AudioEncapsulationMode encapsulationMode;
+    int32_t contentId;
+    int32_t syncId;
 };
 
 /**
diff --git a/audio/common/all-versions/default/HidlUtils.cpp b/audio/common/all-versions/default/HidlUtils.cpp
index 08002c8..a470c9c 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -28,12 +28,13 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-void HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config) {
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config) {
     config->sampleRateHz = halConfig.sample_rate;
     config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
     config->format = AudioFormat(halConfig.format);
-    audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo);
+    status_t status = audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo);
     config->frameCount = halConfig.frame_count;
+    return status;
 }
 
 void HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
@@ -106,8 +107,8 @@
     return static_cast<audio_usage_t>(usage);
 }
 
-void HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
-                                        AudioOffloadInfo* offload) {
+status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+                                            AudioOffloadInfo* offload) {
     offload->sampleRateHz = halOffload.sample_rate;
     offload->channelMask = EnumBitfield<AudioChannelMask>(halOffload.channel_mask);
     offload->format = AudioFormat(halOffload.format);
@@ -119,6 +120,26 @@
     offload->bitWidth = halOffload.bit_width;
     offload->bufferSize = halOffload.offload_buffer_size;
     offload->usage = audioUsageFromHal(halOffload.usage);
+#if MAJOR_VERSION >= 6
+    if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+        offload->encapsulationMode =
+                static_cast<AudioEncapsulationMode>(halOffload.encapsulation_mode);
+        offload->contentId = halOffload.content_id;
+        offload->syncId = halOffload.sync_id;
+    } else {
+        offload->encapsulationMode = AudioEncapsulationMode::NONE;
+        offload->contentId = 0;
+        offload->syncId = 0;
+    }
+#else
+    // nonzero values here are not compatible with HAL versions below 6.
+    if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2 &&
+        (halOffload.encapsulation_mode != AUDIO_ENCAPSULATION_MODE_NONE ||
+         halOffload.content_id != 0 || halOffload.sync_id != 0)) {
+        return BAD_VALUE;
+    }
+#endif
+    return OK;
 }
 
 void HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
@@ -135,6 +156,14 @@
     halOffload->bit_width = offload.bitWidth;
     halOffload->offload_buffer_size = offload.bufferSize;
     halOffload->usage = audioUsageToHal(offload.usage);
+#if MAJOR_VERSION >= 6
+    halOffload->encapsulation_mode =
+            static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
+    halOffload->content_id = offload.contentId;
+    halOffload->sync_id = offload.syncId;
+#else
+    // offload doesn't contain encapsulationMode, contentId, syncId, so this is OK.
+#endif
 }
 
 void HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index 758a7f4..ef6dee3 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -35,8 +35,11 @@
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 
 class HidlUtils {
-   public:
-    static void audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
+  public:
+    // A failure here indicates a platform config that is incompatible with
+    // the compiled HIDL interface version.
+    static status_t audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
+
     static void audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
     static void audioGainConfigFromHal(const struct audio_gain_config& halConfig,
                                        AudioGainConfig* config);
@@ -46,8 +49,10 @@
     static void audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
     static AudioUsage audioUsageFromHal(const audio_usage_t halUsage);
     static audio_usage_t audioUsageToHal(const AudioUsage usage);
-    static void audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
-                                        AudioOffloadInfo* offload);
+    // A failure here indicates a platform offload info that is incompatible with
+    // the compiled HIDL interface version.
+    static status_t audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+                                            AudioOffloadInfo* offload);
     static void audioOffloadInfoToHal(const AudioOffloadInfo& offload,
                                       audio_offload_info_t* halOffload);
     static void audioPortConfigFromHal(const struct audio_port_config& halConfig,
@@ -58,7 +63,7 @@
                                         const struct audio_port_config* halConfigs,
                                         hidl_vec<AudioPortConfig>* configs);
     static std::unique_ptr<audio_port_config[]> audioPortConfigsToHal(
-        const hidl_vec<AudioPortConfig>& configs);
+            const hidl_vec<AudioPortConfig>& configs);
     static void audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
     static void audioPortToHal(const AudioPort& port, struct audio_port* halPort);
     static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 47e31c1..6260ba1 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -171,7 +171,8 @@
         streamOut = new StreamOut(this, halStream);
         ++mOpenedStreamsCount;
     }
-    HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
     return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
 }
 
@@ -198,7 +199,8 @@
         streamIn = new StreamIn(this, halStream);
         ++mOpenedStreamsCount;
     }
-    HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
     return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
 }
 
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index 1a2a764..7a4d72b 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -582,6 +582,38 @@
 }
 #endif
 
+#if MAJOR_VERSION >= 6
+Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, DualMonoMode::OFF);
+    return Void();
+}
+
+Return<Result> StreamOut::setDualMonoMode(DualMonoMode /*mode*/) {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED, -std::numeric_limits<float>::infinity());
+    return Void();
+}
+
+Return<Result> StreamOut::setAudioDescriptionMixLevel(float /*leveldB*/) {
+    return Result::NOT_SUPPORTED;
+}
+
+Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
+    _hidl_cb(Result::NOT_SUPPORTED,
+             // Same as AUDIO_PLAYBACK_RATE_INITIALIZER
+             PlaybackRate{1.0f, 1.0f, TimestretchMode::DEFAULT, TimestretchFallbackMode::FAIL});
+    return Void();
+}
+
+Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate& /*playbackRate*/) {
+    return Result::NOT_SUPPORTED;
+}
+
+#endif
+
 }  // namespace implementation
 }  // namespace CPP_VERSION
 }  // namespace audio
diff --git a/audio/core/all-versions/default/include/core/default/StreamOut.h b/audio/core/all-versions/default/include/core/default/StreamOut.h
index 6334785..5db8626 100644
--- a/audio/core/all-versions/default/include/core/default/StreamOut.h
+++ b/audio/core/all-versions/default/include/core/default/StreamOut.h
@@ -121,6 +121,14 @@
     Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
     Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
 #endif
+#if MAJOR_VERSION >= 6
+    Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
+    Return<Result> setDualMonoMode(DualMonoMode mode) override;
+    Return<void> getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) override;
+    Return<Result> setAudioDescriptionMixLevel(float leveldB) override;
+    Return<void> getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) override;
+    Return<Result> setPlaybackRateParameters(const PlaybackRate& playbackRate) override;
+#endif
 
     static Result getPresentationPositionImpl(audio_stream_out_t* stream, uint64_t* frames,
                                               TimeSpec* timeStamp);
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index 09ef330..9e2a050 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -190,3 +190,64 @@
             hidl_vec<AudioPortConfig>(), hidl_vec<AudioPortConfig>(), returnIn(res, ignored)));
     ASSERT_RESULT(Result::INVALID_ARGUMENTS, res);
 }
+
+using DualMonoModeAccessorHidlTest = AccessorHidlTest<DualMonoMode, OutputStreamTest>;
+TEST_P(DualMonoModeAccessorHidlTest, DualMonoModeTest) {
+    doc::test("Check that dual mono mode can be set and retrieved");
+    testAccessors<OPTIONAL>(&OutputStreamTest::getStream, "dual mono mode",
+                            Initial{DualMonoMode::OFF},
+                            {DualMonoMode::LR, DualMonoMode::LL, DualMonoMode::RR},
+                            &IStreamOut::setDualMonoMode, &IStreamOut::getDualMonoMode);
+}
+
+INSTANTIATE_TEST_CASE_P(DualMonoModeHidl, DualMonoModeAccessorHidlTest,
+                        ::testing::ValuesIn(getOutputDeviceConfigParameters()),
+                        &DeviceConfigParameterToString);
+
+using AudioDescriptionMixLevelHidlTest = AccessorHidlTest<float, OutputStreamTest>;
+TEST_P(AudioDescriptionMixLevelHidlTest, AudioDescriptionMixLevelTest) {
+    doc::test("Check that audio description mix level can be set and retrieved");
+    testAccessors<OPTIONAL>(
+            &OutputStreamTest::getStream, "audio description mix level",
+            Initial{-std::numeric_limits<float>::infinity()}, {-48.0f, -1.0f, 0.0f, 1.0f, 48.0f},
+            &IStreamOut::setAudioDescriptionMixLevel, &IStreamOut::getAudioDescriptionMixLevel,
+            {48.5f, 1000.0f, std::numeric_limits<float>::infinity()});
+}
+
+INSTANTIATE_TEST_CASE_P(AudioDescriptionMixLevelHidl, AudioDescriptionMixLevelHidlTest,
+                        ::testing::ValuesIn(getOutputDeviceConfigParameters()),
+                        &DeviceConfigParameterToString);
+
+using PlaybackRateParametersHidlTest = AccessorHidlTest<PlaybackRate, OutputStreamTest>;
+TEST_P(PlaybackRateParametersHidlTest, PlaybackRateParametersTest) {
+    doc::test("Check that playback rate parameters can be set and retrieved");
+    testAccessors<OPTIONAL>(
+            &OutputStreamTest::getStream, "playback rate parameters",
+            Initial{PlaybackRate{1.0f, 1.0f, TimestretchMode::DEFAULT,
+                                 TimestretchFallbackMode::FAIL}},
+            {// Speed and pitch values in the range from 0.5f to 2.0f must be supported
+             // (see the definition of IStreamOut::setPlaybackRateParameters).
+             PlaybackRate{1.0f, 1.0f, TimestretchMode::DEFAULT, TimestretchFallbackMode::MUTE},
+             PlaybackRate{2.0f, 2.0f, TimestretchMode::DEFAULT, TimestretchFallbackMode::MUTE},
+             PlaybackRate{0.5f, 0.5f, TimestretchMode::DEFAULT, TimestretchFallbackMode::MUTE},
+             // Gross speed / pitch values must not be rejected if the fallback mode is "mute"
+             PlaybackRate{1000.0f, 1000.0f, TimestretchMode::DEFAULT,
+                          TimestretchFallbackMode::MUTE},
+             // Default speed / pitch values must not be rejected in "fail" fallback mode
+             PlaybackRate{1.0f, 1.0f, TimestretchMode::DEFAULT, TimestretchFallbackMode::FAIL},
+             // Same for "voice" mode
+             PlaybackRate{1.0f, 1.0f, TimestretchMode::VOICE, TimestretchFallbackMode::MUTE},
+             PlaybackRate{2.0f, 2.0f, TimestretchMode::VOICE, TimestretchFallbackMode::MUTE},
+             PlaybackRate{0.5f, 0.5f, TimestretchMode::VOICE, TimestretchFallbackMode::MUTE},
+             PlaybackRate{1000.0f, 1000.0f, TimestretchMode::VOICE, TimestretchFallbackMode::MUTE},
+             PlaybackRate{1.0f, 1.0f, TimestretchMode::VOICE, TimestretchFallbackMode::FAIL}},
+            &IStreamOut::setPlaybackRateParameters, &IStreamOut::getPlaybackRateParameters,
+            {PlaybackRate{1000.0f, 1000.0f, TimestretchMode::DEFAULT,
+                          TimestretchFallbackMode::FAIL},
+             PlaybackRate{1000.0f, 1000.0f, TimestretchMode::VOICE,
+                          TimestretchFallbackMode::FAIL}});
+}
+
+INSTANTIATE_TEST_CASE_P(PlaybackRateParametersHidl, PlaybackRateParametersHidlTest,
+                        ::testing::ValuesIn(getOutputDeviceConfigParameters()),
+                        &DeviceConfigParameterToString);
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index d0d39e8..b77aec9 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -140,6 +140,11 @@
 class HidlTest : public HidlTestBase {
   public:
     virtual ~HidlTest() = default;
+    // public access to avoid annoyances when using this method in template classes
+    // derived from test classes
+    sp<IDevice> getDevice() const {
+        return DeviceManager::getInstance().get(getFactoryName(), getDeviceName());
+    }
 
   protected:
     // Factory and device name getters to be overridden in subclasses.
@@ -149,9 +154,6 @@
     sp<IDevicesFactory> getDevicesFactory() const {
         return DevicesFactoryManager::getInstance().get(getFactoryName());
     }
-    sp<IDevice> getDevice() const {
-        return DeviceManager::getInstance().get(getFactoryName(), getDeviceName());
-    }
     bool resetDevice() const {
         return DeviceManager::getInstance().reset(getFactoryName(), getDeviceName());
     }
@@ -419,7 +421,8 @@
         ASSERT_TRUE(getDevice() != nullptr);
     }
 
-  protected:
+    // public access to avoid annoyances when using this method in template classes
+    // derived from test classes
     sp<IPrimaryDevice> getDevice() const {
         return DeviceManager::getInstance().getPrimary(getFactoryName());
     }
@@ -450,15 +453,15 @@
     /** Test a property getter and setter.
      *  The getter and/or the setter may return NOT_SUPPORTED if optionality == OPTIONAL.
      */
-    template <Optionality optionality = REQUIRED, class Getter, class Setter>
-    void testAccessors(const string& propertyName, const Initial expectedInitial,
-                       list<Property> valuesToTest, Setter setter, Getter getter,
-                       const vector<Property>& invalidValues = {}) {
+    template <Optionality optionality = REQUIRED, class IUTGetter, class Getter, class Setter>
+    void testAccessors(IUTGetter iutGetter, const string& propertyName,
+                       const Initial expectedInitial, list<Property> valuesToTest, Setter setter,
+                       Getter getter, const vector<Property>& invalidValues = {}) {
         const auto expectedResults = {Result::OK,
                                       optionality == OPTIONAL ? Result::NOT_SUPPORTED : Result::OK};
 
         Property initialValue = expectedInitial.value;
-        ASSERT_OK((BaseTestClass::getDevice().get()->*getter)(returnIn(res, initialValue)));
+        ASSERT_OK(((this->*iutGetter)().get()->*getter)(returnIn(res, initialValue)));
         ASSERT_RESULT(expectedResults, res);
         if (res == Result::OK && expectedInitial.check == REQUIRED) {
             EXPECT_EQ(expectedInitial.value, initialValue);
@@ -469,7 +472,7 @@
         for (Property setValue : valuesToTest) {
             SCOPED_TRACE("Test " + propertyName + " getter and setter for " +
                          testing::PrintToString(setValue));
-            auto ret = (BaseTestClass::getDevice().get()->*setter)(setValue);
+            auto ret = ((this->*iutGetter)().get()->*setter)(setValue);
             ASSERT_RESULT(expectedResults, ret);
             if (ret == Result::NOT_SUPPORTED) {
                 doc::partialTest(propertyName + " setter is not supported");
@@ -477,7 +480,7 @@
             }
             Property getValue;
             // Make sure the getter returns the same value just set
-            ASSERT_OK((BaseTestClass::getDevice().get()->*getter)(returnIn(res, getValue)));
+            ASSERT_OK(((this->*iutGetter)().get()->*getter)(returnIn(res, getValue)));
             ASSERT_RESULT(expectedResults, res);
             if (res == Result::NOT_SUPPORTED) {
                 doc::partialTest(propertyName + " getter is not supported");
@@ -490,11 +493,18 @@
             SCOPED_TRACE("Try to set " + propertyName + " with the invalid value " +
                          testing::PrintToString(invalidValue));
             EXPECT_RESULT(invalidArgsOrNotSupported,
-                          (BaseTestClass::getDevice().get()->*setter)(invalidValue));
+                          ((this->*iutGetter)().get()->*setter)(invalidValue));
         }
 
         // Restore initial value
-        EXPECT_RESULT(expectedResults, (BaseTestClass::getDevice().get()->*setter)(initialValue));
+        EXPECT_RESULT(expectedResults, ((this->*iutGetter)().get()->*setter)(initialValue));
+    }
+    template <Optionality optionality = REQUIRED, class Getter, class Setter>
+    void testAccessors(const string& propertyName, const Initial expectedInitial,
+                       list<Property> valuesToTest, Setter setter, Getter getter,
+                       const vector<Property>& invalidValues = {}) {
+        testAccessors<optionality>(&BaseTestClass::getDevice, propertyName, expectedInitial,
+                                   valuesToTest, setter, getter, invalidValues);
     }
 };
 
@@ -872,6 +882,11 @@
 
 template <class Stream>
 class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter {
+  public:
+    // public access to avoid annoyances when using this method in template classes
+    // derived from test classes
+    sp<Stream> getStream() const { return stream; }
+
   protected:
     OpenStreamTest() : AudioHidlTestWithDeviceConfigParameter(), helper(stream) {}
     template <class Open>
diff --git a/automotive/can/1.0/default/CanBus.cpp b/automotive/can/1.0/default/CanBus.cpp
index 9f704c1..8b98e5e 100644
--- a/automotive/can/1.0/default/CanBus.cpp
+++ b/automotive/can/1.0/default/CanBus.cpp
@@ -226,7 +226,6 @@
  * \param flag bool object from CanMessage object
  */
 static bool satisfiesFilterFlag(FilterFlag filterFlag, bool flag) {
-    // TODO(b/144458917) add testing for this to VTS tests
     if (filterFlag == FilterFlag::DONT_CARE) return true;
     if (filterFlag == FilterFlag::SET) return flag;
     if (filterFlag == FilterFlag::NOT_SET) return !flag;
@@ -302,7 +301,6 @@
     if ((frame.can_id & CAN_ERR_FLAG) != 0) {
         // error bit is set
         LOG(WARNING) << "CAN Error frame received";
-        // TODO(b/144458917) consider providing different values for isFatal, depending on error
         notifyErrorListeners(parseErrorFrame(frame), false);
         return;
     }
diff --git a/automotive/can/1.0/default/CanBusSlcan.cpp b/automotive/can/1.0/default/CanBusSlcan.cpp
index d15905d..5005ecd 100644
--- a/automotive/can/1.0/default/CanBusSlcan.cpp
+++ b/automotive/can/1.0/default/CanBusSlcan.cpp
@@ -133,7 +133,7 @@
         return ICanController::Result::UNKNOWN_ERROR;
     }
 
-    // set open flag TODO: also support listen only
+    // TODO(b/144775286): set open flag & support listen only
     if (write(mFd.get(), slcanprotocol::kOpenCommand.c_str(),
               slcanprotocol::kOpenCommand.length()) <= 0) {
         LOG(ERROR) << "Failed to set open flag: " << strerror(errno);
diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp
index 17f31e4..f9bccef 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/1.1/Android.bp
@@ -12,6 +12,8 @@
         "IEvsCameraStream.hal",
         "IEvsDisplay.hal",
         "IEvsEnumerator.hal",
+        "IEvsUltrasonicsArray.hal",
+        "IEvsUltrasonicsArrayStream.hal",
     ],
     interfaces: [
         "android.frameworks.automotive.display@1.0",
diff --git a/automotive/evs/1.1/IEvsEnumerator.hal b/automotive/evs/1.1/IEvsEnumerator.hal
index 84dd21f..d604e4f 100644
--- a/automotive/evs/1.1/IEvsEnumerator.hal
+++ b/automotive/evs/1.1/IEvsEnumerator.hal
@@ -18,12 +18,13 @@
 
 import IEvsCamera;
 import IEvsDisplay;
+import IEvsUltrasonicsArray;
 import @1.0::IEvsEnumerator;
 import @1.0::EvsResult;
 import android.hardware.camera.device@3.2::Stream;
 
 /**
- * Provides the mechanism for EVS camera discovery
+ * Provides the mechanism for EVS camera and ultrasonics array discovery
  */
 interface IEvsEnumerator extends @1.0::IEvsEnumerator {
     /**
@@ -76,4 +77,33 @@
      * @return display EvsDisplay object to be used.
      */
     openDisplay_1_1(uint8_t id) generates (IEvsDisplay display);
+
+    /**
+     * Returns a list of all ultrasonics array available to the system.
+     * Will return an empty vector if ultrasonics is not supported.
+     *
+     * @return ultrasonicsArrays A list of ultrasonics available for EVS service.
+     */
+    getUltrasonicsArrayList() generates (vec<UltrasonicsArrayDesc> ultrasonicsArrays);
+
+    /**
+     * Gets the IEvsUltrasonicsArray associated with a ultrasonicsArrayId from a
+     * UltrasonicsDataDesc
+     *
+     * @param  ultrasonicsArrayId  A unique identifier of the ultrasonic array.
+     * @return evsUltrasonicsArray IEvsUltrasonicsArray object associated with a
+     *                             given ultrasonicsArrayId.
+     */
+    openUltrasonicsArray(string ultrasonicsArrayId) generates (
+            IEvsUltrasonicsArray evsUltrasonicsArray);
+
+    /**
+     * Return the specified IEvsUltrasonicsArray interface as no longer in use
+     *
+     * When the IEvsUltrasonicsArray object is no longer required, it must be released.
+     * NOTE: Data streaming must be cleanly stopped before making this call.
+     *
+     * @param  evsUltrasonicsArray EvsUltrasonics array object to be closed.
+     */
+    closeUltrasonicsArray(IEvsUltrasonicsArray evsUltrasonicsArray);
 };
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArray.hal b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
new file mode 100644
index 0000000..ae4f941
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArray.hal
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import @1.0::EvsResult;
+import UltrasonicsArrayDesc;
+import UltrasonicsDataFrameDesc;
+import IEvsUltrasonicsArrayStream;
+
+/**
+ * HAL interface for ultrasonics sensor array.
+ */
+interface IEvsUltrasonicsArray {
+   /**
+    * Returns the ultrasonic sensor array information.
+    *
+    * @return  info  The description of this ultrasonic array. This must be the
+    *                same value as reported by IEvsEnumerator::getUltrasonicsArrayList().
+    */
+   getUltrasonicArrayInfo() generates (UltrasonicsArrayDesc info);
+
+   /**
+    * Specifies the depth of the buffer chain the ultrasonic sensors is
+    * asked to support.
+    *
+    * Up to this many data frames may be held concurrently by the client of IEvsUltrasonicsArray.
+    * If this many frames have been delivered to the receiver without being returned
+    * by doneWithFrame, the stream must skip frames until a buffer is returned for reuse.
+    * It is legal for this call to come at any time, even while streams are already running,
+    * in which case buffers should be added or removed from the chain as appropriate.
+    * If no call is made to this entry point, the IEvsUltrasonicsArray must support at least one
+    * data frame by default. More is acceptable.
+    *
+    * @param  bufferCount Number of buffers the client of
+    *                     IEvsUltrasonicsArray may hold concurrently.
+    * @return result      EvsResult::OK is returned if this call is successful.
+    *                     Will return EvsResult::INVALID_ARG on invalid bufferCount.
+    */
+   setMaxFramesInFlight(uint32_t bufferCount) generates (EvsResult result);
+
+   /**
+    * Requests to start the stream.
+    *
+    * @param  stream Implementation of IEvsUltrasonicsArrayStream.
+    * @return result EvsResult::OK is returned if this call is successful. Returns
+    *                EvsResult::STREAM_ALREADY_RUNNING if stream is already running.
+    */
+   startStream(IEvsUltrasonicsArrayStream stream) generates (EvsResult result);
+
+   /**
+    * Requests to stop the delivery of the ultrasonic array data frames.
+    *
+    * Because delivery is asynchronous, frames may continue to arrive for
+    * some time after this call returns. Each must be returned until the
+    * closure of the stream is signaled to the IEvsCameraStream.
+    * This function cannot fail and is ignored if the stream isn't running.
+    */
+   stopStream();
+
+   /**
+    * Notifies the UltrasonicsDataDesc is consumed that was received from
+    * IEvsUltrasonicsArrayStream.
+    *
+    * @param  dataFrameDesc Ultrasonics data descriptor.
+    */
+    doneWithDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+};
diff --git a/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
new file mode 100644
index 0000000..f95209f
--- /dev/null
+++ b/automotive/evs/1.1/IEvsUltrasonicsArrayStream.hal
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.evs@1.1;
+
+import UltrasonicsDataFrameDesc;
+import @1.1::EvsEventDesc;
+
+/**
+ * Implemented on client side to receive asynchronous ultrasonic data
+ * deliveries.
+ */
+interface IEvsUltrasonicsArrayStream {
+   /**
+    * Receives calls from the HAL each time a data frame is ready.
+    *
+    * @param dataFrameDesc Ultrasonic array data frame descriptor.
+    */
+    oneway deliverDataFrame(UltrasonicsDataFrameDesc dataFrameDesc);
+
+    /**
+     * Receives calls from the HAL each time an event happens.
+     *
+     * @param event Event EVS event with possible event information.
+     */
+    oneway notify(EvsEventDesc event);
+};
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index a35c9db..d843167 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -10,6 +10,7 @@
         "EvsDisplay.cpp",
         "ConfigManager.cpp",
         "ConfigManagerUtil.cpp",
+        "EvsUltrasonicsArray.cpp",
     ],
     init_rc: ["android.hardware.automotive.evs@1.1-service.rc"],
 
@@ -17,11 +18,14 @@
         "android.hardware.automotive.evs@1.0",
         "android.hardware.automotive.evs@1.1",
         "android.hardware.camera.device@3.3",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
         "libbase",
         "libbinder",
         "liblog",
         "libhardware",
         "libhidlbase",
+        "libhidlmemory",
         "liblog",
         "libui",
         "libutils",
diff --git a/automotive/evs/1.1/default/EvsEnumerator.cpp b/automotive/evs/1.1/default/EvsEnumerator.cpp
index 0319560..117ee7a 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.1/default/EvsEnumerator.cpp
@@ -19,6 +19,7 @@
 #include "EvsEnumerator.h"
 #include "EvsCamera.h"
 #include "EvsDisplay.h"
+#include "EvsUltrasonicsArray.h"
 
 namespace android {
 namespace hardware {
@@ -31,12 +32,12 @@
 // NOTE:  All members values are static so that all clients operate on the same state
 //        That is to say, this is effectively a singleton despite the fact that HIDL
 //        constructs a new instance for each client.
-std::list<EvsEnumerator::CameraRecord>   EvsEnumerator::sCameraList;
-wp<EvsDisplay>                           EvsEnumerator::sActiveDisplay;
-unique_ptr<ConfigManager>                EvsEnumerator::sConfigManager;
-sp<IAutomotiveDisplayProxyService>       EvsEnumerator::sDisplayProxyService;
-std::unordered_map<uint8_t, uint64_t>    EvsEnumerator::sDisplayPortList;
-
+std::list<EvsEnumerator::CameraRecord>              EvsEnumerator::sCameraList;
+wp<EvsDisplay>                                      EvsEnumerator::sActiveDisplay;
+unique_ptr<ConfigManager>                           EvsEnumerator::sConfigManager;
+sp<IAutomotiveDisplayProxyService>                  EvsEnumerator::sDisplayProxyService;
+std::unordered_map<uint8_t, uint64_t>               EvsEnumerator::sDisplayPortList;
+std::list<EvsEnumerator::UltrasonicsArrayRecord>    EvsEnumerator::sUltrasonicsArrayRecordList;
 
 EvsEnumerator::EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService) {
     ALOGD("EvsEnumerator created");
@@ -66,6 +67,10 @@
             }
         });
     }
+
+    // Add ultrasonics array desc.
+    sUltrasonicsArrayRecordList.emplace_back(
+            EvsUltrasonicsArray::GetDummyArrayDesc("front_array"));
 }
 
 
@@ -355,6 +360,102 @@
     return pRecord;
 }
 
+EvsEnumerator::UltrasonicsArrayRecord* EvsEnumerator::findUltrasonicsArrayById(
+        const std::string& ultrasonicsArrayId) {
+    auto recordIt = std::find_if(
+            sUltrasonicsArrayRecordList.begin(), sUltrasonicsArrayRecordList.end(),
+                    [&ultrasonicsArrayId](const UltrasonicsArrayRecord& record) {
+                            return ultrasonicsArrayId == record.desc.ultrasonicsArrayId;});
+
+    return (recordIt != sUltrasonicsArrayRecordList.end()) ? &*recordIt : nullptr;
+}
+
+Return<void> EvsEnumerator::getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) {
+    hidl_vec<UltrasonicsArrayDesc> desc;
+    desc.resize(sUltrasonicsArrayRecordList.size());
+
+    // Copy over desc from sUltrasonicsArrayRecordList.
+    for (auto p = std::make_pair(sUltrasonicsArrayRecordList.begin(), desc.begin());
+            p.first != sUltrasonicsArrayRecordList.end(); p.first++, p.second++) {
+        *p.second = p.first->desc;
+    }
+
+    // Send back the results
+    ALOGD("reporting %zu ultrasonics arrays available", desc.size());
+    _hidl_cb(desc);
+
+    // HIDL convention says we return Void if we sent our result back via callback
+    return Void();
+}
+
+Return<sp<IEvsUltrasonicsArray>> EvsEnumerator::openUltrasonicsArray(
+        const hidl_string& ultrasonicsArrayId) {
+    // Find the named ultrasonic array.
+    UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+
+    // Is this a recognized ultrasonic array id?
+    if (!pRecord) {
+        ALOGE("Requested ultrasonics array %s not found", ultrasonicsArrayId.c_str());
+        return nullptr;
+    }
+
+    // Has this ultrasonic array already been instantiated by another caller?
+    sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+    if (pActiveUltrasonicsArray != nullptr) {
+        ALOGW("Killing previous ultrasonics array because of new caller");
+        closeUltrasonicsArray(pActiveUltrasonicsArray);
+    }
+
+    // Construct a ultrasonic array instance for the caller
+    pActiveUltrasonicsArray = EvsUltrasonicsArray::Create(ultrasonicsArrayId.c_str());
+    pRecord->activeInstance = pActiveUltrasonicsArray;
+    if (pActiveUltrasonicsArray == nullptr) {
+        ALOGE("Failed to allocate new EvsUltrasonicsArray object for %s\n",
+              ultrasonicsArrayId.c_str());
+    }
+
+    return pActiveUltrasonicsArray;
+}
+
+Return<void> EvsEnumerator::closeUltrasonicsArray(
+        const sp<IEvsUltrasonicsArray>& pEvsUltrasonicsArray) {
+
+    if (pEvsUltrasonicsArray.get() == nullptr) {
+        ALOGE("Ignoring call to closeUltrasonicsArray with null ultrasonics array");
+        return Void();
+    }
+
+    // Get the ultrasonics array id so we can find it in our list.
+    std::string ultrasonicsArrayId;
+    pEvsUltrasonicsArray->getUltrasonicArrayInfo([&ultrasonicsArrayId](UltrasonicsArrayDesc desc) {
+        ultrasonicsArrayId.assign(desc.ultrasonicsArrayId);
+    });
+
+    // Find the named ultrasonics array
+    UltrasonicsArrayRecord* pRecord = findUltrasonicsArrayById(ultrasonicsArrayId);
+    if (!pRecord) {
+        ALOGE("Asked to close a ultrasonics array whose name isnt not found");
+        return Void();
+    }
+
+    sp<EvsUltrasonicsArray> pActiveUltrasonicsArray = pRecord->activeInstance.promote();
+
+    if (pActiveUltrasonicsArray.get() == nullptr) {
+        ALOGE("Somehow a ultrasonics array is being destroyed when the enumerator didn't know "
+              "one existed");
+    } else if (pActiveUltrasonicsArray != pEvsUltrasonicsArray) {
+        // This can happen if the ultrasonics array was aggressively reopened,
+        // orphaning this previous instance
+        ALOGW("Ignoring close of previously orphaned ultrasonics array - why did a client steal?");
+    } else {
+        // Drop the active ultrasonics array
+        pActiveUltrasonicsArray->forceShutdown();
+        pRecord->activeInstance = nullptr;
+    }
+
+    return Void();
+}
+
 } // namespace implementation
 } // namespace V1_1
 } // namespace evs
diff --git a/automotive/evs/1.1/default/EvsEnumerator.h b/automotive/evs/1.1/default/EvsEnumerator.h
index 9415953..d80124b 100644
--- a/automotive/evs/1.1/default/EvsEnumerator.h
+++ b/automotive/evs/1.1/default/EvsEnumerator.h
@@ -21,6 +21,7 @@
 #include <android/hardware/automotive/evs/1.1/IEvsCamera.h>
 #include <android/hardware/automotive/evs/1.1/IEvsDisplay.h>
 #include <android/frameworks/automotive/display/1.0/IAutomotiveDisplayProxyService.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
 
 #include <list>
 
@@ -46,6 +47,7 @@
 
 class EvsCamera;    // from EvsCamera.h
 class EvsDisplay;   // from EvsDisplay.h
+class EvsUltrasonicsArray;  // from EvsUltrasonicsArray.h
 
 
 class EvsEnumerator : public IEvsEnumerator {
@@ -65,6 +67,11 @@
     Return<bool> isHardware() override { return true; }
     Return<void>                getDisplayIdList(getDisplayIdList_cb _list_cb) override;
     Return<sp<IEvsDisplay_1_1>> openDisplay_1_1(uint8_t port) override;
+    Return<void> getUltrasonicsArrayList(getUltrasonicsArrayList_cb _hidl_cb) override;
+    Return<sp<IEvsUltrasonicsArray>> openUltrasonicsArray(
+            const hidl_string& ultrasonicsArrayId) override;
+    Return<void> closeUltrasonicsArray(
+            const ::android::sp<IEvsUltrasonicsArray>& evsUltrasonicsArray) override;
 
     // Implementation details
     EvsEnumerator(sp<IAutomotiveDisplayProxyService> windowService = nullptr);
@@ -80,10 +87,21 @@
         CameraRecord(const char *cameraId) : desc() { desc.v1.cameraId = cameraId; }
     };
 
+    struct UltrasonicsArrayRecord {
+        UltrasonicsArrayDesc desc;
+        wp<EvsUltrasonicsArray> activeInstance;
+
+        UltrasonicsArrayRecord(const UltrasonicsArrayDesc& arrayDesc) : desc(arrayDesc) {};
+    };
+
     static CameraRecord* findCameraById(const std::string& cameraId);
 
     static std::list<CameraRecord>   sCameraList;
 
+    static UltrasonicsArrayRecord* findUltrasonicsArrayById(const std::string& ultrasonicsArrayId);
+
+    static std::list<UltrasonicsArrayRecord> sUltrasonicsArrayRecordList;
+
     // Weak pointer. Object destructs if client dies.
     static wp<EvsDisplay>            sActiveDisplay;
 
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
new file mode 100644
index 0000000..bc69aa4
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.cpp
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "EvsUltrasonicsArray.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <log/log.h>
+#include <time.h>
+#include <utils/SystemClock.h>
+#include <utils/Timers.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+// Arbitrary limit on number of data frames allowed to be allocated
+// Safeguards against unreasonable resource consumption and provides a testable limit
+const unsigned int kMaximumDataFramesInFlight = 100;
+
+const uint32_t kMaxReadingsPerSensor = 5;
+const uint32_t kMaxReceiversCount = 3;
+
+const unsigned int kSharedMemoryMaxSize =
+        kMaxReadingsPerSensor * kMaxReceiversCount * 2 * sizeof(float);
+
+// Target frame rate in frames per second.
+const int kTargetFrameRate = 10;
+
+namespace {
+
+void fillDummyArrayDesc(UltrasonicsArrayDesc& arrayDesc) {
+    arrayDesc.maxReadingsPerSensorCount = kMaxReadingsPerSensor;
+    arrayDesc.maxReceiversCount = kMaxReceiversCount;
+
+    const int kSensorCount = 3;
+    const float kMaxRange = 4000;                // 4 metres.
+    const float kAngleOfMeasurement = 0.261799;  // 15 degrees.
+
+    std::vector<UltrasonicSensor> sensors(kSensorCount);
+
+    // Sensor pointing forward on left side of front bumper.
+    sensors[0].maxRange = kMaxRange;
+    sensors[0].angleOfMeasurement = kAngleOfMeasurement;
+    sensors[0].pose = {{1, 0, 0, 0}, {-1000, 2000, 200}};
+
+    // Sensor pointing forward on center of front bumper.
+    sensors[1].maxRange = kMaxRange;
+    sensors[1].angleOfMeasurement = kAngleOfMeasurement;
+    sensors[1].pose = {{1, 0, 0, 0}, {0, 2000, 200}};
+
+    // Sensor pointing forward on right side of front bumper.
+    sensors[2].maxRange = kMaxRange;
+    sensors[2].angleOfMeasurement = kAngleOfMeasurement;
+    sensors[2].pose = {{1, 0, 0, 0}, {1000, 2000, 200}};
+
+    arrayDesc.sensors = sensors;
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+    uint8_t receiverId;
+    std::vector<std::pair<float, float>> readings;
+};
+
+// Serializes data provided in waveformDataList to a shared memory data pointer.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+void SerializeWaveformData(const std::vector<WaveformData>& waveformDataList, uint8_t* pData) {
+    for (auto& waveformData : waveformDataList) {
+        // Set Id
+        memcpy(pData, &waveformData.receiverId, sizeof(uint8_t));
+        pData += sizeof(uint8_t);
+
+        for (auto& reading : waveformData.readings) {
+            // Set the time of flight.
+            memcpy(pData, &reading.first, sizeof(float));
+            pData += sizeof(float);
+
+            // Set the resonance.
+            memcpy(pData, &reading.second, sizeof(float));
+            pData += sizeof(float);
+        }
+    }
+}
+
+// Fills dataFrameDesc with dummy data.
+bool fillDummyDataFrame(UltrasonicsDataFrameDesc& dataFrameDesc, sp<IMemory> pIMemory) {
+    dataFrameDesc.timestampNs = elapsedRealtimeNano();
+
+    const std::vector<uint8_t> transmittersIdList = {0};
+    dataFrameDesc.transmittersIdList = transmittersIdList;
+
+    const std::vector<uint8_t> recvIdList = {0, 1, 2};
+    dataFrameDesc.receiversIdList = recvIdList;
+
+    const std::vector<uint32_t> receiversReadingsCountList = {2, 2, 4};
+    dataFrameDesc.receiversReadingsCountList = receiversReadingsCountList;
+
+    const std::vector<WaveformData> waveformDataList = {
+            {recvIdList[0], { {1000, 0.1f}, {2000, 0.8f} }},
+            {recvIdList[1], { {1000, 0.1f}, {2000, 1.0f} }},
+            {recvIdList[2], { {1000, 0.1f}, {2000, 0.2f}, {4000, 0.2f}, {5000, 0.1f} }}
+    };
+
+    if (pIMemory.get() == nullptr) {
+        return false;
+    }
+
+    uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+
+    pIMemory->update();
+    SerializeWaveformData(waveformDataList, pData);
+    pIMemory->commit();
+
+    return true;
+}
+
+}  // namespace
+
+EvsUltrasonicsArray::EvsUltrasonicsArray(const char* deviceName)
+    : mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED) {
+    LOG(DEBUG) << "EvsUltrasonicsArray instantiated";
+
+    // Set up dummy data for description.
+    mArrayDesc.ultrasonicsArrayId = deviceName;
+    fillDummyArrayDesc(mArrayDesc);
+
+    // Assign allocator.
+    mShmemAllocator = IAllocator::getService("ashmem");
+    if (mShmemAllocator.get() == nullptr) {
+        LOG(ERROR) << "SurroundViewHidlTest getService ashmem failed";
+    }
+}
+
+sp<EvsUltrasonicsArray> EvsUltrasonicsArray::Create(const char* deviceName) {
+    return sp<EvsUltrasonicsArray>(new EvsUltrasonicsArray(deviceName));
+}
+
+EvsUltrasonicsArray::~EvsUltrasonicsArray() {
+    LOG(DEBUG) << "EvsUltrasonicsArray being destroyed";
+    forceShutdown();
+}
+
+// This gets called if another caller "steals" ownership of the ultrasonic array.
+void EvsUltrasonicsArray::forceShutdown() {
+    LOG(DEBUG) << "EvsUltrasonicsArray forceShutdown";
+
+    // Make sure our output stream is cleaned up
+    // (It really should be already)
+    stopStream();
+
+    // Claim the lock while we work on internal state
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    // Drop all the data frames we've been using
+    for (auto&& dataFrame : mDataFrames) {
+        if (dataFrame.inUse) {
+            LOG(ERROR) << "Error - releasing data frame despite remote ownership";
+        }
+        dataFrame.sharedMemory.clear();
+    }
+    mDataFrames.clear();
+
+    // Put this object into an unrecoverable error state since somebody else
+    // is going to own the underlying ultrasonic array now
+    mStreamState = DEAD;
+}
+
+UltrasonicsArrayDesc EvsUltrasonicsArray::GetDummyArrayDesc(const char* deviceName) {
+    UltrasonicsArrayDesc ultrasonicsArrayDesc;
+    ultrasonicsArrayDesc.ultrasonicsArrayId = deviceName;
+    fillDummyArrayDesc(ultrasonicsArrayDesc);
+    return ultrasonicsArrayDesc;
+}
+
+Return<void> EvsUltrasonicsArray::getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) {
+    LOG(DEBUG) << "EvsUltrasonicsArray getUltrasonicsArrayInfo";
+
+    // Return the description for the get info callback.
+    _get_info_cb(mArrayDesc);
+
+    return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::setMaxFramesInFlight(uint32_t bufferCount) {
+    LOG(DEBUG) << "EvsUltrasonicsArray setMaxFramesInFlight";
+
+    // Lock mutex for performing changes to available frames.
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    // We cannot function without at least one buffer to send data.
+    if (bufferCount < 1) {
+        LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested";
+        return EvsResult::INVALID_ARG;
+    }
+
+    // Update our internal state of buffer count.
+    if (setAvailableFrames_Locked(bufferCount)) {
+        return EvsResult::OK;
+    } else {
+        return EvsResult::BUFFER_NOT_AVAILABLE;
+    }
+
+    return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+    LOG(DEBUG) << "EvsUltrasonicsArray doneWithFrame";
+
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    if (dataFrameDesc.dataFrameId >= mDataFrames.size()) {
+        LOG(ERROR) << "ignoring doneWithFrame called with invalid dataFrameId "
+                   << dataFrameDesc.dataFrameId << "(max is " << mDataFrames.size() - 1 << ")";
+        return Void();
+    }
+
+    if (!mDataFrames[dataFrameDesc.dataFrameId].inUse) {
+        LOG(ERROR) << "ignoring doneWithFrame called on frame " << dataFrameDesc.dataFrameId
+                   << "which is already free";
+        return Void();
+    }
+
+    // Mark the frame as available
+    mDataFrames[dataFrameDesc.dataFrameId].inUse = false;
+    mFramesInUse--;
+
+    // If this frame's index is high in the array, try to move it down
+    // to improve locality after mFramesAllowed has been reduced.
+    if (dataFrameDesc.dataFrameId >= mFramesAllowed) {
+        // Find an empty slot lower in the array (which should always exist in this case)
+        for (auto&& dataFrame : mDataFrames) {
+            if (!dataFrame.sharedMemory.IsValid()) {
+                dataFrame.sharedMemory = mDataFrames[dataFrameDesc.dataFrameId].sharedMemory;
+                mDataFrames[dataFrameDesc.dataFrameId].sharedMemory.clear();
+                return Void();
+            }
+        }
+    }
+
+    return Void();
+}
+
+Return<EvsResult> EvsUltrasonicsArray::startStream(
+        const ::android::sp<IEvsUltrasonicsArrayStream>& stream) {
+    LOG(DEBUG) << "EvsUltrasonicsArray startStream";
+
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    if (mStreamState != STOPPED) {
+        LOG(ERROR) << "ignoring startStream call when a stream is already running.";
+        return EvsResult::STREAM_ALREADY_RUNNING;
+    }
+
+    // If the client never indicated otherwise, configure ourselves for a single streaming buffer
+    if (mFramesAllowed < 1) {
+        if (!setAvailableFrames_Locked(1)) {
+            LOG(ERROR)
+                    << "Failed to start stream because we couldn't get shared memory data buffer";
+            return EvsResult::BUFFER_NOT_AVAILABLE;
+        }
+    }
+
+    // Record the user's callback for use when we have a frame ready
+    mStream = stream;
+
+    // Start the frame generation thread
+    mStreamState = RUNNING;
+    mCaptureThread = std::thread([this]() { generateDataFrames(); });
+
+    return EvsResult::OK;
+}
+
+Return<void> EvsUltrasonicsArray::stopStream() {
+    LOG(DEBUG) << "EvsUltrasonicsArray stopStream";
+
+    bool streamStateStopping = false;
+    {
+        std::lock_guard<std::mutex> lock(mAccessLock);
+        if (mStreamState == RUNNING) {
+            // Tell the GenerateFrames loop we want it to stop
+            mStreamState = STOPPING;
+            streamStateStopping = true;
+        }
+    }
+
+    if (streamStateStopping) {
+        // Block outside the mutex until the "stop" flag has been acknowledged
+        // We won't send any more frames, but the client might still get some already in flight
+        LOG(DEBUG) << "Waiting for stream thread to end...";
+        mCaptureThread.join();
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mAccessLock);
+        mStreamState = STOPPED;
+        mStream = nullptr;
+        LOG(DEBUG) << "Stream marked STOPPED.";
+    }
+
+    return Void();
+}
+
+bool EvsUltrasonicsArray::setAvailableFrames_Locked(unsigned bufferCount) {
+    if (bufferCount < 1) {
+        LOG(ERROR) << "Ignoring request to set buffer count to zero";
+        return false;
+    }
+    if (bufferCount > kMaximumDataFramesInFlight) {
+        LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
+        return false;
+    }
+
+    // Is an increase required?
+    if (mFramesAllowed < bufferCount) {
+        // An increase is required
+        unsigned needed = bufferCount - mFramesAllowed;
+        LOG(INFO) << "Number of data frame buffers to add: " << needed;
+
+        unsigned added = increaseAvailableFrames_Locked(needed);
+        if (added != needed) {
+            // If we didn't add all the frames we needed, then roll back to the previous state
+            LOG(ERROR) << "Rolling back to previous frame queue size";
+            decreaseAvailableFrames_Locked(added);
+            return false;
+        }
+    } else if (mFramesAllowed > bufferCount) {
+        // A decrease is required
+        unsigned framesToRelease = mFramesAllowed - bufferCount;
+        LOG(INFO) << "Number of data frame buffers to reduce: " << framesToRelease;
+
+        unsigned released = decreaseAvailableFrames_Locked(framesToRelease);
+        if (released != framesToRelease) {
+            // This shouldn't happen with a properly behaving client because the client
+            // should only make this call after returning sufficient outstanding buffers
+            // to allow a clean resize.
+            LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
+        }
+    }
+
+    return true;
+}
+
+EvsUltrasonicsArray::SharedMemory EvsUltrasonicsArray::allocateAndMapSharedMemory() {
+    SharedMemory sharedMemory;
+
+    // Check shared memory allocator is valid.
+    if (mShmemAllocator.get() == nullptr) {
+        LOG(ERROR) << "Shared memory allocator not initialized.";
+        return SharedMemory();
+    }
+
+    // Allocate memory.
+    bool allocateSuccess = false;
+    Return<void> result = mShmemAllocator->allocate(kSharedMemoryMaxSize,
+                                                    [&](bool success, const hidl_memory& hidlMem) {
+                                                        if (!success) {
+                                                            return;
+                                                        }
+                                                        allocateSuccess = success;
+                                                        sharedMemory.hidlMemory = hidlMem;
+                                                    });
+
+    // Check result of allocated memory.
+    if (!result.isOk() || !allocateSuccess) {
+        LOG(ERROR) << "Shared memory allocation failed.";
+        return SharedMemory();
+    }
+
+    // Map shared memory.
+    sharedMemory.pIMemory = mapMemory(sharedMemory.hidlMemory);
+    if (sharedMemory.pIMemory.get() == nullptr) {
+        LOG(ERROR) << "Shared memory mapping failed.";
+        return SharedMemory();
+    }
+
+    // Return success.
+    return sharedMemory;
+}
+
+unsigned EvsUltrasonicsArray::increaseAvailableFrames_Locked(unsigned numToAdd) {
+    unsigned added = 0;
+
+    while (added < numToAdd) {
+        SharedMemory sharedMemory = allocateAndMapSharedMemory();
+
+        // If allocate and map fails, break.
+        if (!sharedMemory.IsValid()) {
+            break;
+        }
+
+        // Find a place to store the new buffer
+        bool stored = false;
+        for (auto&& dataFrame : mDataFrames) {
+            if (!dataFrame.sharedMemory.IsValid()) {
+                // Use this existing entry
+                dataFrame.sharedMemory = sharedMemory;
+                dataFrame.inUse = false;
+                stored = true;
+                break;
+            }
+        }
+
+        if (!stored) {
+            // Add a BufferRecord wrapping this handle to our set of available buffers
+            mDataFrames.emplace_back(sharedMemory);
+        }
+
+        mFramesAllowed++;
+        added++;
+    }
+
+    return added;
+}
+
+unsigned EvsUltrasonicsArray::decreaseAvailableFrames_Locked(unsigned numToRemove) {
+    unsigned removed = 0;
+
+    for (auto&& dataFrame : mDataFrames) {
+        // Is this record not in use, but holding a buffer that we can free?
+        if (!dataFrame.inUse && dataFrame.sharedMemory.IsValid()) {
+            // Release buffer and update the record so we can recognize it as "empty"
+            dataFrame.sharedMemory.clear();
+
+            mFramesAllowed--;
+            removed++;
+
+            if (removed == numToRemove) {
+                break;
+            }
+        }
+    }
+
+    return removed;
+}
+
+// This is the asynchronous data frame generation thread that runs in parallel with the
+// main serving thread. There is one for each active ultrasonic array instance.
+void EvsUltrasonicsArray::generateDataFrames() {
+    LOG(DEBUG) << "Data frame generation loop started";
+
+    unsigned idx = 0;
+
+    while (true) {
+        bool timeForFrame = false;
+
+        nsecs_t startTime = elapsedRealtimeNano();
+
+        // Lock scope for updating shared state
+        {
+            std::lock_guard<std::mutex> lock(mAccessLock);
+
+            if (mStreamState != RUNNING) {
+                // Break out of our main thread loop
+                break;
+            }
+
+            // Are we allowed to issue another buffer?
+            if (mFramesInUse >= mFramesAllowed) {
+                // Can't do anything right now -- skip this frame
+                LOG(WARNING) << "Skipped a frame because too many are in flight";
+            } else {
+                // Identify an available buffer to fill
+                for (idx = 0; idx < mDataFrames.size(); idx++) {
+                    if (!mDataFrames[idx].inUse && mDataFrames[idx].sharedMemory.IsValid()) {
+                        // Found an available record, so stop looking
+                        break;
+                    }
+                }
+                if (idx >= mDataFrames.size()) {
+                    // This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
+                    LOG(ERROR) << "Failed to find an available buffer slot";
+                } else {
+                    // We're going to make the frame busy
+                    mDataFrames[idx].inUse = true;
+                    mFramesInUse++;
+                    timeForFrame = true;
+                }
+            }
+        }
+
+        if (timeForFrame) {
+            // Assemble the buffer description we'll transmit below
+            UltrasonicsDataFrameDesc dummyDataFrameDesc;
+            dummyDataFrameDesc.dataFrameId = idx;
+            dummyDataFrameDesc.waveformsData = mDataFrames[idx].sharedMemory.hidlMemory;
+
+            // Fill dummy waveform data.
+            fillDummyDataFrame(dummyDataFrameDesc, mDataFrames[idx].sharedMemory.pIMemory);
+
+            // Issue the (asynchronous) callback to the client -- can't be holding the lock
+            auto result = mStream->deliverDataFrame(dummyDataFrameDesc);
+            if (result.isOk()) {
+                LOG(DEBUG) << "Delivered data frame id: " << dummyDataFrameDesc.dataFrameId;
+            } else {
+                // This can happen if the client dies and is likely unrecoverable.
+                // To avoid consuming resources generating failing calls, we stop sending
+                // frames.  Note, however, that the stream remains in the "STREAMING" state
+                // until cleaned up on the main thread.
+                LOG(ERROR) << "Frame delivery call failed in the transport layer.";
+
+                // Since we didn't actually deliver it, mark the frame as available
+                std::lock_guard<std::mutex> lock(mAccessLock);
+                mDataFrames[idx].inUse = false;
+                mFramesInUse--;
+
+                break;
+            }
+        }
+
+        // Sleep to generate frames at kTargetFrameRate.
+        static const nsecs_t kTargetFrameTimeUs = 1000 * 1000 / kTargetFrameRate;
+        const nsecs_t now = elapsedRealtimeNano();
+        const nsecs_t workTimeUs = (now - startTime) / 1000;
+        const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
+        if (sleepDurationUs > 0) {
+            usleep(sleepDurationUs);
+        }
+    }
+
+    // If we've been asked to stop, send an event to signal the actual end of stream
+    EvsEventDesc event;
+    event.aType = EvsEventType::STREAM_STOPPED;
+    auto result = mStream->notify(event);
+    if (!result.isOk()) {
+        LOG(ERROR) << "Error delivering end of stream marker";
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace evs
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/evs/1.1/default/EvsUltrasonicsArray.h b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
new file mode 100644
index 0000000..7a41012
--- /dev/null
+++ b/automotive/evs/1.1/default/EvsUltrasonicsArray.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+#define ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
+
+#include <thread>
+#include <utility>
+
+#include <android-base/macros.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <utils/threads.h>
+
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/types.h>
+
+using ::android::hardware::hidl_memory;
+using ::android::hardware::automotive::evs::V1_0::EvsResult;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsArrayDesc;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace evs {
+namespace V1_1 {
+namespace implementation {
+
+class EvsUltrasonicsArray : public IEvsUltrasonicsArray {
+  public:
+    // Methods from ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray follow.
+    Return<void> getUltrasonicArrayInfo(getUltrasonicArrayInfo_cb _get_info_cb) override;
+    Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
+    Return<void> doneWithDataFrame(const UltrasonicsDataFrameDesc& dataFrameDesc) override;
+    Return<EvsResult> startStream(const ::android::sp<IEvsUltrasonicsArrayStream>& stream) override;
+    Return<void> stopStream() override;
+
+    // Factory function to create a array.
+    static sp<EvsUltrasonicsArray> Create(const char* deviceName);
+
+    // Returns a ultrasonics array descriptor filled with sample data.
+    static UltrasonicsArrayDesc GetDummyArrayDesc(const char* id);
+
+    DISALLOW_COPY_AND_ASSIGN(EvsUltrasonicsArray);
+    virtual ~EvsUltrasonicsArray() override;
+    void forceShutdown();  // This gets called if another caller "steals" ownership
+
+  private:
+    // Structure holding the hidl memory struct and the interface to a shared memory.
+    struct SharedMemory {
+        hidl_memory hidlMemory;
+        sp<IMemory> pIMemory;
+
+        SharedMemory() : hidlMemory(hidl_memory()), pIMemory(nullptr){};
+
+        SharedMemory(hidl_memory hidlMem, sp<IMemory> pIMem)
+            : hidlMemory(hidlMem), pIMemory(pIMem) {}
+
+        bool IsValid() { return (pIMemory.get() != nullptr && hidlMemory.valid()); }
+
+        void clear() {
+            hidlMemory = hidl_memory();
+            pIMemory.clear();
+        }
+    };
+
+    // Struct for a data frame record.
+    struct DataFrameRecord {
+        SharedMemory sharedMemory;
+        bool inUse;
+        explicit DataFrameRecord(SharedMemory shMem) : sharedMemory(shMem), inUse(false){};
+    };
+
+    enum StreamStateValues {
+        STOPPED,
+        RUNNING,
+        STOPPING,
+        DEAD,
+    };
+
+    EvsUltrasonicsArray(const char* deviceName);
+
+    // These three functions are expected to be called while mAccessLock is held
+    bool setAvailableFrames_Locked(unsigned bufferCount);
+    unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
+    unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
+
+    void generateDataFrames();
+
+    SharedMemory allocateAndMapSharedMemory();
+
+    UltrasonicsArrayDesc mArrayDesc = {};  // The properties of this ultrasonic array.
+
+    std::thread mCaptureThread;  // The thread we'll use to synthesize frames
+
+    sp<IEvsUltrasonicsArrayStream> mStream = nullptr;  // The callback used to deliver each frame
+
+    sp<IAllocator> mShmemAllocator = nullptr;  // Shared memory allocator.
+
+    std::mutex mAccessLock;
+    std::vector<DataFrameRecord> mDataFrames GUARDED_BY(mAccessLock);  // Shared memory buffers.
+    unsigned mFramesAllowed GUARDED_BY(mAccessLock);  // How many buffers are we currently using.
+    unsigned mFramesInUse GUARDED_BY(mAccessLock);  // How many buffers are currently outstanding.
+
+    StreamStateValues mStreamState GUARDED_BY(mAccessLock);
+};
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace evs
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_AUTOMOTIVE_EVS_V1_1_EVSULTRASONICSARRAY_H
diff --git a/automotive/evs/1.1/types.hal b/automotive/evs/1.1/types.hal
index aafdb70..b34e7e7 100644
--- a/automotive/evs/1.1/types.hal
+++ b/automotive/evs/1.1/types.hal
@@ -177,3 +177,194 @@
      */
     ABSOLUTE_ZOOM,
 };
+
+/**
+ * Structure identifies and describes an ultrasonics array in the car.
+ *
+ * A ultrasonics array represents a group of ultrasonic sensors within the
+ * car. These may be sensors that are physically connected to the same hardware
+ * control unit or represent a logical group of sensors like front and back.
+ * The HAL is responsible for filling out this structure for each Ultrasonics
+ * Array.
+ */
+struct UltrasonicsArrayDesc {
+    /**
+     * Unique identifier for the ultrasonic array. This may be a path or name of the
+     * physical control device or a string identifying a logical group of sensors forming an array
+     * such as "front_array" and "back_array".
+     */
+    string ultrasonicsArrayId;
+
+    /**
+     * Maximum number of readings (points on waveform) provided per sensor in
+     * each data frame. Used by client to pre-allocate required memory buffer for
+     * incoming data.
+     */
+    uint32_t maxReadingsPerSensorCount;
+
+    /**
+     * Maximum number of receiver sensors in a data frame. Must be between 1
+     * and sensorCount. Used by client to pre-allocate required memory buffer for
+     * incoming data.
+     */
+    uint32_t maxReceiversCount;
+
+    /**
+     * The order of sensors specified should preferably be in clockwise order
+     * around the car, starting from front left-most sensor.
+     */
+    vec<UltrasonicSensor> sensors;
+};
+
+/**
+ * Structure for rotation expressed as quaternions.
+ * Convention used: Unit quaternion with hamilton convention.
+ */
+struct RotationQuat {
+    float x;
+    float y;
+    float z;
+    float w;
+};
+
+/** Structure for translation with x, y and z units. */
+struct Translation {
+    float x;
+    float y;
+    float z;
+};
+
+/**
+ * Provides the orientation and location of a car sensor relative to the android automotive
+ * coordinate system:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ * The sensor pose defines the transformation to be applied to the android automotive axes to
+ * obtain the sensor local axes.
+ * The pose consists of rotation, (specified as a quaternions) and translation
+ * (vector with x, y, z).
+ * This rotation and translation applied to the sensor data in the sensor's local coordinate
+ * system transform the data to the automotive coordinate system.
+ * i.e   Pcar =  ( Rot * Psensor ) + Trans
+ * Here Pcar is a point in automotive coordinate system and Psensor is a point in the sensor's
+ * coordinate system.
+ * Example:
+ * For a sensor on the front bumper and on the left corner of the car with its X axis pointing to
+ * the front, the sensor is located at (-2, 4, 0) meters w.r.t android automotive axes and the
+ * sensor local axes has a rotation of 90 degrees counter-clockwise w.r.t android automotive axes
+ * when viewing the car from top on the +Z axis side:
+ *
+ *      ↑X sensor
+ *    Y←∘______
+ *      |      |  front
+ *      | car  |
+ *      |  ↑Y  |
+ *      |  ∘→X |  rear
+ *      |______|
+ *
+ * For this example the rotation and translation will be:
+ * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion.
+ * Translation = (-2, 4, 0) in meters = (-2000, 4000, 0) in milli-meters.
+ * Note: Every sensor type must specify its own pose.
+ */
+struct SensorPose {
+    /**
+     * Rotation part of the sensor pose, expressed as a unit quaternion.
+     */
+    RotationQuat rotation;
+
+    /**
+     * Translation part of the sensor pose, in (x, y, z) format with milli-meter units.
+     */
+    Translation translation;
+};
+
+/**
+ * Structure that contains all information of an ultrasonic sensor.
+ */
+struct UltrasonicSensor {
+    /**
+     * Pose provides the orientation and location of the ultrasonic sensor within the car.
+     * The +Y axis points along the center of the beam spread the X axis to the right and the Z
+     * axis in the up direction.
+     */
+    SensorPose pose;
+
+    /**
+     * Maximum range of the sensor in milli-metres.
+     */
+    float maxRange;
+
+    /**
+     * Half-angle of the angle of measurement of the sensor, relative to the
+     * sensor’s x axis, in radians.
+     */
+    float angleOfMeasurement;
+};
+
+/**
+ * Structure that describes the data frame received from an ultrasonics array.
+ *
+ * Each data frame returned consists of received waveform signals from a subset
+ * of sensors in an array as indicated by the receiversIdList. The signal is
+ * transmitted at a particular time instant indicated by timestampNs from a
+ * subset of sensors in the array as provided in the transmittersIdList.
+ */
+struct UltrasonicsDataFrameDesc {
+    /**
+     * Timestamp of the start of the transmit signal for this data frame.
+     * Timestamp unit is nanoseconds and is obtained from android elapsed realtime clock which is
+     * the time since system was booted and includes deep sleep.
+     * timeOfFlight readings are future-deltas to this timestamp.
+     */
+    uint64_t timestampNs;
+
+    /**
+     * Identifier of data frame. Used by implementation for managing multiple frames in flight.
+     */
+    uint32_t dataFrameId;
+
+    /**
+     * List of indexes of sensors in range [0, sensorCount - 1] that
+     * transmitted the signal for this data frame.
+     */
+    vec<uint8_t> transmittersIdList;
+
+    /**
+     * List of indexes of sensors in range [0, sensorCount - 1] that received
+     * the signal. The order of ids must match the order of the waveforms in the
+     * waveformsData.
+     * Size of list is upper bound by maxReceiversCount.
+     */
+    vec<uint8_t> receiversIdList;
+
+    /**
+     * List of the number of readings corresponding to each ultrasonics sensor in
+     * the receiversIdList. Order of the readings count must match the order in
+     * receiversIdList.
+     * Size of list is upper bound by maxReadingsPerSensorCount.
+     */
+    vec<uint32_t> receiversReadingsCountList;
+
+    /**
+     * Shared memory object containing the waveforms data. Contains one waveform
+     * for each sensor specified in receiversIdList, in order.
+     * Each waveform is represented by a number of readings, which are sample
+     * points on the waveform. The number of readings for each waveform is as
+     * specified in the receiversReadingsCountList.
+     * Each reading is a pair of time Of flight and resonance.
+     * Time of flight (float): Time between transmit and receive signal in nanoseconds.
+     * Resonance (float): Resonance at time on waveform in range [0.0, 1.0].
+     *
+     * The structure of shared memory (example with 2 waveforms, each with 2 readings):
+     *
+     * Byte: |   0    |  1-4  |  5-8  | 9-12  | 13-16 ||   17   |  18-21 | 22-25  | 26-29 | 30-33 |
+     * Data: | RecId1 | TOF1  | RES1  | TOF2  | RES2  || RecId2 |  TOF1  |  RES1  | TOF2  | RES2  |
+     *       |              Waveform1                 ||             Waveform2                    |
+     * Here:
+     * RecId : Receiver's Id. Order matches the receiversIdList, type uint8_t
+     * TOF : Time of flight, type float (4 bytes)
+     * RES : Resonance, type float (4 bytes)
+     * Note: All readings and waveforms are contigious with no padding.
+     */
+    memory waveformsData;
+};
diff --git a/automotive/evs/1.1/vts/functional/Android.bp b/automotive/evs/1.1/vts/functional/Android.bp
index 4753933..086a199 100644
--- a/automotive/evs/1.1/vts/functional/Android.bp
+++ b/automotive/evs/1.1/vts/functional/Android.bp
@@ -18,12 +18,16 @@
     name: "VtsHalEvsV1_1TargetTest",
     srcs: [
         "FrameHandler.cpp",
+        "FrameHandlerUltrasonics.cpp",
         "VtsHalEvsV1_1TargetTest.cpp",
     ],
     defaults: ["VtsHalTargetTestDefaults"],
     shared_libs: [
         "libui",
         "libcamera_metadata",
+        "libhidlmemory",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
     ],
     static_libs: [
         "android.hardware.automotive.evs@1.0",
@@ -34,7 +38,7 @@
         "android.hardware.graphics.common@1.2",
         "android.hardware.camera.device@3.2",
     ],
-    test_suites: ["general-tests"],
+    test_suites: ["vts-core"],
     cflags: [
         "-O0",
         "-g",
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
new file mode 100644
index 0000000..22522ce
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "FrameHandlerUltrasonics.h"
+
+#include <android-base/logging.h>
+#include <hidlmemory/mapping.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::Return;
+using ::android::sp;
+
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream;
+using ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray;
+using ::android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventDesc;
+using ::android::hardware::automotive::evs::V1_1::EvsEventType;
+
+FrameHandlerUltrasonics::FrameHandlerUltrasonics(sp<IEvsUltrasonicsArray> pEvsUltrasonicsArray) :
+    mEvsUltrasonicsArray(pEvsUltrasonicsArray), mReceiveFramesCount(0) {
+    // Nothing but member initialization
+}
+
+Return<void> FrameHandlerUltrasonics::notify(const EvsEventDesc& evsEvent) {
+    switch (evsEvent.aType) {
+        case EvsEventType::STREAM_STARTED:
+        case EvsEventType::STREAM_STOPPED:
+        case EvsEventType::FRAME_DROPPED:
+        case EvsEventType::TIMEOUT:
+            mReceivedEvents.emplace_back(evsEvent);
+            break;
+        default:
+            LOG(ERROR) << "Received unexpected event";
+    }
+
+    return android::hardware::Void();
+}
+
+// Struct used by SerializeWaveformData().
+struct WaveformData {
+    uint8_t receiverId;
+    std::vector<std::pair<float, float>> readings;
+};
+
+// De-serializes shared memory to vector of WaveformData.
+// TODO(b/149950362): Add a common library for serialiazing and deserializing waveform data.
+std::vector<WaveformData> DeSerializeWaveformData(std::vector<uint32_t> recvReadingsCountList,
+        uint8_t* pData) {
+    std::vector<WaveformData> waveformDataList(recvReadingsCountList.size());
+
+    for (int i = 0; i < waveformDataList.size(); i++) {
+        // Set Id
+        memcpy(&waveformDataList[i].receiverId, pData, sizeof(uint8_t));
+        pData += sizeof(uint8_t);
+
+        waveformDataList[i].readings.resize(recvReadingsCountList[i]);
+
+        for (auto& reading : waveformDataList[i].readings) {
+            // Set the time of flight.
+            memcpy(&reading.first, pData, sizeof(float));
+            pData += sizeof(float);
+
+            // Set the resonance.
+            memcpy(&reading.second, pData, sizeof(float));
+            pData += sizeof(float);
+        }
+    }
+    return waveformDataList;
+}
+
+bool DataFrameValidator(const UltrasonicsDataFrameDesc& dataFrameDesc) {
+
+    if (dataFrameDesc.receiversIdList.size() != dataFrameDesc.receiversReadingsCountList.size()) {
+        LOG(ERROR) << "Size mismatch of receiversIdList and receiversReadingsCountList";
+        return false;
+    }
+
+    if(!dataFrameDesc.waveformsData.valid()) {
+        LOG(ERROR) << "Data frame does not valid hidl memory";
+        return false;
+    }
+
+    // Check total bytes from dataFrameDesc are within the shared memory size.
+    int totalWaveformDataBytesSize = 0;
+    for (int i = 0; i < dataFrameDesc.receiversIdList.size(); i++) {
+        totalWaveformDataBytesSize = 1 + (4 * 2 * dataFrameDesc.receiversReadingsCountList[i]);
+    }
+    if (totalWaveformDataBytesSize > dataFrameDesc.waveformsData.size()) {
+        LOG(ERROR) << "Total waveform data bytes in desc exceed shared memory size";
+        return false;
+    }
+
+    sp<IMemory> pIMemory = mapMemory(dataFrameDesc.waveformsData);
+    if(pIMemory.get() == nullptr) {
+        LOG(ERROR) << "Failed to map hidl memory";
+        return false;
+    }
+
+    uint8_t* pData = (uint8_t*)((void*)pIMemory->getPointer());
+    if(pData == nullptr) {
+        LOG(ERROR) << "Failed getPointer from mapped shared memory";
+        return false;
+    }
+
+    const std::vector<WaveformData> waveformDataList = DeSerializeWaveformData(
+            dataFrameDesc.receiversReadingsCountList, pData);
+
+    // Verify the waveforms data.
+    for(int i = 0; i < waveformDataList.size(); i++) {
+        if (waveformDataList[i].receiverId != dataFrameDesc.receiversIdList[i]) {
+            LOG(ERROR) << "Receiver Id mismatch";
+            return false;
+        }
+        for(auto& reading : waveformDataList[i].readings) {
+            if (reading.second < 0.0f || reading.second > 1.0f) {
+                LOG(ERROR) << "Resonance reading is not in range [0, 1]";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+Return<void> FrameHandlerUltrasonics::deliverDataFrame(
+        const UltrasonicsDataFrameDesc& dataFrameDesc) {
+    LOG(DEBUG) << "FrameHandlerUltrasonics::receiveFrames";
+
+    mReceiveFramesCount++;
+    mLastReceivedFrames = dataFrameDesc;
+
+    if(!DataFrameValidator(dataFrameDesc)) {
+        mAllFramesValid = false;
+    }
+
+    // Send done with data frame.
+    mEvsUltrasonicsArray->doneWithDataFrame(dataFrameDesc);
+
+    return android::hardware::Void();
+}
+
+bool FrameHandlerUltrasonics::checkEventReceived(EvsEventDesc evsEvent) {
+    LOG(DEBUG) << "FrameHandlerUltrasonics::checkEventReceived";
+    int size = mReceivedEvents.size(); // work around
+    LOG(DEBUG) << "Received event number: " << size;
+    auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), evsEvent);
+    return iter != mReceivedEvents.end();
+}
+
+int FrameHandlerUltrasonics::getReceiveFramesCount() {
+    return mReceiveFramesCount;
+}
+
+bool FrameHandlerUltrasonics::areAllFramesValid() {
+    return mAllFramesValid;
+}
diff --git a/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
new file mode 100644
index 0000000..1fc2143
--- /dev/null
+++ b/automotive/evs/1.1/vts/functional/FrameHandlerUltrasonics.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FRAME_HANDLER_ULTRASONICS_H
+#define FRAME_HANDLER_ULTRASONICS_H
+
+#include <android/hardware/automotive/evs/1.1/types.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArrayStream.h>
+#include <android/hardware/automotive/evs/1.1/IEvsUltrasonicsArray.h>
+
+#include <vector>
+
+class FrameHandlerUltrasonics : public
+        android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream {
+public:
+    FrameHandlerUltrasonics(
+            android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+            pEvsUltrasonicsArray);
+
+    // Implementation for ::android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArrayStream
+    android::hardware::Return<void> notify(
+            const android::hardware::automotive::evs::V1_1::EvsEventDesc& evsEvent) override;
+    android::hardware::Return<void> deliverDataFrame(
+            const android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc&
+             dataFrameDesc) override;
+
+    bool checkEventReceived(android::hardware::automotive::evs::V1_1::EvsEventDesc evsEvent);
+    int getReceiveFramesCount();
+    bool areAllFramesValid();
+
+private:
+    android::sp<android::hardware::automotive::evs::V1_1::IEvsUltrasonicsArray>
+            mEvsUltrasonicsArray;
+    android::hardware::automotive::evs::V1_1::UltrasonicsDataFrameDesc mLastReceivedFrames;
+    std::vector<android::hardware::automotive::evs::V1_1::EvsEventDesc> mReceivedEvents;
+    int mReceiveFramesCount;
+    bool mAllFramesValid = true;
+};
+
+#endif //FRAME_HANDLER_ULTRASONICS_H
diff --git a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
index 2c8c1e1..cde8048 100644
--- a/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
+++ b/automotive/evs/1.1/vts/functional/VtsHalEvsV1_1TargetTest.cpp
@@ -17,15 +17,6 @@
 #define LOG_TAG "VtsHalEvsTest"
 
 
-// Note:  We have't got a great way to indicate which target
-// should be tested, so we'll leave the interface served by the
-// default (mock) EVS driver here for easy reference.  All
-// actual EVS drivers should serve on the EvsEnumeratorHw name,
-// however, so the code is checked in that way.
-//const static char kEnumeratorName[]  = "EvsEnumeratorHw-Mock";
-const static char kEnumeratorName[]  = "EvsEnumeratorHw";
-
-
 // These values are called out in the EVS design doc (as of Mar 8, 2017)
 static const int kMaxStreamStartMilliseconds = 500;
 static const int kMinimumFramesPerSecond = 10;
@@ -37,6 +28,7 @@
 
 
 #include "FrameHandler.h"
+#include "FrameHandlerUltrasonics.h"
 
 #include <cstdio>
 #include <cstring>
@@ -60,10 +52,12 @@
 #include <ui/DisplayConfig.h>
 #include <ui/DisplayState.h>
 
-#include <VtsHalHidlTargetTestBase.h>
-#include <VtsHalHidlTargetTestEnvBase.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
 
 using namespace ::android::hardware::automotive::evs::V1_1;
+using namespace std::chrono_literals;
 
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -96,29 +90,13 @@
 } RawStreamConfig;
 
 
-// Test environment for Evs HIDL HAL.
-class EvsHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
-   public:
-    // get the test environment singleton
-    static EvsHidlEnvironment* Instance() {
-        static EvsHidlEnvironment* instance = new EvsHidlEnvironment;
-        return instance;
-    }
-
-    virtual void registerTestServices() override { registerTestService<IEvsEnumerator>(); }
-
-   private:
-    EvsHidlEnvironment() {}
-};
-
 // The main test class for EVS
-class EvsHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+class EvsHidlTest : public ::testing::TestWithParam<std::string> {
 public:
     virtual void SetUp() override {
         // Make sure we can connect to the enumerator
-        string service_name =
-            EvsHidlEnvironment::Instance()->getServiceName<IEvsEnumerator>(kEnumeratorName);
-        pEnumerator = getService<IEvsEnumerator>(service_name);
+        std::string service_name = GetParam();
+        pEnumerator = IEvsEnumerator::getService(service_name);
         ASSERT_NE(pEnumerator.get(), nullptr);
 
         mIsHwModule = pEnumerator->isHardware();
@@ -151,9 +129,21 @@
                 }
             }
         );
+    }
 
-        // We insist on at least one camera for EVS to pass any camera tests
-        ASSERT_GE(cameraInfo.size(), 1u);
+    void loadUltrasonicsArrayList() {
+        // SetUp() must run first!
+        assert(pEnumerator != nullptr);
+
+        // Get the ultrasonics array list
+        pEnumerator->getUltrasonicsArrayList([this](hidl_vec<UltrasonicsArrayDesc> ultraList) {
+            ALOGI("Ultrasonics array list callback received %zu arrays", ultraList.size());
+            ultrasonicsArraysInfo.reserve(ultraList.size());
+            for (auto&& ultraArray : ultraList) {
+                ALOGI("Found ultrasonics array %s", ultraArray.ultrasonicsArrayId.c_str());
+                ultrasonicsArraysInfo.push_back(ultraArray);
+            }
+        });
     }
 
     bool isLogicalCamera(const camera_metadata_t *metadata) {
@@ -240,6 +230,11 @@
                                                    // is HW module implementation.
     std::deque<wp<IEvsCamera_1_1>>  activeCameras; // A list of active camera handles that are
                                                    // needed to be cleaned up.
+    std::vector<UltrasonicsArrayDesc>
+            ultrasonicsArraysInfo;                           // Empty unless/until
+                                                             // loadUltrasonicsArrayList() is called
+    std::deque<wp<IEvsCamera_1_1>> activeUltrasonicsArrays;  // A list of active ultrasonic array
+                                                             // handles that are to be cleaned up.
 };
 
 
@@ -251,7 +246,7 @@
  * Opens each camera reported by the enumerator and then explicitly closes it via a
  * call to closeCamera.  Then repeats the test to ensure all cameras can be reopened.
  */
-TEST_F(EvsHidlTest, CameraOpenClean) {
+TEST_P(EvsHidlTest, CameraOpenClean) {
     ALOGI("Starting CameraOpenClean test");
 
     // Get the camera list
@@ -320,7 +315,7 @@
  * call.  This ensures that the intended "aggressive open" behavior works.  This is necessary for
  * the system to be tolerant of shutdown/restart race conditions.
  */
-TEST_F(EvsHidlTest, CameraOpenAggressive) {
+TEST_P(EvsHidlTest, CameraOpenAggressive) {
     ALOGI("Starting CameraOpenAggressive test");
 
     // Get the camera list
@@ -397,7 +392,7 @@
  * CameraStreamPerformance:
  * Measure and qualify the stream start up time and streaming frame rate of each reported camera
  */
-TEST_F(EvsHidlTest, CameraStreamPerformance) {
+TEST_P(EvsHidlTest, CameraStreamPerformance) {
     ALOGI("Starting CameraStreamPerformance test");
 
     // Get the camera list
@@ -487,7 +482,7 @@
  * Ensure the camera implementation behaves properly when the client holds onto buffers for more
  * than one frame time.  The camera must cleanly skip frames until the client is ready again.
  */
-TEST_F(EvsHidlTest, CameraStreamBuffering) {
+TEST_P(EvsHidlTest, CameraStreamBuffering) {
     ALOGI("Starting CameraStreamBuffering test");
 
     // Arbitrary constant (should be > 1 and less than crazy)
@@ -572,7 +567,7 @@
  * imagery is simply copied to the display buffer and presented on screen.  This is the one test
  * which a human could observe to see the operation of the system on the physical display.
  */
-TEST_F(EvsHidlTest, CameraToDisplayRoundTrip) {
+TEST_P(EvsHidlTest, CameraToDisplayRoundTrip) {
     ALOGI("Starting CameraToDisplayRoundTrip test");
 
     // Get the camera list
@@ -671,7 +666,7 @@
  * Verify that each client can start and stop video streams on the same
  * underlying camera.
  */
-TEST_F(EvsHidlTest, MultiCameraStream) {
+TEST_P(EvsHidlTest, MultiCameraStream) {
     ALOGI("Starting MultiCameraStream test");
 
     if (mIsHwModule) {
@@ -778,7 +773,7 @@
  * CameraParameter:
  * Verify that a client can adjust a camera parameter.
  */
-TEST_F(EvsHidlTest, CameraParameter) {
+TEST_P(EvsHidlTest, CameraParameter) {
     ALOGI("Starting CameraParameter test");
 
     // Get the camera list
@@ -922,7 +917,7 @@
  * Verify that non-master client gets notified when the master client either
  * terminates or releases a role.
  */
-TEST_F(EvsHidlTest, CameraMasterRelease) {
+TEST_P(EvsHidlTest, CameraMasterRelease) {
     ALOGI("Starting CameraMasterRelease test");
 
     if (mIsHwModule) {
@@ -1103,7 +1098,7 @@
  * Verify that master and non-master clients behave as expected when they try to adjust
  * camera parameters.
  */
-TEST_F(EvsHidlTest, MultiCameraParameter) {
+TEST_P(EvsHidlTest, MultiCameraParameter) {
     ALOGI("Starting MultiCameraParameter test");
 
     if (mIsHwModule) {
@@ -1386,7 +1381,7 @@
 
         std::mutex eventLock;
         auto timer = std::chrono::system_clock::now();
-        unique_lock<std::mutex> lock(eventLock);
+        std::unique_lock<std::mutex> lock(eventLock);
         while (!listening) {
             eventCond.wait_until(lock, timer + 1s);
         }
@@ -1576,7 +1571,7 @@
  * EVS client, which owns the display, is priortized and therefore can take over
  * a master role from other EVS clients without the display.
  */
-TEST_F(EvsHidlTest, HighPriorityCameraClient) {
+TEST_P(EvsHidlTest, HighPriorityCameraClient) {
     ALOGI("Starting HighPriorityCameraClient test");
 
     if (mIsHwModule) {
@@ -1949,7 +1944,7 @@
  * CameraToDisplayRoundTrip test case but this case retrieves available stream
  * configurations from EVS and uses one of them to start a video stream.
  */
-TEST_F(EvsHidlTest, CameraUseStreamConfigToDisplay) {
+TEST_P(EvsHidlTest, CameraUseStreamConfigToDisplay) {
     ALOGI("Starting CameraUseStreamConfigToDisplay test");
 
     // Get the camera list
@@ -2053,7 +2048,7 @@
  * Verify that each client can start and stop video streams on the same
  * underlying camera with same configuration.
  */
-TEST_F(EvsHidlTest, MultiCameraStreamUseConfig) {
+TEST_P(EvsHidlTest, MultiCameraStreamUseConfig) {
     ALOGI("Starting MultiCameraStream test");
 
     if (mIsHwModule) {
@@ -2202,7 +2197,7 @@
  * checking its capability and locating supporting physical camera device
  * identifiers.
  */
-TEST_F(EvsHidlTest, LogicalCameraMetadata) {
+TEST_P(EvsHidlTest, LogicalCameraMetadata) {
     ALOGI("Starting LogicalCameraMetadata test");
 
     // Get the camera list
@@ -2220,11 +2215,113 @@
 }
 
 
-int main(int argc, char** argv) {
-    ::testing::AddGlobalTestEnvironment(EvsHidlEnvironment::Instance());
-    ::testing::InitGoogleTest(&argc, argv);
-    EvsHidlEnvironment::Instance()->init(&argc, argv);
-    int status = RUN_ALL_TESTS();
-    ALOGI("Test result = %d", status);
-    return status;
+/*
+ * UltrasonicsArrayOpenClean:
+ * Opens each ultrasonics arrays reported by the enumerator and then explicitly closes it via a
+ * call to closeUltrasonicsArray. Then repeats the test to ensure all ultrasonics arrays
+ * can be reopened.
+ */
+TEST_P(EvsHidlTest, UltrasonicsArrayOpenClean) {
+    ALOGI("Starting UltrasonicsArrayOpenClean test");
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // Open and close each ultrasonics array twice
+    for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+        for (int pass = 0; pass < 2; pass++) {
+            sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+                    pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+            ASSERT_NE(pUltrasonicsArray, nullptr);
+
+            // Verify that this ultrasonics array self-identifies correctly
+            pUltrasonicsArray->getUltrasonicArrayInfo([&ultraInfo](UltrasonicsArrayDesc desc) {
+                ALOGD("Found ultrasonics array %s", ultraInfo.ultrasonicsArrayId.c_str());
+                EXPECT_EQ(ultraInfo.ultrasonicsArrayId, desc.ultrasonicsArrayId);
+            });
+
+            // Explicitly close the ultrasonics array so resources are released right away
+            pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+        }
+    }
 }
+
+
+// Starts a stream and verifies all data received is valid.
+TEST_P(EvsHidlTest, UltrasonicsVerifyStreamData) {
+    ALOGI("Starting UltrasonicsVerifyStreamData");
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // For each ultrasonics array.
+    for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+        ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+        sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+                pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+        ASSERT_NE(pUltrasonicsArray, nullptr);
+
+        sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+        // Start stream.
+        EvsResult result = pUltrasonicsArray->startStream(frameHandler);
+        ASSERT_EQ(result, EvsResult::OK);
+
+        // Wait 5 seconds to receive frames.
+        sleep(5);
+
+        // Stop stream.
+        pUltrasonicsArray->stopStream();
+
+        EXPECT_GT(frameHandler->getReceiveFramesCount(), 0);
+        EXPECT_TRUE(frameHandler->areAllFramesValid());
+
+        // Explicitly close the ultrasonics array so resources are released right away
+        pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+    }
+}
+
+
+// Sets frames in flight before and after start of stream and verfies success.
+TEST_P(EvsHidlTest, UltrasonicsSetFramesInFlight) {
+    ALOGI("Starting UltrasonicsSetFramesInFlight");
+
+    // Get the ultrasonics array list
+    loadUltrasonicsArrayList();
+
+    // For each ultrasonics array.
+    for (auto&& ultraInfo : ultrasonicsArraysInfo) {
+        ALOGD("Testing ultrasonics array: %s", ultraInfo.ultrasonicsArrayId.c_str());
+
+        sp<IEvsUltrasonicsArray> pUltrasonicsArray =
+                pEnumerator->openUltrasonicsArray(ultraInfo.ultrasonicsArrayId);
+        ASSERT_NE(pUltrasonicsArray, nullptr);
+
+        EvsResult result = pUltrasonicsArray->setMaxFramesInFlight(10);
+        EXPECT_EQ(result, EvsResult::OK);
+
+        sp<FrameHandlerUltrasonics> frameHandler = new FrameHandlerUltrasonics(pUltrasonicsArray);
+
+        // Start stream.
+        result = pUltrasonicsArray->startStream(frameHandler);
+        ASSERT_EQ(result, EvsResult::OK);
+
+        result = pUltrasonicsArray->setMaxFramesInFlight(5);
+        EXPECT_EQ(result, EvsResult::OK);
+
+        // Stop stream.
+        pUltrasonicsArray->stopStream();
+
+        // Explicitly close the ultrasonics array so resources are released right away
+        pEnumerator->closeUltrasonicsArray(pUltrasonicsArray);
+    }
+}
+
+
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance,
+    EvsHidlTest,
+    testing::ValuesIn(android::hardware::getAllHalInstanceNames(IEvsEnumerator::descriptor)),
+    android::hardware::PrintInstanceNameToString);
+
diff --git a/automotive/sv/1.0/Android.bp b/automotive/sv/1.0/Android.bp
new file mode 100644
index 0000000..769bdc6
--- /dev/null
+++ b/automotive/sv/1.0/Android.bp
@@ -0,0 +1,24 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+    name: "android.hardware.automotive.sv@1.0",
+    root: "android.hardware",
+    vndk: {
+        enabled: true,
+    },
+    srcs: [
+        "types.hal",
+        "ISurroundViewStream.hal",
+        "ISurroundViewSession.hal",
+        "ISurroundView2dSession.hal",
+        "ISurroundView3dSession.hal",
+        "ISurroundViewService.hal",
+    ],
+    interfaces: [
+        "android.hidl.base@1.0",
+        "android.hardware.graphics.common@1.0",
+        "android.hardware.graphics.common@1.1",
+        "android.hardware.graphics.common@1.2",
+    ],
+    gen_java: true,
+}
diff --git a/automotive/sv/1.0/ISurroundView2dSession.hal b/automotive/sv/1.0/ISurroundView2dSession.hal
new file mode 100644
index 0000000..fa49674
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundView2dSession.hal
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewSession;
+
+/**
+ * Interface representing a surround view 2d session.
+ *
+ * Surround view 2d provides a top/bird's eye view of the car and its surroundings.
+ */
+interface ISurroundView2dSession extends ISurroundViewSession {
+
+    /**
+     * Gets mapping information for 2d surround view.
+     *
+     * Mapping information maps the output frame of 2d surround view to actual dimensions
+     * covered on the ground. Mapping information is fixed for a car and is based upon its camera
+     * coverage. Mapping information can be used for doing overlays of objects in 3d space
+     * onto the surround view 2d output frame.
+     *
+     * @param sv2dConfig Configuration to set.
+     * @return sv2dMappingInfo mapping information of the 2d surround view.
+     */
+    get2dMappingInfo() generates (Sv2dMappingInfo sv2dMappingInfo);
+
+    /**
+     * Sets the configuration of 2d surround view.
+     *
+     * Configuration is used for supported different target use-cases of the surround view eg.
+     * fullscreen or preview. Default configuration is FULLSCREEN.
+     * A set config call can be performed at any time (before or after startStream) of the session.
+     * Once config change is complete, a CONFIG_CHANGED event is sent, after which
+     * all frames received will be of the updated config.
+     *
+     * @param sv2dConfig Configuration to set.
+     * @return svResult  Returns OK if successful, appropriate error result otherwise.
+     */
+    set2dConfig(Sv2dConfig sv2dConfig) generates (SvResult svResult);
+
+    /**
+     * Gets the current configuration of the 2d surround view.
+     *
+     * Configuration is used for supported different target use-cases of the surround view eg.
+     * fullscreen view or preview. Use setConfig call to set a configuration.
+     *
+     * @return sv2dConfig the active current configuration of the 2d session.
+     */
+    get2dConfig() generates (Sv2dConfig sv2dConfig);
+
+    /**
+     * Projects points on camera image to surround view 2d image.
+     *
+     * Useful for mapping points detected on individual camera frames onto the surround view 2d
+     * output frame.
+     *
+     * @param cameraPoints  List of camera pixel points to be projected in range including (0, 0)
+     *                      and (width - 1, height -1) of camera frame. If point is outside camera
+                            frame INVALID_ARG error is returned.
+     * @param cameraId      Id of the EvsCamera to use for projecting points. Id must be one of the
+     *                      cameras as returned by getCameraIds() else INVALID_ARG error is returned
+     * @return points2d     Returns a list of 2d pixel points projecting into surround view 2d
+     *                      frame in the same order as cameraPoints. Point projected maybe outside
+     *                      surround view frame i.e. outside (0, 0) and
+     *                      (sv_width - 1, sv_height - 1). Points that do not project to ground
+     *                      plane are set with inValid true.
+     */
+    projectCameraPoints(vec<Point2dInt> cameraPoints, string cameraId) generates (
+            vec<Point2dFloat> points2d);
+};
diff --git a/automotive/sv/1.0/ISurroundView3dSession.hal b/automotive/sv/1.0/ISurroundView3dSession.hal
new file mode 100644
index 0000000..d2b0c53
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundView3dSession.hal
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewSession;
+
+/**
+ * Interface representing a surround view 3d session.
+ *
+ * Surround view 3d provides a virtual view from any desired position in the 3d space around the
+ * car. Surround view 3d creates an approximate 3d surface around the car to match the surrounds
+ * and provides a virtual view as seen on this surface.
+ */
+interface ISurroundView3dSession extends ISurroundViewSession {
+
+    /**
+     * Sets the desired views of surround view 3d.
+     *
+     * Surround view 3d takes a list of desired virtual view points and provides an output frame
+     * for each view. Default view list is a single view from behind the car front facing in the
+     * front direction.
+     * A call to setViews() results in the views set by a previous call to be discarded.
+     * Each view set is identified by an Id which is returned with the corresponding output frame
+     * of that view.
+     * Clients can call setViews() at any stage of the session (before/after startStream). Client
+     * may continue to receive frames of previous views after setViews() call for a while and can
+     * identify updated set of views once effective using the view Id provided in the updated
+     * views frames.
+     *
+     * @param views     List of desired views to generate output frames.
+     * @return svResult Returns OK if successful, appropriate error result otherwise.
+     */
+    setViews(vec<View3d> views) generates (SvResult svResult);
+
+    /**
+     * Sets the configuration of 3d surround view.
+     *
+     * Configuration is used for supported different target use-cases of the surround view eg.
+     * fullscreen view or preview. A set config call can be performed at anytime (before or after
+     * startStream) of the session.
+     * Once config change is complete, a CONFIG_CHANGED event is sent, after which
+     * all frames received will be of the updated config.
+     *
+     * @param sv3dConfig Configuration to set.
+     * @return svResult  Returns OK if successful, appropriate error result otherwise.
+     */
+    set3dConfig(Sv3dConfig sv3dConfig) generates (SvResult svResult);
+
+    /**
+     * Gets the current configuration of the 3d surround view.
+     *
+     * Configuration is used for supported different target use-cases of the surround view eg.
+     * fullscreen view or preview. Use setConfig call to set a configuration.
+     *
+     * @return sv3dConfig The current active configuration of the 3d session.
+     */
+    get3dConfig() generates (Sv3dConfig sv3dConfig);
+
+    /**
+     * Updates 3d overlays in scene.
+     *
+     * updateOverlays() provides a way to set a 3d overlay object in the scene. An overlay is an
+     * 3d object in the scene which can be a visual indicator to provide additional information eg.
+     * parking sensor distance indicators or overlays for objects in scene.
+     *
+     * An overlay object is defined by a set of points (forming triangles) with some color and
+     * transparency values and each overlay is identified by an overlay Id.
+     * When an overlay with a new Id is passed, a new overlay is added to the scene.
+     * When an overlay with previous id is passed, its vertices/color are updated with passed data.
+     * If the overlay data is empty, the overlay is removed from the scene.
+     *
+     * @param overlaysData   Object with shared memory containing overlays to add/update in the
+     *                       scene. Refer to OverlaysData structure for layout in shared memory.
+     * @return svResult      Returns OK if successful, appropriate error result otherwise.
+     */
+    updateOverlays(OverlaysData overlaysData) generates (SvResult svResult);
+
+    /**
+     * Projects points on camera image to surround view 3D surface.
+     *
+     * Useful for mapping points detected on individual camera frames onto the surround view 3D
+     * surface, these 3d points can then be used to set overlays using the updateOverlays() for
+     * the detected objects in the scene.
+     * Note:
+     * 3d points returned are projected on an approximate 3d surface and do not provide the exact
+     * 3d location.
+     *
+     * @param cameraPoints  List of camera pixel points to be projected in range including (0, 0)
+     *                      and (width - 1, height -1) of camera frame. If point is outside camera
+                            frame INVALID_ARG error is returned.
+     * @param cameraId      Id of the EvsCamera to use for projecting points. Id must be one of the
+     *                      cameras as returned by getCameraIds() else INVALID_ARG error is returned
+     * @return points3d     Returns a list of 3d points on the approximate 3d surface in the
+     *                      automotive coordinate system in the same order as cameraPoints.
+     *                      Points that do not project to 3d surface are set with inValid true.
+     */
+    projectCameraPointsTo3dSurface(vec<Point2dInt> cameraPoints, string cameraId) generates (
+            vec<Point3dFloat> points3d);
+};
diff --git a/automotive/sv/1.0/ISurroundViewService.hal b/automotive/sv/1.0/ISurroundViewService.hal
new file mode 100644
index 0000000..7de0bd1
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewService.hal
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundView2dSession;
+import ISurroundView3dSession;
+
+/**
+ * Interface representing entry point for surround view.
+ *
+ * Surround view service has two types of sessions 2d and 3d. Refer to their respective interface
+ * for more details.
+ */
+interface ISurroundViewService {
+
+    /**
+     * Gets a list of camera ids that are used for generating surround view.
+     * For 4 camera configuration, the cameras ids are ordered in clockwise direction
+     * when viewed from top of the car starting with the front camera. i.e. FRONT, RIGHT, REAR and
+     * LEFT. All other configurations must follow clockwise order.
+     *
+     * @result cameraIds List of camera ids that matching the Id of EVS Cameras used by service.
+     */
+    getCameraIds() generates (vec<string> cameraIds);
+
+    /**
+     * Starts a surround view 2d session.
+     *
+     * @result sv2dSession Returns a new 2d session that was created.
+     *         result      Returns OK if successful, appropriate error result otherwise.
+     */
+    start2dSession() generates (ISurroundView2dSession sv2dSession, SvResult result);
+
+    /**
+     * Stops a surround view 2d session.
+     *
+     * @param sv2dSession Valid 2d session to be stopped.
+     * @return svResult   Returns OK if successful, appropriate error result otherwise.
+     */
+    stop2dSession(ISurroundView2dSession sv2dSession) generates (SvResult result);
+
+    /**
+     * Starts a surround view 3d session.
+     *
+     * @result sv3dSession Returns a new 3d session that was created.
+     *         result      Returns OK if successful, appropriate error result otherwise.
+     */
+    start3dSession() generates (ISurroundView3dSession sv3dSession, SvResult result);
+
+    /**
+     * Stops a surround view 2d session.
+     *
+     * @param sv2dSession Valid 2d session to be stopped.
+     * @return svResult  Returns OK if successful, appropriate error result otherwise.
+     */
+    stop3dSession(ISurroundView3dSession sv3dSession) generates (SvResult result);
+};
diff --git a/automotive/sv/1.0/ISurroundViewSession.hal b/automotive/sv/1.0/ISurroundViewSession.hal
new file mode 100644
index 0000000..62cfac0
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewSession.hal
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import ISurroundViewStream;
+
+/**
+ * Common interface for surround view session extended by surround view 2d and 3d
+ * session.
+ */
+interface ISurroundViewSession {
+    /**
+     * Requests to start receiving surround view frames.
+     *
+     * For surround view 3d, setViews() must be set before calling startStream().
+     *
+     * @param stream     Stream to receiving callbacks for the session.
+     * @return svResult  Returns OK if successful, returns VIEW_NOT_SET if setViews() is not
+     *                   called for surround view 3d, appropriate error results otherwise.
+     */
+    startStream(ISurroundViewStream stream) generates (SvResult svResult);
+
+    /**
+     * Requests to stop stream.
+     *
+     * Frames may continue to arrive after call returns. Each must be returned until
+     * the closure of the stream is signaled by the ISurroundViewStream.
+     */
+    stopStream();
+
+    /**
+     * Signal from client that a frame, which was delivered by the stream, has been consumed.
+     *
+     * @param  svFramesDesc Descriptor to signal done with frame.
+     */
+    oneway doneWithFrames(SvFramesDesc svFramesDesc);
+};
diff --git a/automotive/sv/1.0/ISurroundViewStream.hal b/automotive/sv/1.0/ISurroundViewStream.hal
new file mode 100644
index 0000000..22d610f
--- /dev/null
+++ b/automotive/sv/1.0/ISurroundViewStream.hal
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+/**
+ * Interface representing a surround view stream.
+ *
+ * This interface is to be implemented by client to receive callback for output frames and events.
+ */
+interface ISurroundViewStream {
+    /**
+     * Receives callback of surround view 2d/3d frames.
+     *
+     * @param svFramesDesc Frames descriptor containing the output frames.
+     */
+    oneway receiveFrames(SvFramesDesc svFramesDesc);
+
+    /**
+     * Receives callback for surround view events.
+     *
+     * @param svEvent Surround view event.
+    */
+    oneway notify(SvEvent svEvent);
+};
diff --git a/automotive/sv/1.0/default/Android.bp b/automotive/sv/1.0/default/Android.bp
new file mode 100644
index 0000000..8417949
--- /dev/null
+++ b/automotive/sv/1.0/default/Android.bp
@@ -0,0 +1,47 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_binary {
+    name: "android.hardware.automotive.sv@1.0-service",
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "service.cpp",
+        "SurroundViewService.cpp",
+        "SurroundView2dSession.cpp",
+        "SurroundView3dSession.cpp",
+    ],
+    init_rc: ["android.hardware.automotive.sv@1.0-service.rc"],
+    vintf_fragments: ["android.hardware.automotive.sv@1.0-service.xml"],
+    shared_libs: [
+        "android.hardware.automotive.sv@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libbinder",
+        "libcutils",
+        "libhardware",
+        "libhidlbase",
+        "liblog",
+        "libui",
+        "libutils",
+        "libhidlmemory",
+    ],
+
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/automotive/sv/1.0/default/SurroundView2dSession.cpp b/automotive/sv/1.0/default/SurroundView2dSession.cpp
new file mode 100644
index 0000000..4f97598
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView2dSession.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundView2dSession.h"
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+SurroundView2dSession::SurroundView2dSession() :
+    mStreamState(STOPPED) {
+    mEvsCameraIds = {"0" , "1", "2", "3"};
+
+    mConfig.width = 640;
+    mConfig.blending = SvQuality::HIGH;
+
+    framesRecord.frames.svBuffers.resize(1);
+    framesRecord.frames.svBuffers[0].viewId = 0;
+    framesRecord.frames.svBuffers[0].hardwareBuffer.nativeHandle =
+        new native_handle_t();
+    framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] =
+        mConfig.width;
+    framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] =
+        mConfig.width * 3 / 4;
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession
+Return<SvResult> SurroundView2dSession::startStream(
+    const sp<ISurroundViewStream>& stream) {
+    ALOGD("SurroundView2dSession::startStream");
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    if (mStreamState != STOPPED) {
+        ALOGE("ignoring startVideoStream call"
+              "when a stream is already running.");
+        return SvResult::INTERNAL_ERROR;
+    }
+
+    mStream = stream;
+
+    ALOGD("Notify SvEvent::STREAM_STARTED");
+    mStream->notify(SvEvent::STREAM_STARTED);
+
+    // Start the frame generation thread
+    mStreamState = RUNNING;
+    mCaptureThread = std::thread([this](){ generateFrames(); });
+
+    return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::stopStream() {
+    ALOGD("SurroundView2dSession::stopStream");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    if (mStreamState == RUNNING) {
+        // Tell the GenerateFrames loop we want it to stop
+        mStreamState = STOPPING;
+
+        // Block outside the mutex until the "stop" flag has been acknowledged
+        // We won't send any more frames, but the client might still get some
+        // already in flight
+        ALOGD("Waiting for stream thread to end...");
+        lock.unlock();
+        mCaptureThread.join();
+        lock.lock();
+
+        mStreamState = STOPPED;
+        mStream = nullptr;
+        ALOGD("Stream marked STOPPED.");
+    }
+
+    return android::hardware::Void();
+}
+
+Return<void> SurroundView2dSession::doneWithFrames(
+    const SvFramesDesc& svFramesDesc){
+    ALOGD("SurroundView2dSession::doneWithFrames");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    framesRecord.inUse = false;
+
+    (void)svFramesDesc;
+    return android::hardware::Void();
+}
+
+// Methods from ISurroundView2dSession follow.
+Return<void> SurroundView2dSession::get2dMappingInfo(
+    get2dMappingInfo_cb _hidl_cb) {
+    ALOGD("SurroundView2dSession::get2dMappingInfo");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    Sv2dMappingInfo info;
+    info.width = 8; // keeps ratio to 4:3
+    info.height = 6;
+    info.center.isValid = true;
+    info.center.x = 0;
+    info.center.y = 0;
+    _hidl_cb(info);
+    return android::hardware::Void();
+}
+
+Return<SvResult> SurroundView2dSession::set2dConfig(
+    const Sv2dConfig& sv2dConfig) {
+    ALOGD("SurroundView2dSession::setConfig");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    mConfig.width = sv2dConfig.width;
+    mConfig.blending = sv2dConfig.blending;
+    ALOGD("Notify SvEvent::CONFIG_UPDATED");
+    mStream->notify(SvEvent::CONFIG_UPDATED);
+
+    return SvResult::OK;
+}
+
+Return<void> SurroundView2dSession::get2dConfig(get2dConfig_cb _hidl_cb) {
+    ALOGD("SurroundView2dSession::getConfig");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    _hidl_cb(mConfig);
+    return android::hardware::Void();
+}
+
+Return<void> SurroundView2dSession::projectCameraPoints(
+        const hidl_vec<Point2dInt>& points2dCamera,
+        const hidl_string& cameraId,
+        projectCameraPoints_cb _hidl_cb) {
+    ALOGD("SurroundView2dSession::projectCameraPoints");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    bool cameraIdFound = false;
+    for (auto evsCameraId : mEvsCameraIds) {
+      if (cameraId == evsCameraId) {
+          cameraIdFound = true;
+          ALOGI("Camera id found.");
+          break;
+      }
+    }
+
+    if (!cameraIdFound) {
+        ALOGE("Camera id not found.");
+        _hidl_cb(hidl_vec<Point2dFloat>());
+        return android::hardware::Void();
+    }
+
+    hidl_vec<Point2dFloat> outPoints;
+    outPoints.resize(points2dCamera.size());
+
+    int width = mConfig.width;
+    int height = mConfig.width * 3 / 4;
+    for (int i=0; i<points2dCamera.size(); i++) {
+        // Assuming all the points in the image frame can be projected into 2d
+        // Surround View space. Otherwise cannot.
+        if (points2dCamera[i].x < 0 || points2dCamera[i].y > width-1 ||
+            points2dCamera[i].x < 0 || points2dCamera[i].y > height-1) {
+            ALOGW("SurroundView2dSession::projectCameraPoints "
+                  "gets invalid 2d camera points. Ignored");
+            outPoints[i].isValid = false;
+            outPoints[i].x = 10000;
+            outPoints[i].y = 10000;
+        } else {
+            outPoints[i].isValid = true;
+            outPoints[i].x = 0;
+            outPoints[i].y = 0;
+        }
+    }
+
+    _hidl_cb(outPoints);
+    return android::hardware::Void();
+}
+
+void SurroundView2dSession::generateFrames() {
+    ALOGD("SurroundView2dSession::generateFrames");
+
+    int sequenceId = 0;
+
+    while(true) {
+        {
+            std::lock_guard<std::mutex> lock(mAccessLock);
+
+            if (mStreamState != RUNNING) {
+                // Break out of our main thread loop
+                break;
+            }
+
+            framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] =
+                mConfig.width;
+            framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] =
+                mConfig.width * 3 / 4;
+        }
+
+        usleep(100 * 1000);
+
+        framesRecord.frames.timestampNs = elapsedRealtimeNano();
+        framesRecord.frames.sequenceId = sequenceId++;
+
+        {
+            std::lock_guard<std::mutex> lock(mAccessLock);
+
+            if (framesRecord.inUse) {
+                ALOGD("Notify SvEvent::FRAME_DROPPED");
+                mStream->notify(SvEvent::FRAME_DROPPED);
+            } else {
+                framesRecord.inUse = true;
+                mStream->receiveFrames(framesRecord.frames);
+            }
+        }
+    }
+
+    // If we've been asked to stop, send an event to signal the actual
+    // end of stream
+    ALOGD("Notify SvEvent::STREAM_STOPPED");
+    mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView2dSession.h b/automotive/sv/1.0/default/SurroundView2dSession.h
new file mode 100644
index 0000000..ee751e7
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView2dSession.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <thread>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+using ::std::mutex;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView2dSession : public ISurroundView2dSession {
+public:
+    SurroundView2dSession();
+
+    // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+    Return<SvResult> startStream(
+        const sp<ISurroundViewStream>& stream) override;
+    Return<void> stopStream() override;
+    Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+    // Methods from ISurroundView2dSession follow.
+    Return<void> get2dMappingInfo(get2dMappingInfo_cb _hidl_cb) override;
+    Return<SvResult> set2dConfig(const Sv2dConfig& sv2dConfig) override;
+    Return<void> get2dConfig(get2dConfig_cb _hidl_cb) override;
+    Return<void> projectCameraPoints(
+        const hidl_vec<Point2dInt>& points2dCamera,
+        const hidl_string& cameraId,
+        projectCameraPoints_cb _hidl_cb) override;
+
+    // TODO(tanmayp): Make private and add set/get method.
+    // Stream subscribed for the session.
+    sp<ISurroundViewStream> mStream;
+
+private:
+    void generateFrames();
+
+    enum StreamStateValues {
+        STOPPED,
+        RUNNING,
+        STOPPING,
+        DEAD,
+    };
+    StreamStateValues mStreamState;
+
+    Sv2dConfig mConfig;
+
+    std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+    struct FramesRecord {
+        SvFramesDesc frames;
+        bool inUse = false;
+    };
+
+    FramesRecord framesRecord;
+
+    // Synchronization necessary to deconflict mCaptureThread from the main service thread
+    std::mutex mAccessLock;
+
+    std::vector<std::string> mEvsCameraIds;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView3dSession.cpp b/automotive/sv/1.0/default/SurroundView3dSession.cpp
new file mode 100644
index 0000000..da36f32
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView3dSession.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundView3dSession.h"
+
+#include <set>
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::hidl_memory;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+SurroundView3dSession::SurroundView3dSession() :
+    mStreamState(STOPPED){
+
+    mEvsCameraIds = {"0" , "1", "2", "3"};
+
+    mConfig.width = 640;
+    mConfig.height = 480;
+    mConfig.carDetails = SvQuality::HIGH;
+
+    framesRecord.frames.svBuffers.resize(1);
+    framesRecord.frames.svBuffers[0].viewId = 0;
+    framesRecord.frames.svBuffers[0].hardwareBuffer.nativeHandle = new native_handle_t();
+    framesRecord.frames.svBuffers[0].hardwareBuffer.description[0] = mConfig.width;
+    framesRecord.frames.svBuffers[0].hardwareBuffer.description[1] = mConfig.height;
+}
+
+// Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+Return<SvResult> SurroundView3dSession::startStream(
+    const sp<ISurroundViewStream>& stream) {
+    ALOGD("SurroundView3dSession::startStream");
+    std::lock_guard<std::mutex> lock(mAccessLock);
+
+    if (mStreamState != STOPPED) {
+        ALOGE("ignoring startVideoStream call when a stream is already running.");
+        return SvResult::INTERNAL_ERROR;
+    }
+
+    if (mViews.empty()) {
+        ALOGE("No views have been set for current Surround View 3d Session. "
+              "Please call setViews before starting the stream.");
+        return SvResult::VIEW_NOT_SET;
+    }
+
+    mStream = stream;
+
+    ALOGD("Notify SvEvent::STREAM_STARTED");
+    mStream->notify(SvEvent::STREAM_STARTED);
+
+    // Start the frame generation thread
+    mStreamState = RUNNING;
+    mCaptureThread = std::thread([this](){ generateFrames(); });
+
+    return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::stopStream() {
+    ALOGD("SurroundView3dSession::stopStream");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    if (mStreamState == RUNNING) {
+        // Tell the GenerateFrames loop we want it to stop
+        mStreamState = STOPPING;
+
+        // Block outside the mutex until the "stop" flag has been acknowledged
+        // We won't send any more frames, but the client might still get some already in flight
+        ALOGD("Waiting for stream thread to end...");
+        lock.unlock();
+        mCaptureThread.join();
+        lock.lock();
+
+        mStreamState = STOPPED;
+        mStream = nullptr;
+        ALOGD("Stream marked STOPPED.");
+    }
+
+    return android::hardware::Void();
+}
+
+Return<void> SurroundView3dSession::doneWithFrames(
+    const SvFramesDesc& svFramesDesc){
+    ALOGD("SurroundView3dSession::doneWithFrames");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    framesRecord.inUse = false;
+
+    (void)svFramesDesc;
+    return android::hardware::Void();
+}
+
+// Methods from ISurroundView3dSession follow.
+Return<SvResult> SurroundView3dSession::setViews(const hidl_vec<View3d>& views) {
+    ALOGD("SurroundView3dSession::stopStream");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    mViews.resize(views.size());
+    for (int i=0; i<views.size(); i++) {
+        mViews[i] = views[i];
+    }
+
+    return SvResult::OK;
+}
+
+Return<SvResult> SurroundView3dSession::set3dConfig(const Sv3dConfig& sv3dConfig) {
+    ALOGD("SurroundView3dSession::set3dConfig");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    mConfig.width = sv3dConfig.width;
+    mConfig.height = sv3dConfig.height;
+    mConfig.carDetails = sv3dConfig.carDetails;
+    ALOGD("Notify SvEvent::CONFIG_UPDATED");
+    mStream->notify(SvEvent::CONFIG_UPDATED);
+
+    return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::get3dConfig(get3dConfig_cb _hidl_cb) {
+    ALOGD("SurroundView3dSession::get3dConfig");
+    std::unique_lock <std::mutex> lock(mAccessLock);
+
+    _hidl_cb(mConfig);
+    return android::hardware::Void();
+}
+
+bool VerifyOverlayData(const OverlaysData& overlaysData) {
+    // Check size of shared memory matches overlaysMemoryDesc.
+    const int kVertexSize = 16;
+    const int kIdSize = 2;
+    int memDescSize = 0;
+    for (auto overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+        memDescSize += kIdSize + kVertexSize * overlayMemDesc.verticesCount;
+    }
+    if (memDescSize != overlaysData.overlaysMemory.size()) {
+        ALOGE("shared memory and overlaysMemoryDesc size mismatch.");
+        return false;
+    }
+
+    // Map memory.
+    sp<IMemory> pSharedMemory = mapMemory(overlaysData.overlaysMemory);
+    if(pSharedMemory.get() == nullptr) {
+        ALOGE("mapMemory failed.");
+        return false;
+    }
+
+    // Get Data pointer.
+    uint8_t* pData = (uint8_t*)((void*)pSharedMemory->getPointer());
+    if (pData == nullptr) {
+        ALOGE("Shared memory getPointer() failed.");
+        return false;
+    }
+
+    int idOffset = 0;
+    std::set<uint16_t> overlayIdSet;
+    for (auto overlayMemDesc : overlaysData.overlaysMemoryDesc) {
+
+        if (overlayIdSet.find(overlayMemDesc.id) != overlayIdSet.end()) {
+            ALOGE("Duplicate id within memory descriptor.");
+            return false;
+        }
+        overlayIdSet.insert(overlayMemDesc.id);
+
+        if(overlayMemDesc.verticesCount < 3) {
+            ALOGE("Less than 3 vertices.");
+            return false;
+        }
+
+        if (overlayMemDesc.overlayPrimitive == OverlayPrimitive::TRIANGLES &&
+                overlayMemDesc.verticesCount % 3 != 0) {
+            ALOGE("Triangles primitive does not have vertices multiple of 3.");
+            return false;
+        }
+
+        uint16_t overlayId = *((uint16_t*)(pData + idOffset));
+
+        if (overlayId != overlayMemDesc.id) {
+            ALOGE("Overlay id mismatch %d , %d", overlayId, overlayMemDesc.id);
+            return false;
+        }
+
+        idOffset += kIdSize + (kVertexSize * overlayMemDesc.verticesCount);
+    }
+
+    return true;
+}
+
+Return<SvResult>  SurroundView3dSession::updateOverlays(
+        const OverlaysData& overlaysData) {
+
+    if(!VerifyOverlayData(overlaysData)) {
+        ALOGE("VerifyOverlayData failed.");
+        return SvResult::INVALID_ARG;
+    }
+
+    return SvResult::OK;
+}
+
+Return<void> SurroundView3dSession::projectCameraPointsTo3dSurface(
+    const hidl_vec<Point2dInt>& cameraPoints,
+    const hidl_string& cameraId,
+    projectCameraPointsTo3dSurface_cb _hidl_cb) {
+
+    std::vector<Point3dFloat> points3d;
+    bool cameraIdFound = false;
+    for (auto evsCameraId : mEvsCameraIds) {
+      if (cameraId == evsCameraId) {
+          cameraIdFound = true;
+          ALOGI("Camera id found.");
+          break;
+      }
+    }
+
+    if (!cameraIdFound) {
+        ALOGE("Camera id not found.");
+        _hidl_cb(points3d);
+        return android::hardware::Void();
+    }
+
+    for (const auto cameraPoint : cameraPoints) {
+        Point3dFloat point3d;
+        point3d.isValid = true;
+
+        if (cameraPoint.x < 0 || cameraPoint.x >= mConfig.width-1 ||
+                cameraPoint.y < 0 || cameraPoint.y >= mConfig.height-1) {
+            ALOGE("Camera point out of bounds.");
+            point3d.isValid = false;
+        }
+        points3d.push_back(point3d);
+    }
+    _hidl_cb(points3d);
+    return android::hardware::Void();
+}
+
+void SurroundView3dSession::generateFrames() {
+    ALOGD("SurroundView3dSession::generateFrames");
+
+    int sequenceId = 0;
+
+    while(true) {
+        {
+            std::lock_guard<std::mutex> lock(mAccessLock);
+
+            if (mStreamState != RUNNING) {
+                // Break out of our main thread loop
+                break;
+            }
+        }
+
+        usleep(100 * 1000);
+
+        framesRecord.frames.timestampNs = elapsedRealtimeNano();
+        framesRecord.frames.sequenceId = sequenceId++;
+
+        framesRecord.frames.svBuffers.resize(mViews.size());
+        for (int i=0; i<mViews.size(); i++) {
+            framesRecord.frames.svBuffers[i].viewId = mViews[i].viewId;
+            framesRecord.frames.svBuffers[i].hardwareBuffer.nativeHandle = new native_handle_t();
+            framesRecord.frames.svBuffers[i].hardwareBuffer.description[0] = mConfig.width; // width
+            framesRecord.frames.svBuffers[i].hardwareBuffer.description[1] = mConfig.height; // height
+        }
+
+        {
+            std::lock_guard<std::mutex> lock(mAccessLock);
+
+            if (framesRecord.inUse) {
+                ALOGD("Notify SvEvent::FRAME_DROPPED");
+                mStream->notify(SvEvent::FRAME_DROPPED);
+            } else {
+                framesRecord.inUse = true;
+                mStream->receiveFrames(framesRecord.frames);
+            }
+        }
+    }
+
+    // If we've been asked to stop, send an event to signal the actual end of stream
+    ALOGD("Notify SvEvent::STREAM_STOPPED");
+    mStream->notify(SvEvent::STREAM_STOPPED);
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundView3dSession.h b/automotive/sv/1.0/default/SurroundView3dSession.h
new file mode 100644
index 0000000..5c638db
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundView3dSession.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <thread>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::hidl_vec;
+using ::android::sp;
+using ::std::mutex;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundView3dSession : public ISurroundView3dSession {
+public:
+    SurroundView3dSession();
+
+    // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewSession.
+    Return<SvResult> startStream(
+        const sp<ISurroundViewStream>& stream) override;
+    Return<void> stopStream() override;
+    Return<void> doneWithFrames(const SvFramesDesc& svFramesDesc) override;
+
+    // Methods from ISurroundView3dSession follow.
+    Return<SvResult> setViews(const hidl_vec<View3d>& views) override;
+    Return<SvResult> set3dConfig(const Sv3dConfig& sv3dConfig) override;
+    Return<void> get3dConfig(get3dConfig_cb _hidl_cb) override;
+    Return<SvResult>  updateOverlays(const OverlaysData& overlaysData);
+    Return<void> projectCameraPointsTo3dSurface(
+        const hidl_vec<Point2dInt>& cameraPoints,
+        const hidl_string& cameraId,
+        projectCameraPointsTo3dSurface_cb _hidl_cb);
+
+    // Stream subscribed for the session.
+    // TODO(tanmayp): Make private and add set/get method.
+    sp<ISurroundViewStream> mStream;
+
+private:
+    void generateFrames();
+
+    enum StreamStateValues {
+        STOPPED,
+        RUNNING,
+        STOPPING,
+        DEAD,
+    };
+    StreamStateValues mStreamState;
+
+    std::thread mCaptureThread; // The thread we'll use to synthesize frames
+
+    struct FramesRecord {
+        SvFramesDesc frames;
+        bool inUse = false;
+    };
+
+    FramesRecord framesRecord;
+
+    // Synchronization necessary to deconflict mCaptureThread from the main service thread
+    std::mutex mAccessLock;
+
+    std::vector<View3d> mViews;
+
+    Sv3dConfig mConfig;
+
+    std::vector<std::string> mEvsCameraIds;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/sv/1.0/default/SurroundViewService.cpp b/automotive/sv/1.0/default/SurroundViewService.cpp
new file mode 100644
index 0000000..fe89dd5
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundViewService.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundViewService.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+const std::string kCameraIds[] = {"0", "1", "2", "3"};
+
+Return<void> SurroundViewService::getCameraIds(getCameraIds_cb _hidl_cb) {
+  std::vector<hidl_string> cameraIds = {kCameraIds[0], kCameraIds[1],
+          kCameraIds[2], kCameraIds[3]};
+  _hidl_cb(cameraIds);
+  return android::hardware::Void();
+}
+
+Return<void> SurroundViewService::start2dSession(start2dSession_cb _hidl_cb) {
+    ALOGD("SurroundViewService::start2dSession");
+    if (mSurroundView2dSession != nullptr) {
+        ALOGW("Only one 2d session is supported at the same time");
+        _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+    } else {
+        mSurroundView2dSession = new SurroundView2dSession();
+        _hidl_cb(mSurroundView2dSession, SvResult::OK);
+    }
+    return android::hardware::Void();
+}
+
+Return<SvResult> SurroundViewService::stop2dSession(
+    const sp<ISurroundView2dSession>& sv2dSession) {
+    ALOGD("SurroundViewService::stop2dSession");
+    if (sv2dSession != nullptr && sv2dSession == mSurroundView2dSession) {
+        mSurroundView2dSession = nullptr;
+        return SvResult::OK;
+    } else {
+        ALOGE("Invalid arg for stop2dSession");
+        return SvResult::INVALID_ARG;
+    }
+}
+
+Return<void> SurroundViewService::start3dSession(start3dSession_cb _hidl_cb) {
+    ALOGD("SurroundViewService::start3dSession");
+    if (mSurroundView3dSession != nullptr) {
+        ALOGW("Only one 3d session is supported at the same time");
+        _hidl_cb(nullptr, SvResult::INTERNAL_ERROR);
+    } else {
+        mSurroundView3dSession = new SurroundView3dSession();
+        _hidl_cb(mSurroundView3dSession, SvResult::OK);
+    }
+    return android::hardware::Void();
+}
+
+Return<SvResult> SurroundViewService::stop3dSession(
+    const sp<ISurroundView3dSession>& sv3dSession) {
+    ALOGD("SurroundViewService::stop3dSession");
+    if (sv3dSession != nullptr && sv3dSession == mSurroundView3dSession) {
+        mSurroundView3dSession = nullptr;
+        return SvResult::OK;
+    } else {
+        ALOGE("Invalid arg for stop3dSession");
+        return SvResult::INVALID_ARG;
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
+
diff --git a/automotive/sv/1.0/default/SurroundViewService.h b/automotive/sv/1.0/default/SurroundViewService.h
new file mode 100644
index 0000000..9e0e151
--- /dev/null
+++ b/automotive/sv/1.0/default/SurroundViewService.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include "SurroundView2dSession.h"
+#include "SurroundView3dSession.h"
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+namespace android {
+namespace hardware {
+namespace automotive {
+namespace sv {
+namespace V1_0 {
+namespace implementation {
+
+class SurroundViewService : public ISurroundViewService {
+public:
+    // Methods from ::android::hardware::automotive::sv::V1_0::ISurroundViewService follow.
+    Return<void> getCameraIds(getCameraIds_cb _hidl_cb) override;
+    Return<void> start2dSession(start2dSession_cb _hidl_cb) override;
+    Return<SvResult> stop2dSession(
+        const sp<ISurroundView2dSession>& sv2dSession) override;
+
+    Return<void> start3dSession(start3dSession_cb _hidl_cb) override;
+    Return<SvResult> stop3dSession(
+        const sp<ISurroundView3dSession>& sv3dSession) override;
+
+private:
+    sp<SurroundView2dSession> mSurroundView2dSession;
+    sp<SurroundView3dSession> mSurroundView3dSession;
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace sv
+}  // namespace automotive
+}  // namespace hardware
+}  // namespace android
diff --git a/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc
new file mode 100644
index 0000000..e822017
--- /dev/null
+++ b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.rc
@@ -0,0 +1,5 @@
+service sv_service /vendor/bin/hw/android.hardware.automotive.sv@1.0-service
+    class hal
+    user automotive_evs
+    group automotive_evs
+    disabled
diff --git a/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml
new file mode 100644
index 0000000..ba8e5ac
--- /dev/null
+++ b/automotive/sv/1.0/default/android.hardware.automotive.sv@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.automotive.sv</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>ISurroundViewService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/automotive/sv/1.0/default/service.cpp b/automotive/sv/1.0/default/service.cpp
new file mode 100644
index 0000000..fae7425
--- /dev/null
+++ b/automotive/sv/1.0/default/service.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "android.hardware.automotive.sv@1.0-service"
+
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware_buffer.h>
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+#include <thread>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <utils/SystemClock.h>
+
+#include "SurroundViewService.h"
+
+// libhidl:
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+
+// implementation:
+using android::hardware::automotive::sv::V1_0::implementation::SurroundViewService;
+
+int main() {
+    ALOGI("ISurroundViewService default implementation is starting");
+    android::sp<ISurroundViewService> service = new SurroundViewService();
+
+    configureRpcThreadpool(1, true /* callerWillJoin */);
+
+    // Register our service -- if somebody is already registered by our name,
+    // they will be killed (their thread pool will throw an exception).
+    android::status_t status = service->registerAsService();
+
+    LOG_ALWAYS_FATAL_IF(status != android::OK,
+                        "Could not register default Surround View Service (%d)",
+                        status);
+
+    joinRpcThreadpool();
+
+    // In normal operation, we don't expect the thread pool to exit
+    ALOGE("Surround View Service is shutting down");
+    return 1;
+}
diff --git a/automotive/sv/1.0/types.hal b/automotive/sv/1.0/types.hal
new file mode 100644
index 0000000..573bf11
--- /dev/null
+++ b/automotive/sv/1.0/types.hal
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.sv@1.0;
+
+import android.hardware.graphics.common@1.2::HardwareBuffer;
+
+/** Structure for translation with x, y and z units. */
+struct Translation {
+    float x;
+    float y;
+    float z;
+};
+
+/**
+ * Structure for rotation expressed as quaternions.
+ * Convention used: Unit quaternion with hamilton convention.
+ */
+struct RotationQuat {
+    float x;
+    float y;
+    float z;
+    float w;
+};
+
+/** Structure representing a 2D point with integers. Units are pixels. */
+struct Point2dInt {
+    uint32_t x;
+    uint32_t y;
+};
+
+/** Structure representing a 2D point with floats. */
+struct Point2dFloat {
+    /** Boolean flag to indicate the (x, y) data is valid. */
+    bool isValid;
+
+    /** (x, y) data is only valid if isValid is true. Units are pixels or milli-meters. */
+    float x;
+    float y;
+};
+
+/** Structure representing a 3D point with floats. */
+struct Point3dFloat {
+    /** Boolean flag to indicate the (x, y, z) data is valid. */
+    bool isValid;
+
+    /**
+     * (x, y, z) data is only valid if isValid is true. Units are milli-meters.
+     */
+    float x;
+    float y;
+    float z;
+};
+
+/**
+ * Structure defining the pose in 3D space.
+ */
+struct Pose {
+    /**
+     * Rotation part of the pose, expressed as a unit quaternion.
+     */
+    RotationQuat rotation;
+
+    /**
+     * Translation part of the pose, in (x, y, z) format with milli-meter units.
+     */
+    Translation translation;
+};
+
+/**
+ * Struct defining a virtual view in the 3d space around the car.
+ */
+struct View3d {
+    /**
+     * Id to identify each custom view, this is passed along in each result SvBuffer.
+     * Recommend client to have a unique id for each different view.
+     */
+    uint32_t viewId;
+
+    /**
+     * Pose of the view. Describes the orientation and location of a virtual view relative to the
+     * android automotive coordinate system:
+     * https://source.android.com/devices/sensors/sensor-types#auto_axes
+     * The virtual view axes are defined as +Y as look-at direction, +X as right direction and
+     * +Z as up direction.
+     * The rotation and translation of the virtual view axes w.r.t the android automotive axes is
+     * specified by the rotation and tranlation component of the pose respectively.
+     * Example: A virtual view points to the right face of the car, located on right side of
+     * the car at (4, 2, 0) and is upright w.r.t the ground :
+     *         ______
+     *  front |      |
+     *        | car  |    ↑X
+     *        |  ↑Y  |  Y←∘  view
+     *  rear  |  ∘→X |  (4,2)
+     *        |(0,0) |
+     *        |______|
+     *
+     * Here the view axes are rotated by 90 counter-clockwise w.r.t android automotive axes.
+     * For this example the rotation and translation will be:
+     * Rotation = + 90 degrees around Z axis = (0.7071, 0, 0, 0.7071) as a unit quaternion.
+     * Translation = (4, 2, 0) in meters = (2000, 4000, 0) in milli-meters.
+     */
+    Pose pose;
+
+    /**
+     * Horizontal field of view of the virtual view in degrees. Vertical fov is scaled accordingly
+     * to maintain the aspect ratio of the output frame. Must be in range (20,
+     */
+    float horizontalFov;
+};
+
+/**
+ * Memory Buffer that stores the output of a single view from 2d/3d surround view.
+ */
+struct SvBuffer {
+    /**
+     * viewId identifying the view as passed by the client in setViews() call for
+     * surround view 3d. Id value is 0 for 2d surround view frame.
+     */
+    uint32_t viewId;
+
+    /** Hardware buffer containing the surround view 2d/3d result. */
+    HardwareBuffer hardwareBuffer;
+};
+
+/**
+ * Structure describing a set of frames to be returned as output from 2d/3d surround view.
+ */
+struct SvFramesDesc {
+    /**
+     * Elapsed real-time nanoseconds of earliest camera frame from the set of camera
+     * frames used to generate the view.
+     */
+    uint64_t timestampNs;
+
+    /**
+     * Incremental counter for client to keep track of frames.
+     */
+    uint32_t sequenceId;
+
+    /**
+     * Frames generated with different views.
+     * 2d surround view has only a single svBuffer with Id 0.
+     */
+    vec<SvBuffer> svBuffers;
+};
+
+/**
+ * Enumerator for list of result returns by surround view .
+ */
+enum SvResult : uint32_t {
+    /** Operation was successful. */
+    OK = 0,
+
+    /** Invalid argument to function was provided. */
+    INVALID_ARG,
+
+    /** Error indicating the particular operation is not supported. */
+    NOT_SUPPORTED,
+
+    /** Error indicating view not set before starting stream. */
+    VIEW_NOT_SET,
+
+    /**
+     * Error indicating system does not currently have enough resources to
+     * allocate for a new requested session.
+     * Clients may retry request for session if resources become available.
+     */
+    NO_RESOURCE,
+
+    /** Internal error in surround view service. */
+    INTERNAL_ERROR,
+};
+
+/**
+ * Enumerator listing events for surround view.
+ */
+enum SvEvent : uint32_t {
+    STREAM_STARTED = 1,
+
+    STREAM_STOPPED,
+
+    /**
+     * Event sent after service switches to an updated config, all frames
+     * streamed after this event are of the updated config.
+     */
+    CONFIG_UPDATED,
+
+    /** Each frame dropped will be notified with this event. */
+    FRAME_DROPPED,
+
+    /**
+     * Timeout event occurs if any individual camera stream has a timeout.
+     * Frames will not be delivered and clients must stop the stream.
+     */
+    TIMEOUT,
+};
+
+/**
+ * Structure defining the mapping information for 2d surround view.
+ *
+ * Mapping information provides the area on ground (width and height) and
+ * position w.r.t the car that the surround view 2d covers. This can be used for
+ * mapping (linear transformation) with other sensors whose data is available in
+ * the car coordinate system (eg. Ultrasonics).
+ * Axes and origin are as per the android automotive axes:
+ * https://source.android.com/devices/sensors/sensor-types#auto_axes
+ */
+struct Sv2dMappingInfo {
+    /** Width in milli-meters of the 2d surround view along the ground plane. */
+    float width;
+
+    /** Height in milli-meters of the 2d surround view along the ground plane. */
+    float height;
+
+    /**
+     * Coordinates (x, y) of the center of the view in android automotive coordinate system on the
+     * ground plane. Units are milli-meters.
+     */
+    Point2dFloat center;
+};
+
+/**
+ * Enumerator for quality presets for 2d/3d surround view.
+ * Details of each preset are specified in the respective 2d/3d config structures.
+ */
+enum SvQuality : uint32_t {
+    HIGH = 0,
+    LOW,
+};
+
+/** Structure for surround view 2d configuration. */
+struct Sv2dConfig {
+    /**
+     * Desired output width in pixels. Must be in range (0, 4096].
+     * Height is computed keeping the aspect ratio of the mapping info,
+     * Example: If width = 1080 px and mapping_width = 5000 mm, mapping_height = 10000 mm.
+     *          then, height = width * (mapping_height / mapping_width) = 2160 px.
+     * Height is set to the floor value in case of (mapping_height / mapping_width) is not integer.
+     * Mapping width, height is fixed for a car and is based on camera parameters and their ground
+     * coverage.
+     */
+    uint32_t width;
+
+    /**
+     * Blending quality preset to use.
+     * HIGH: High quality blending (eg. multiband blending) that consumes more resources.
+     * LOW: Low quality blending (eg. alpha blending) that consumes less resources.
+     */
+    SvQuality blending;
+};
+
+/** Structure for surround view 3d configuration. */
+struct Sv3dConfig {
+    /** Desired output width in pixels. Must be in range (0, 4096]. */
+    uint32_t width;
+
+    /** Desired output height in pixels. Must be in range (0, 4096]. */
+    uint32_t height;
+
+    /**
+     * Car model rendering details level.
+     * HIGH: Rendering includes shadows and reflections. Default option.
+     * LOW: Rendering with no shadows and reflections.
+     */
+    SvQuality carDetails;
+};
+
+/**
+ * Enumerator for a list of overlay primitives.
+ *
+ * Given a list of vertices for an overlay, a primitive type defines which vertices are used to form
+ * the surfaces of the overlay object.
+ */
+enum OverlayPrimitive : uint32_t {
+    /**
+     * Consecutive vertices picked in order 3 at a time form a triangle.
+     * Eg: In a list of vertices (V1, V2, V3, V4, V5, V6)
+     * (V1, V2, V3) form a triangle and (V4, V5, V6) form a triangle.
+     */
+    TRIANGLES = 0,
+
+    /**
+     * Every 3 consecutive vertices form a triangle.
+     * Example in a list of vertices V1, V2, V3, V4, V5, V6
+     * (V1, V2, V3), (V2, V3, V4), (V3, V4, V5) and (V4, V5, V6) form triangles.
+     */
+    TRIANGLES_STRIP,
+};
+
+/**
+ * Structure identifying an overlay and describing the size and arrangement of its data in
+ * shared memory.
+ */
+struct OverlayMemoryDesc {
+    /** Identifier of the overlay. */
+    uint16_t id;
+
+    /** Number of vertices in the overlay. */
+    uint32_t verticesCount;
+
+    /** Primitive for the overlay. */
+    OverlayPrimitive overlayPrimitive;
+};
+
+/**
+ *  Structure containing the overlays data in shared memory.
+ */
+struct OverlaysData {
+    /** List of overlay memory descriptors, describing the data in the shared memory */
+    vec<OverlayMemoryDesc> overlaysMemoryDesc;
+
+    /**
+     * Shared memory object containing a list of vertices for each overlay as described by
+     * overlaysMemoryDesc.
+     *
+     * Each vertex comprises of:
+     * | PositionX | PositionY | PositionZ |   RGBA       |
+     * |   float   |   float   |   float   |  4 * uint8_t |
+     *
+     * Each vertex is of 3 floats and 4 bytes = 16 bytes.
+     *
+     * Layout of vertices in shared memory is in order:
+     *
+     * Bytes: | 0-1 | 2-18 | 19-34 | 35-50 | 51-66 | 67-68 | 69-84 | 85-100 | 101-116  |...
+     * Data:  | id1 |  V1  |  V2   |   V3  |   V4  |  id2  |   V1  |   V2   |  V3      |...
+     *        |           overlay1                 |          overlay 2                |
+     *
+     * The order of overlays must match the order as specified in the overlaysMemoryDesc.
+     * The number of vertices each overlay has must match the verticesCount in overlaysMemoryDesc.
+     * The id must match the id specificed in the OverlayMemoryDesc. This is used for verification.
+     * For each overlay the number of vertices must be 3 or greater.
+     * For TRIANGLES primitive the number of vertices must be a multiple of 3.
+     * The overlay vertices are grouped as per the overlayPrimitive specified in overlaysMemoryDesc,
+     * eg: If primitive is TRIANGLES, (V1, V2, V3) and (V4, V5, V6) form a triangle.
+     */
+    memory overlaysMemory;
+};
diff --git a/automotive/sv/1.0/vts/functional/Android.bp b/automotive/sv/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..0e5d3df
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+    name: "VtsHalSurroundViewV1_0TargetTest",
+    srcs: [
+        "VtsHalSurroundViewV1_0TargetTest.cpp",
+        "SurroundViewStreamHandler.cpp",
+    ],
+    defaults: ["VtsHalTargetTestDefaults"],
+    static_libs: [
+        "libnativewindow",
+        "android.hardware.automotive.sv@1.0",
+        "android.hardware.graphics.common@1.0",
+        "android.hardware.graphics.common@1.1",
+        "android.hardware.graphics.common@1.2",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlmemory",
+    ],
+    test_suites: ["general-tests", "vts-core"],
+    cflags: [
+        "-O0",
+        "-g",
+    ],
+}
diff --git a/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp
new file mode 100644
index 0000000..cb45caa
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SurroundViewStreamHandler.h"
+
+#include <utils/Log.h>
+
+using std::lock_guard;
+
+SurroundViewServiceHandler::SurroundViewServiceHandler(sp<ISurroundViewSession> pSession) :
+    mSession(pSession),
+    mReceiveFramesCount(0),
+    mDoNotReturnFrames(false) {
+    // Nothing but member initialization
+}
+
+Return<void> SurroundViewServiceHandler::notify(SvEvent svEvent) {
+    ALOGD("SurroundViewServiceHandler::notify %d", svEvent);
+
+    lock_guard<mutex> lock(mLock);
+    switch (svEvent) {
+        case SvEvent::STREAM_STARTED:
+        case SvEvent::CONFIG_UPDATED:
+        case SvEvent::STREAM_STOPPED:
+        case SvEvent::FRAME_DROPPED:
+        case SvEvent::TIMEOUT:
+            mReceivedEvents.emplace_back(svEvent);
+            break;
+        default:
+            ALOGI("[SurroundViewLog] Received other event");
+    }
+
+    return android::hardware::Void();
+}
+
+Return<void> SurroundViewServiceHandler::receiveFrames(const SvFramesDesc& svFramesDesc) {
+    ALOGD("SurroundViewServiceHandler::receiveFrames");
+
+    lock_guard<mutex> lock(mLock);
+    unsigned long timestampNs = svFramesDesc.timestampNs;
+    unsigned sequenceId = svFramesDesc.sequenceId;
+    ALOGD("receiveFrames count: %d", mReceiveFramesCount);
+    ALOGD("timestampNs: %lu, sequenceId: %u", timestampNs, sequenceId);
+    if (mReceiveFramesCount != 0
+        && (mLastReceivedFrames.timestampNs >= svFramesDesc.timestampNs
+            || mLastReceivedFrames.sequenceId >= svFramesDesc.sequenceId)) {
+        mAllFramesValid = false;
+        ALOGD("The incoming frames are with invalid timestamp or sequenceId!");
+    }
+
+    for (int i=0; i<svFramesDesc.svBuffers.size(); i++) {
+        if (svFramesDesc.svBuffers[i].hardwareBuffer.nativeHandle == nullptr) {
+            mAllFramesValid = false;
+            ALOGD("The incoming frames are with invalid nativeHandle!");
+            break;
+        }
+    }
+
+    mReceiveFramesCount++;
+
+    // Store all the information except for the handle
+    mLastReceivedFrames.timestampNs = svFramesDesc.timestampNs;
+    mLastReceivedFrames.sequenceId = svFramesDesc.sequenceId;
+    mLastReceivedFrames.svBuffers.resize(svFramesDesc.svBuffers.size());
+    for (int i=0; i<svFramesDesc.svBuffers.size(); i++) {
+        mLastReceivedFrames.svBuffers[i].viewId = svFramesDesc.svBuffers[i].viewId;
+        mLastReceivedFrames.svBuffers[i].hardwareBuffer.description =
+            svFramesDesc.svBuffers[i].hardwareBuffer.description;
+    }
+
+    if (!mDoNotReturnFrames) {
+        mSession->doneWithFrames(svFramesDesc);
+    }
+
+    return android::hardware::Void();
+}
+
+bool SurroundViewServiceHandler::checkEventReceived(SvEvent svEvent) {
+    ALOGD("SurroundViewServiceHandler::checkEventReceived");
+    int size = mReceivedEvents.size(); // work around
+    ALOGD("Received event number: %d", size);
+    auto iter = find(mReceivedEvents.begin(), mReceivedEvents.end(), svEvent);
+    return iter != mReceivedEvents.end();
+}
+
+SvFramesDesc SurroundViewServiceHandler::getLastReceivedFrames() {
+    return mLastReceivedFrames;
+}
+
+int SurroundViewServiceHandler::getReceiveFramesCount() {
+    return mReceiveFramesCount;
+}
+
+bool SurroundViewServiceHandler::areAllFramesValid() {
+    return mAllFramesValid;
+}
+
+void SurroundViewServiceHandler::setDoNotReturnFrames(bool flag) {
+    mDoNotReturnFrames = flag;
+}
diff --git a/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h
new file mode 100644
index 0000000..7d3f61d
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/SurroundViewStreamHandler.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SURROUND_VIEW_STREAM_HANDLER_H
+#define SURROUND_VIEW_STREAM_HANDLER_H
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewSession.h>
+
+#include <thread>
+#include <vector>
+
+using std::vector;
+using std::mutex;
+using android::hardware::Return;
+using android::sp;
+using namespace ::android::hardware::automotive::sv::V1_0;
+
+class SurroundViewServiceHandler : public ISurroundViewStream {
+public:
+    SurroundViewServiceHandler(sp<ISurroundViewSession> session);
+
+    Return<void> notify(SvEvent svEvent) override;
+    Return<void> receiveFrames(const SvFramesDesc& svFramesDesc) override;
+
+    bool checkEventReceived(SvEvent svEvent);
+    SvFramesDesc getLastReceivedFrames();
+    int getReceiveFramesCount();
+    bool areAllFramesValid();
+    void setDoNotReturnFrames(bool flag);
+
+private:
+    mutex mLock;
+
+    vector<SvEvent> mReceivedEvents;
+    sp<ISurroundViewSession> mSession;
+    SvFramesDesc mLastReceivedFrames; // only use timestampNs and sequenceId
+    int mReceiveFramesCount; // TODO(haoxiangl): figure out a better name
+    bool mAllFramesValid = true;
+    bool mDoNotReturnFrames;
+};
+
+#endif //SURROUND_VIEW_STREAM_HANDLER_H
diff --git a/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp b/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp
new file mode 100644
index 0000000..b1b9d16
--- /dev/null
+++ b/automotive/sv/1.0/vts/functional/VtsHalSurroundViewV1_0TargetTest.cpp
@@ -0,0 +1,1137 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#define LOG_TAG "VtsHalSurroundViewTest"
+
+#include <android/hardware/automotive/sv/1.0/types.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewService.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundViewStream.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView2dSession.h>
+#include <android/hardware/automotive/sv/1.0/ISurroundView3dSession.h>
+#include <android/hardware_buffer.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <hidlmemory/mapping.h>
+#include <math.h>
+#include <utils/Log.h>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include "SurroundViewStreamHandler.h"
+
+using namespace ::android::hardware::automotive::sv::V1_0;
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_string;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMemory;
+using ::android::hardware::hidl_memory;
+
+const int kVertexByteSize = (3 * sizeof(float)) + 4;
+const int kIdByteSize = 2;
+
+// The main test class for Surround View Service
+class SurroundViewHidlTest : public ::testing::TestWithParam<std::string> {
+public:
+    virtual void SetUp() override {
+        mSurroundViewService = ISurroundViewService::getService(GetParam());
+        ASSERT_NE(mSurroundViewService.get(), nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    sp<ISurroundViewService> mSurroundViewService;    // Every test needs access to the service
+};
+
+TEST_P(SurroundViewHidlTest, startAndStop2dSession) {
+    ALOGD("SurroundViewHidlTest::startAndStop2dSession");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    SvResult result = mSurroundViewService->stop2dSession(surroundView2dSession);
+    ASSERT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid2dSession) {
+    ALOGD("SurroundViewHidlTest::stopInvalid2dSession");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    SvResult result = mSurroundViewService->stop2dSession(surroundView2dSession);
+    ASSERT_NE(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop2dStream) {
+    ALOGD("SurroundViewHidlTest::startAndStop2dStream");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+    surroundView2dSession->stopStream();
+
+    sleep(1);
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+    result = mSurroundViewService->stop2dSession(surroundView2dSession);
+    EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, start2dStreamWithoutReturningFrames) {
+    ALOGD("SurroundViewHidlTest::start2dStreamWithoutReturningFrames");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+    handler->setDoNotReturnFrames(true);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::FRAME_DROPPED));
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+    surroundView2dSession->stopStream();
+
+    sleep(1);
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+    result = mSurroundViewService->stop2dSession(surroundView2dSession);
+    EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, duplicateStart2dStream) {
+    ALOGD("SurroundViewHidlTest, duplicateStart2dStream");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    result = surroundView2dSession->startStream(handler);
+    EXPECT_NE(result, SvResult::OK);
+
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid2dStream) {
+    ALOGD("SurroundViewHidlTest, stopInvalid2dStream");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, validate2dSvFramesDesc) {
+    ALOGD("SurroundViewHidlTest, validate2dSvFramesDesc");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    // Validate timestampNs and sequenceId
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+    EXPECT_TRUE(handler->areAllFramesValid());
+
+    // Validate 2d SvFramesDesc. Do not compare nativeHandle since it is not
+    // stored and already verified on the fly.
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+
+    SvBuffer svBuffer2d = frames.svBuffers[0];
+    EXPECT_EQ(svBuffer2d.viewId, 0);
+
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+    float mapWidth, mapHeight;
+    surroundView2dSession->get2dMappingInfo([&mapWidth, &mapHeight] (Sv2dMappingInfo info) {
+        mapWidth = info.width;
+        mapHeight = info.height;
+    });
+    EXPECT_EQ(pDesc->height, floor(pDesc->width * (mapHeight / mapWidth)));
+
+    // Clean up
+    surroundView2dSession->stopStream();
+    result = mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, get2dMappingInfo) {
+    ALOGD("SurroundViewHidlTest, get2dMappingInfo");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    surroundView2dSession->get2dMappingInfo([] (Sv2dMappingInfo info) {
+        EXPECT_GT(info.width, 0);
+        EXPECT_GT(info.height, 0);
+    });
+
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set2dConfigResolution) {
+    ALOGD("SurroundViewHidlTest, set2dConfigResolution");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Change config
+    Sv2dConfig config;
+    config.width = 1920;
+    config.blending = SvQuality::HIGH;
+    surroundView2dSession->set2dConfig(config);
+
+    sleep(1);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+    // Check width has been changed but not the ratio
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer2d = frames.svBuffers[0];
+    EXPECT_EQ(svBuffer2d.viewId, 0);
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+    EXPECT_EQ(pDesc->width, config.width);
+
+    float mapWidth, mapHeight;
+    surroundView2dSession->get2dMappingInfo([&mapWidth, &mapHeight] (Sv2dMappingInfo info) {
+        mapWidth = info.width;
+        mapHeight = info.height;
+    });
+    EXPECT_EQ(pDesc->height, floor (pDesc->width * (mapHeight / mapWidth)));
+
+    // Clean up
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set2dConfigBlending) {
+    ALOGD("SurroundViewHidlTest, set2dConfigBlending");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Get the width before config changed
+    int oldWidth;
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer2d = frames.svBuffers[0];
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+    oldWidth = pDesc->width;
+
+    // Change config
+    Sv2dConfig config;
+    config.width = oldWidth;
+    config.blending = SvQuality::LOW;
+    surroundView2dSession->set2dConfig(config);
+
+    sleep(1);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+    Sv2dConfig retConfig;
+    surroundView2dSession->get2dConfig([&retConfig] (Sv2dConfig config) {
+        retConfig.width = config.width;
+        retConfig.blending = config.blending;
+    });
+
+    // Check config blending has been changed but not the width
+    EXPECT_EQ(retConfig.blending, config.blending);
+    EXPECT_EQ(retConfig.width, oldWidth);
+
+    // Clean up
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectCameraPointsWithValidCameraId) {
+    ALOGD("SurroundViewHidlTest, projectCameraPointsWithValidCameraId");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    hidl_vec<hidl_string> cameraIds;
+    mSurroundViewService->getCameraIds([&cameraIds](
+            const hidl_vec<hidl_string>& camIds) {
+        cameraIds = camIds;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView2dSession);
+
+    SvResult result = surroundView2dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Get the width and height of the frame
+    int width, height;
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer2d = frames.svBuffers[0];
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+    width = pDesc->width;
+    height = pDesc->height;
+
+    float mapWidth, mapHeight, mapCenter[2];
+    surroundView2dSession->get2dMappingInfo(
+        [&mapWidth, &mapHeight, &mapCenter] (Sv2dMappingInfo info) {
+        mapWidth = info.width;
+        mapHeight = info.height;
+        mapCenter[0] = info.center.x;
+        mapCenter[1] = info.center.y;
+    });
+
+    // Set one valid point and one invalid point
+    hidl_vec<Point2dInt> points2dCamera;
+    points2dCamera.resize(2);
+    points2dCamera[0].x = 0;
+    points2dCamera[0].y = 0;
+    points2dCamera[1].x = width * 2;
+    points2dCamera[1].y = height * 2;
+
+    surroundView2dSession->projectCameraPoints(
+        points2dCamera,
+        cameraIds[0],
+        [&mapWidth, &mapHeight, &mapCenter] (
+            const hidl_vec<Point2dFloat>& outPoints) {
+            // Make sure point[0] is valid.
+            EXPECT_TRUE(outPoints[0].isValid);
+            EXPECT_GE(outPoints[0].x, mapCenter[0] - mapWidth);
+            EXPECT_LE(outPoints[0].x, mapCenter[0] + mapWidth);
+            EXPECT_GE(outPoints[0].y, mapCenter[1] - mapHeight);
+            EXPECT_LE(outPoints[0].y, mapCenter[1] + mapHeight);
+
+            // Make sure point[1] is invalid.
+            EXPECT_FALSE(outPoints[1].isValid);
+        });
+
+    // Clean up
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectCameraPointsWithInvalidCameraId) {
+    ALOGD("SurroundViewHidlTest, projectCameraPointsWithInvalidCameraId");
+    sp<ISurroundView2dSession> surroundView2dSession;
+    mSurroundViewService->start2dSession(
+        [&surroundView2dSession](
+            const sp<ISurroundView2dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView2dSession = session;
+    });
+
+    hidl_vec<hidl_string> cameraIds;
+    mSurroundViewService->getCameraIds([&cameraIds](
+            const hidl_vec<hidl_string>& camIds) {
+        cameraIds = camIds;
+    });
+
+    hidl_string invalidCameraId = "INVALID_CAMERA_ID";
+
+    // In case one of the camera id happens to be identical to
+    // the invalid camera id.
+    for (auto cameraId : cameraIds) {
+        ASSERT_NE(cameraId, invalidCameraId);
+    }
+
+    // Set one valid point
+    hidl_vec<Point2dInt> points2dCamera;
+    points2dCamera.resize(1);
+    points2dCamera[0].x = 0;
+    points2dCamera[0].y = 0;
+
+    surroundView2dSession->projectCameraPoints(
+        points2dCamera,
+        invalidCameraId,
+        [] (const hidl_vec<Point2dFloat>& outPoints) {
+            // No points are return due to invalid camera id
+            EXPECT_EQ(outPoints.size(), 0);
+        });
+
+    // Clean up
+    surroundView2dSession->stopStream();
+    mSurroundViewService->stop2dSession(surroundView2dSession);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop3dSession) {
+    ALOGD("SurroundViewHidlTest, startAndStop3dSession");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    SvResult result = mSurroundViewService->stop3dSession(surroundView3dSession);
+    EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, stopInvalid3dSession) {
+    ALOGD("SurroundViewHidlTest, stopInvalid3dSession");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    SvResult result = mSurroundViewService->stop3dSession(surroundView3dSession);
+    EXPECT_NE(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, startAndStop3dStream) {
+    ALOGD("SurroundViewHidlTest::startAndStop3dStream");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::vector<View3d> views(1);
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+    surroundView3dSession->stopStream();
+
+    sleep(1);
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+    result = mSurroundViewService->stop3dSession(surroundView3dSession);
+    EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, start3dStreamWithoutReturningFrames) {
+    ALOGD("SurroundViewHidlTest::start3dStreamWithoutReturningFrames");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::vector<View3d> views(1);
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+    handler->setDoNotReturnFrames(true);
+
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STARTED));
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::FRAME_DROPPED));
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+
+    surroundView3dSession->stopStream();
+
+    sleep(1);
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::STREAM_STOPPED));
+
+    result = mSurroundViewService->stop3dSession(surroundView3dSession);
+    EXPECT_EQ(result, SvResult::OK);
+}
+
+TEST_P(SurroundViewHidlTest, duplicateStart3dStream) {
+    ALOGD("SurroundViewHidlTest, duplicateStart3dStream");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::vector<View3d> views(1);
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    result = surroundView3dSession->startStream(handler);
+    EXPECT_NE(result, SvResult::OK);
+
+    surroundView3dSession->stopStream();
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, start3dStreamNoViewSetFail) {
+    ALOGD("SurroundViewHidlTest, start3dStreamNoViewSetFail");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::VIEW_NOT_SET);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, validate3dSvFramesDesc) {
+    ALOGD("SurroundViewHidlTest, validate3dSvFramesDesc");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    std::vector<View3d> views(1);
+    views[0].viewId = 0;
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(5);
+
+    EXPECT_GT(handler->getReceiveFramesCount(), 0);
+    EXPECT_TRUE(handler->areAllFramesValid());
+
+    // Validate 3d SvFramesDesc when only one view is set.
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    EXPECT_EQ(frames.svBuffers[0].viewId, 0);
+
+    views.resize(3);
+    views[0].viewId = 0;
+    views[1].viewId = 1;
+    views[2].viewId = 2;
+    setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+
+    sleep(1);
+
+    // Validate 3d SvFramesDesc when multiple views are set.
+    EXPECT_TRUE(handler->areAllFramesValid());
+    frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 3);
+    EXPECT_EQ(frames.svBuffers[0].viewId, 0);
+    EXPECT_EQ(frames.svBuffers[1].viewId, 1);
+    EXPECT_EQ(frames.svBuffers[2].viewId, 2);
+    EXPECT_EQ(frames.svBuffers[0].hardwareBuffer.description[0],
+              frames.svBuffers[1].hardwareBuffer.description[0]);
+    EXPECT_EQ(frames.svBuffers[0].hardwareBuffer.description[1],
+              frames.svBuffers[1].hardwareBuffer.description[1]);
+    EXPECT_EQ(frames.svBuffers[1].hardwareBuffer.description[0],
+              frames.svBuffers[2].hardwareBuffer.description[0]);
+    EXPECT_EQ(frames.svBuffers[1].hardwareBuffer.description[1],
+              frames.svBuffers[2].hardwareBuffer.description[1]);
+
+    // Clean up
+    surroundView3dSession->stopStream();
+    result = mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set3dConfigResolution) {
+    ALOGD("SurroundViewHidlTest, set3dConfigResolution");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    std::vector<View3d> views(1);
+    views[0].viewId = 0;
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Change config
+    Sv3dConfig config;
+    config.width = 1920;
+    config.height = 1080;
+    config.carDetails = SvQuality::HIGH;
+    surroundView3dSession->set3dConfig(config);
+
+    sleep(1);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+    // Check width has been changed but not the ratio
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer3d = frames.svBuffers[0];
+    EXPECT_EQ(svBuffer3d.viewId, 0);
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer3d.hardwareBuffer.description);
+    EXPECT_EQ(pDesc->width, config.width);
+    EXPECT_EQ(pDesc->height, config.height);
+
+    // Clean up
+    surroundView3dSession->stopStream();
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, set3dConfigCarDetails) {
+    ALOGD("SurroundViewHidlTest, set3dConfigCarDetails");
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    std::vector<View3d> views(1);
+    views[0].viewId = 0;
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Get the width before config changed
+    int oldWidth, oldHeight;
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer3d = frames.svBuffers[0];
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer3d.hardwareBuffer.description);
+    oldWidth = pDesc->width;
+    oldHeight = pDesc->height;
+
+    // Change config
+    Sv3dConfig config;
+    config.width = oldWidth;
+    config.height = oldHeight;
+    config.carDetails = SvQuality::LOW;
+    surroundView3dSession->set3dConfig(config);
+
+    sleep(1);
+
+    EXPECT_TRUE(handler->checkEventReceived(SvEvent::CONFIG_UPDATED));
+
+    Sv3dConfig retConfig;
+    surroundView3dSession->get3dConfig([&retConfig] (Sv3dConfig config) {
+        retConfig.width = config.width;
+        retConfig.height = config.height;
+        retConfig.carDetails = config.carDetails;
+    });
+
+    // Check config blending has been changed but not the width
+    EXPECT_EQ(retConfig.carDetails, config.carDetails);
+    EXPECT_EQ(retConfig.width, oldWidth);
+    EXPECT_EQ(retConfig.height, oldHeight);
+
+    // Clean up
+    surroundView3dSession->stopStream();
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+std::pair<hidl_memory, sp<IMemory>> GetMappedSharedMemory(int bytesSize) {
+
+    const auto nullResult = std::make_pair(hidl_memory(), nullptr);
+
+    sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
+    if (ashmemAllocator.get() == nullptr) {
+        ALOGE("SurroundViewHidlTest getService ashmem failed");
+        return nullResult;
+    }
+
+    // Allocate shared memory.
+    hidl_memory hidlMemory;
+    bool allocateSuccess = false;
+    Return<void> result = ashmemAllocator->allocate(bytesSize,
+            [&](bool success, const hidl_memory& hidlMem) {
+                if (!success) {
+                    return;
+                }
+                allocateSuccess = success;
+                hidlMemory = hidlMem;
+            });
+
+    // Check result of allocated memory.
+    if (!result.isOk() || !allocateSuccess) {
+        ALOGE("SurroundViewHidlTest allocate shared memory failed");
+        return nullResult;
+    }
+
+    // Map shared memory.
+    sp<IMemory> pIMemory = mapMemory(hidlMemory);
+    if (pIMemory.get() == nullptr) {
+        ALOGE("SurroundViewHidlTest map shared memory failed");
+        return nullResult;
+    }
+
+    return std::make_pair(hidlMemory, pIMemory);
+}
+
+void SetIndexOfOverlaysMemory(
+        const std::vector<OverlayMemoryDesc>& overlaysMemDesc,
+        sp<IMemory> pIMemory, int indexPosition, uint16_t indexValue) {
+
+    // Count the number of vertices until the index.
+    int totalVerticesCount = 0;
+    for (int i = 0; i < indexPosition; i++) {
+        totalVerticesCount += overlaysMemDesc[i].verticesCount;
+    }
+
+    const int indexBytePosition = (indexPosition * kIdByteSize) +
+            (kVertexByteSize * totalVerticesCount);
+
+    uint8_t* pSharedMemoryData = (uint8_t*)((void*)pIMemory->getPointer());
+    pSharedMemoryData += indexBytePosition;
+    uint16_t* pIndex16bit = (uint16_t*)pSharedMemoryData;
+
+    ALOGD("Setting index at pos %d", indexBytePosition);
+
+    // Modify shared memory.
+    pIMemory->update();
+    *pIndex16bit = indexValue;
+    pIMemory->commit();
+}
+
+std::pair<OverlaysData, sp<IMemory>> GetSampleOverlaysData() {
+    OverlaysData overlaysData;
+    overlaysData.overlaysMemoryDesc.resize(2);
+
+    int sharedMemBytesSize = 0;
+    OverlayMemoryDesc overlayMemDesc1, overlayMemDesc2;
+    overlayMemDesc1.id = 0;
+    overlayMemDesc1.verticesCount = 6;
+    overlayMemDesc1.overlayPrimitive = OverlayPrimitive::TRIANGLES;
+    overlaysData.overlaysMemoryDesc[0] = overlayMemDesc1;
+    sharedMemBytesSize += kIdByteSize +
+            kVertexByteSize * overlayMemDesc1.verticesCount;
+
+    overlayMemDesc2.id = 1;
+    overlayMemDesc2.verticesCount = 4;
+    overlayMemDesc2.overlayPrimitive = OverlayPrimitive::TRIANGLES_STRIP;
+    overlaysData.overlaysMemoryDesc[1] = overlayMemDesc2;
+    sharedMemBytesSize += kIdByteSize +
+            kVertexByteSize * overlayMemDesc2.verticesCount;
+
+    std::pair<hidl_memory, sp<IMemory>> sharedMem =
+            GetMappedSharedMemory(sharedMemBytesSize);
+    sp<IMemory> pIMemory = sharedMem.second;
+    if (pIMemory.get() == nullptr) {
+        return std::make_pair(OverlaysData(), nullptr);
+    }
+
+    // Get pointer to shared memory data and set all bytes to 0.
+    uint8_t* pSharedMemoryData = (uint8_t*)((void*)pIMemory->getPointer());
+    pIMemory->update();
+    memset(pSharedMemoryData, 0, sharedMemBytesSize);
+    pIMemory->commit();
+
+    std::vector<OverlayMemoryDesc> overlaysDesc = {overlayMemDesc1,
+            overlayMemDesc2};
+
+    // Set indexes in shared memory.
+    SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 0, overlayMemDesc1.id);
+    SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, overlayMemDesc2.id);
+
+    overlaysData.overlaysMemoryDesc = overlaysDesc;
+    overlaysData.overlaysMemory = sharedMem.first;
+
+    return std::make_pair(overlaysData, pIMemory);
+}
+
+TEST_P(SurroundViewHidlTest, updateOverlaysSuccess) {
+    ALOGD("SurroundViewHidlTest, updateOverlaysSuccess");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::pair<OverlaysData, sp<IMemory>> overlaysData = GetSampleOverlaysData();
+    ASSERT_NE(overlaysData.second, nullptr);
+
+    SvResult result = surroundView3dSession->updateOverlays(overlaysData.first);
+    EXPECT_EQ(result, SvResult::OK);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataMismatchIdFail) {
+    ALOGD("SurroundViewHidlTest, overlaysDataMismatchIdFail");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::pair<OverlaysData, sp<IMemory>> overlaysDataMismatchId
+            = GetSampleOverlaysData();
+    ASSERT_NE(overlaysDataMismatchId.second, nullptr);
+
+    // Set id of second overlay in shared memory to 2 (expected is 1).
+    auto& overlaysDesc = overlaysDataMismatchId.first.overlaysMemoryDesc;
+    auto& pIMemory = overlaysDataMismatchId.second;
+    SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, 2);
+
+    SvResult result = surroundView3dSession->updateOverlays(
+            overlaysDataMismatchId.first);
+    EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataNullMemoryFail) {
+    ALOGD("SurroundViewHidlTest, overlaysDataNullMemoryFail");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::pair<OverlaysData, sp<IMemory>> overlaysDataNullMemory
+            = GetSampleOverlaysData();
+
+    // Set shared memory to null.
+    overlaysDataNullMemory.first.overlaysMemory = hidl_memory();
+
+    SvResult result = surroundView3dSession->updateOverlays(
+            overlaysDataNullMemory.first);
+    EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataLessThan3VerticesFail) {
+    ALOGD("SurroundViewHidlTest, overlaysDataLessThan3VerticesFail");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::pair<OverlaysData, sp<IMemory>> overlaysData2Vertices
+            = GetSampleOverlaysData();
+
+    // Set vertices count of second overlay to 2.
+    overlaysData2Vertices.first.overlaysMemoryDesc[1].verticesCount = 2;
+
+    SvResult result = surroundView3dSession->updateOverlays(
+            overlaysData2Vertices.first);
+    EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataVerticesCountFail) {
+    ALOGD("SurroundViewHidlTest, overlaysDataVerticesNotMultipleOf3Fail");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    OverlaysData overlaysData;
+    overlaysData.overlaysMemoryDesc.resize(1);
+
+    OverlayMemoryDesc overlayMemDesc1;
+    overlayMemDesc1.id = 0;
+    overlayMemDesc1.verticesCount = 4; // Invalid count for TRIANGLES primitive.
+    overlayMemDesc1.overlayPrimitive = OverlayPrimitive::TRIANGLES;
+    overlaysData.overlaysMemoryDesc[0] = overlayMemDesc1;
+
+    const int sharedMemBytesSize =
+            2 + sizeof(float) * overlayMemDesc1.verticesCount;
+    auto sharedMem = GetMappedSharedMemory(sharedMemBytesSize);
+
+    // Set index in shared memory.
+    auto& overlaysDesc = overlaysData.overlaysMemoryDesc;
+    auto& pIMemory = sharedMem.second;
+    SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 0, overlayMemDesc1.id);
+
+    SvResult result = surroundView3dSession->updateOverlays(overlaysData);
+    EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, overlaysDataSameIdFail) {
+    ALOGD("SurroundViewHidlTest, overlaysDataSameIdFail");
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    std::pair<OverlaysData, sp<IMemory>> overlaysDataSameId
+            = GetSampleOverlaysData();
+    ASSERT_NE(overlaysDataSameId.second, nullptr);
+
+    // Set id of second overlay as id of first.
+    auto& overlaysDesc = overlaysDataSameId.first.overlaysMemoryDesc;
+        auto& pIMemory = overlaysDataSameId.second;
+    SetIndexOfOverlaysMemory(overlaysDesc, pIMemory, 1, overlaysDesc[0].id);
+
+    SvResult result = surroundView3dSession->updateOverlays(
+            overlaysDataSameId.first);
+    EXPECT_EQ(result, SvResult::INVALID_ARG);
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectPointsIncorrectCameraIdFail) {
+    ALOGD("SurroundViewHidlTest, projectPointsIncorrectCameraIdFail");
+
+    hidl_vec<hidl_string> cameraIds;
+    mSurroundViewService->getCameraIds([&cameraIds](
+            const hidl_vec<hidl_string>& camIds) {
+        cameraIds = camIds;
+    });
+    EXPECT_GT(cameraIds.size(), 0);
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    Point2dInt cameraPoint;
+    cameraPoint.x = 0;
+    cameraPoint.y = 0;
+    std::vector<Point2dInt> cameraPoints = {cameraPoint};
+
+    hidl_string invalidCameraId = "INVALID_CAMERA_ID";
+
+    std::vector<Point3dFloat> points3d;
+    surroundView3dSession->projectCameraPointsTo3dSurface(
+        cameraPoints, invalidCameraId,
+        [&points3d](const hidl_vec<Point3dFloat>& points3dproj) {
+                points3d = points3dproj;
+            });
+
+    EXPECT_TRUE(points3d.empty());
+
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+TEST_P(SurroundViewHidlTest, projectPointsInvalidPointsFail) {
+    ALOGD("SurroundViewHidlTest, projectPointsInvalidPointsFail");
+
+    hidl_vec<hidl_string> cameraIds;
+    mSurroundViewService->getCameraIds([&cameraIds](
+            const hidl_vec<hidl_string>& camIds) {
+        cameraIds = camIds;
+    });
+
+    EXPECT_GT(cameraIds.size(), 0);
+
+    sp<ISurroundView3dSession> surroundView3dSession;
+    mSurroundViewService->start3dSession(
+        [&surroundView3dSession](
+            const sp<ISurroundView3dSession>& session, SvResult result) {
+        ASSERT_EQ(result, SvResult::OK);
+        surroundView3dSession = session;
+    });
+
+    sp<SurroundViewServiceHandler> handler =
+        new SurroundViewServiceHandler(surroundView3dSession);
+
+    std::vector<View3d> views(1);
+    views[0].viewId = 0;
+    SvResult setViewResult = surroundView3dSession->setViews(views);
+    EXPECT_EQ(setViewResult, SvResult::OK);
+    SvResult result = surroundView3dSession->startStream(handler);
+    EXPECT_EQ(result, SvResult::OK);
+
+    sleep(1);
+
+    // Get the width and height of the frame
+    int width, height;
+    SvFramesDesc frames = handler->getLastReceivedFrames();
+    EXPECT_EQ(frames.svBuffers.size(), 1);
+    SvBuffer svBuffer2d = frames.svBuffers[0];
+    const AHardwareBuffer_Desc* pDesc =
+        reinterpret_cast<const AHardwareBuffer_Desc *>(&svBuffer2d.hardwareBuffer.description);
+    width = pDesc->width;
+    height = pDesc->height;
+
+    Point2dInt cameraPoint;
+    cameraPoint.x = width * 2;
+    cameraPoint.y = height * 2;
+    std::vector<Point2dInt> cameraPoints = {cameraPoint};
+
+    std::vector<Point3dFloat> points3d;
+    surroundView3dSession->projectCameraPointsTo3dSurface(
+              cameraPoints, cameraIds[0],
+        [&points3d](const hidl_vec<Point3dFloat>& points3dproj) {
+                points3d = points3dproj;
+            });
+
+    EXPECT_FALSE(points3d[0].isValid);
+
+    surroundView3dSession->stopStream();
+    mSurroundViewService->stop3dSession(surroundView3dSession);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    PerInstance,
+    SurroundViewHidlTest,
+    testing::ValuesIn(
+        android::hardware::getAllHalInstanceNames(ISurroundViewService::descriptor)
+    ),
+    android::hardware::PrintInstanceNameToString
+);
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
index 785f0e0..4b94800 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultConfig.h
@@ -70,6 +70,7 @@
     (int)(0x104 | VehiclePropertyGroup::VENDOR | VehiclePropertyType::STRING | VehicleArea::GLOBAL);
 constexpr int FUEL_DOOR_REAR_LEFT = (int)PortLocationType::REAR_LEFT;
 constexpr int CHARGE_PORT_FRONT_LEFT = (int)PortLocationType::FRONT_LEFT;
+constexpr int CHARGE_PORT_REAR_LEFT = (int)PortLocationType::REAR_LEFT;
 constexpr int LIGHT_STATE_ON = (int)VehicleLightState::ON;
 constexpr int LIGHT_SWITCH_AUTO = (int)VehicleLightSwitch::AUTOMATIC;
 constexpr int WHEEL_FRONT_LEFT = (int)VehicleAreaWheel::LEFT_FRONT;
@@ -249,6 +250,14 @@
 
         {.config =
                  {
+                         .prop = toInt(VehicleProperty::INFO_MULTI_EV_PORT_LOCATIONS),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::STATIC,
+                 },
+         .initialValue = {.int32Values = {CHARGE_PORT_FRONT_LEFT, CHARGE_PORT_REAR_LEFT}}},
+
+        {.config =
+                 {
                          .prop = toInt(VehicleProperty::INFO_MAKE),
                          .access = VehiclePropertyAccess::READ,
                          .changeMode = VehiclePropertyChangeMode::STATIC,
@@ -457,6 +466,14 @@
                  },
          .initialValue = {.int32Values = {0, 0, 0}}},
 
+        {.config =
+                 {
+                         .prop = toInt(VehicleProperty::HW_ROTARY_INPUT),
+                         .access = VehiclePropertyAccess::READ,
+                         .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
+                 },
+         .initialValue = {.int32Values = {0, 0, 0}}},
+
         {.config = {.prop = toInt(VehicleProperty::HVAC_POWER_ON),
                     .access = VehiclePropertyAccess::READ_WRITE,
                     .changeMode = VehiclePropertyChangeMode::ON_CHANGE,
diff --git a/automotive/vehicle/2.0/types.hal b/automotive/vehicle/2.0/types.hal
index cbd9e28..23f9135 100644
--- a/automotive/vehicle/2.0/types.hal
+++ b/automotive/vehicle/2.0/types.hal
@@ -320,6 +320,25 @@
         | VehicleArea:GLOBAL),
 
     /**
+     * Multiple EV port locations
+     *
+     * Implement this property if the vehicle has multiple EV ports.
+     * Port locations are defined in PortLocationType.
+     * For example, a car has one port in front left and one port in rear left:
+     *   int32Values[0] = PortLocationType::FRONT_LEFT
+     *   int32Values[0] = PortLocationType::REAR_LEFT
+     *
+     * @change_mode VehiclePropertyChangeMode:STATIC
+     * @access VehiclePropertyAccess:READ
+     * @data_enum PortLocationType
+     */
+    INFO_MULTI_EV_PORT_LOCATIONS = (
+        0x010C
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:INT32_VEC
+        | VehicleArea:GLOBAL),
+
+    /**
      * Current odometer value of the vehicle
      *
      * @change_mode VehiclePropertyChangeMode:CONTINUOUS
@@ -1413,6 +1432,33 @@
         | VehiclePropertyType:INT32_VEC
         | VehicleArea:GLOBAL),
 
+    /**
+     * Property to feed H/W rotary events to android
+     *
+     * int32Values[0] : RotaryInputType identifying which rotary knob rotated
+     * int32Values[1] : number of detents (clicks), positive for clockwise,
+     *                  negative for counterclockwise
+     * int32Values[2] : target display defined in VehicleDisplay. Events not
+     *                  tied to specific display must be sent to
+     *                  VehicleDisplay#MAIN.
+     * int32values[3 .. 3 + abs(number of detents) - 2]:
+     *                  nanosecond deltas between pairs of consecutive detents,
+     *                  if the number of detents is > 1 or < -1
+     *
+     * VehiclePropValue.timestamp: when the rotation occurred. If the number of
+     *                             detents is > 1 or < -1, this is when the
+     *                             first detent of rotation occurred.
+     *
+     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
+     * @data_enum RotaryInputType
+     * @access VehiclePropertyAccess:READ
+     */
+    HW_ROTARY_INPUT = (
+        0x0A20
+        | VehiclePropertyGroup:SYSTEM
+        | VehiclePropertyType:INT32_VEC
+        | VehicleArea:GLOBAL),
+
     /***************************************************************************
      * Most Car Cabin properties have both a POSition and MOVE parameter.  These
      * are used to control the various movements for seats, doors, and windows
@@ -4651,3 +4697,18 @@
 
     UserIdentificationAssociationSetValue value;
 };
+
+/**
+ * A rotary control which can rotate without limits. These controls use HW_ROTARY_INPUT to report
+ * relative clockwise or counterclockwise motion. They have no absolute position.
+ */
+enum RotaryInputType : int32_t {
+    /**
+      * Main rotary control, typically in the center console, used to navigate the user interface.
+      */
+    ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION = 0,
+
+    /** Volume control for adjusting audio volume. */
+    ROTARY_INPUT_TYPE_AUDIO_VOLUME = 1,
+};
+
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index f518a15..677b496 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -20,6 +20,7 @@
 
 #include <algorithm>
 #include <array>
+#include <regex>
 #include <linux/videodev2.h>
 #include "android-base/macros.h"
 #include "CameraMetadata.h"
@@ -46,10 +47,20 @@
 
 } // anonymous namespace
 
+const std::regex kDevicePathRE("/dev/video([0-9]+)");
+
 ExternalCameraDevice::ExternalCameraDevice(
-        const std::string& cameraId, const ExternalCameraConfig& cfg) :
-        mCameraId(cameraId),
-        mCfg(cfg) {}
+        const std::string& devicePath, const ExternalCameraConfig& cfg) :
+        mCameraId("-1"),
+        mDevicePath(devicePath),
+        mCfg(cfg) {
+    std::smatch sm;
+    if (std::regex_match(mDevicePath, sm, kDevicePathRE)) {
+        mCameraId = std::to_string(mCfg.cameraIdOffset + std::stoi(sm[1]));
+    } else {
+        ALOGE("%s: device path match failed for %s", __FUNCTION__, mDevicePath.c_str());
+    }
+}
 
 ExternalCameraDevice::~ExternalCameraDevice() {}
 
@@ -129,20 +140,20 @@
         return Void();
     }
 
-    unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+    unique_fd fd(::open(mDevicePath.c_str(), O_RDWR));
     if (fd.get() < 0) {
         int numAttempt = 0;
         do {
             ALOGW("%s: v4l2 device %s open failed, wait 33ms and try again",
-                    __FUNCTION__, mCameraId.c_str());
+                    __FUNCTION__, mDevicePath.c_str());
             usleep(OPEN_RETRY_SLEEP_US); // sleep and try again
-            fd.reset(::open(mCameraId.c_str(), O_RDWR));
+            fd.reset(::open(mDevicePath.c_str(), O_RDWR));
             numAttempt++;
         } while (fd.get() < 0 && numAttempt <= MAX_RETRY);
 
         if (fd.get() < 0) {
             ALOGE("%s: v4l2 device open %s failed: %s",
-                    __FUNCTION__, mCameraId.c_str(), strerror(errno));
+                    __FUNCTION__, mDevicePath.c_str(), strerror(errno));
             mLock.unlock();
             _hidl_cb(Status::INTERNAL_ERROR, nullptr);
             return Void();
@@ -203,9 +214,9 @@
 status_t ExternalCameraDevice::initCameraCharacteristics() {
     if (mCameraCharacteristics.isEmpty()) {
         // init camera characteristics
-        unique_fd fd(::open(mCameraId.c_str(), O_RDWR));
+        unique_fd fd(::open(mDevicePath.c_str(), O_RDWR));
         if (fd.get() < 0) {
-            ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mCameraId.c_str());
+            ALOGE("%s: v4l2 device open %s failed", __FUNCTION__, mDevicePath.c_str());
             return DEAD_OBJECT;
         }
 
diff --git a/camera/device/3.4/default/ExternalCameraUtils.cpp b/camera/device/3.4/default/ExternalCameraUtils.cpp
index 62a4c87..8f4626c 100644
--- a/camera/device/3.4/default/ExternalCameraUtils.cpp
+++ b/camera/device/3.4/default/ExternalCameraUtils.cpp
@@ -703,6 +703,7 @@
 namespace common {
 
 namespace {
+    const int kDefaultCameraIdOffset = 100;
     const int kDefaultJpegBufSize = 5 << 20; // 5MB
     const int kDefaultNumVideoBuffer = 4;
     const int kDefaultNumStillBuffer = 2;
@@ -738,6 +739,11 @@
         return ret;
     }
 
+    XMLElement *cameraIdOffset = providerCfg->FirstChildElement("CameraIdOffset");
+    if (cameraIdOffset != nullptr) {
+        ret.cameraIdOffset = std::atoi(cameraIdOffset->GetText());
+    }
+
     XMLElement *ignore = providerCfg->FirstChildElement("ignore");
     if (ignore == nullptr) {
         ALOGI("%s: no internal ignored device specified", __FUNCTION__);
@@ -874,6 +880,7 @@
 }
 
 ExternalCameraConfig::ExternalCameraConfig() :
+        cameraIdOffset(kDefaultCameraIdOffset),
         maxJpegBufSize(kDefaultJpegBufSize),
         numVideoBuffers(kDefaultNumVideoBuffer),
         numStillBuffers(kDefaultNumStillBuffer),
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
index 1958fcb..88726f4 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h
@@ -149,6 +149,7 @@
     bool mInitialized = false;
     bool mInitFailed = false;
     std::string mCameraId;
+    std::string mDevicePath;
     const ExternalCameraConfig& mCfg;
     std::vector<SupportedV4L2Format> mSupportedFormats;
     CroppingType mCroppingType;
diff --git a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
index 74f75eb..b354406 100644
--- a/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
+++ b/camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraUtils.h
@@ -68,6 +68,9 @@
     static const char* kDefaultCfgPath;
     static ExternalCameraConfig loadFromCfg(const char* cfgPath = kDefaultCfgPath);
 
+    // CameraId base offset for numerical representation
+    uint32_t cameraIdOffset;
+
     // List of internal V4L2 video nodes external camera HAL must ignore.
     std::unordered_set<std::string> mInternalDevices;
 
diff --git a/camera/device/3.6/types.hal b/camera/device/3.6/types.hal
index 743b139..f4c50ed 100644
--- a/camera/device/3.6/types.hal
+++ b/camera/device/3.6/types.hal
@@ -119,14 +119,20 @@
      * For devices that does not support the OFFLINE_PROCESSING capability, this
      * fields will always be false.
      *
-     * For devices support the OFFLINE_PROCESSING capability: any input stream
-     * and any output stream that can be output of the input stream must set
-     * this field to true. Also any stream of YUV420_888 format or JPEG format,
-     * with CPU_READ usage flag, must set this field to true. All other streams
-     * are up to camera HAL to advertise support or not, though it is not
-     * recommended to list support for streams with hardware composer or video
-     * encoder usage flags as these streams tend to be targeted continuously and
-     * can lead to long latency when trying to switch to offline.
+     * For backward compatible camera devices that support the
+     * OFFLINE_PROCESSING capability: any input stream and any output stream
+     * that can be output of the input stream must set this field to true. Also
+     * any stream of YUV420_888 format or JPEG format, with CPU_READ usage flag,
+     * must set this field to true.
+     *
+     * For depth only camera devices that support the OFFLINE_PROCESSING
+     * capability: any DEPTH16 output stream must set this field to true.
+     *
+     * All other streams are up to camera HAL to advertise support or not,
+     * though it is not recommended to list support for streams with
+     * hardware composer or video encoder usage flags as these streams tend
+     * to be targeted continuously and can lead to long latency when trying to
+     * switch to offline.
      *
      */
     bool supportOffline;
diff --git a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
index 2bfced2..64a51f6 100644
--- a/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
+++ b/camera/provider/2.4/default/ExternalCameraProviderImpl_2_4.cpp
@@ -44,17 +44,19 @@
 const char* kDevicePath = "/dev/";
 constexpr char kPrefix[] = "video";
 constexpr int kPrefixLen = sizeof(kPrefix) - 1;
+constexpr int kDevicePrefixLen = sizeof(kDevicePath) + kPrefixLen + 1;
 
-bool matchDeviceName(const hidl_string& deviceName, std::string* deviceVersion,
-                     std::string* cameraId) {
+bool matchDeviceName(int cameraIdOffset,
+                     const hidl_string& deviceName, std::string* deviceVersion,
+                     std::string* cameraDevicePath) {
     std::string deviceNameStd(deviceName.c_str());
     std::smatch sm;
     if (std::regex_match(deviceNameStd, sm, kDeviceNameRE)) {
         if (deviceVersion != nullptr) {
             *deviceVersion = sm[1];
         }
-        if (cameraId != nullptr) {
-            *cameraId = sm[2];
+        if (cameraDevicePath != nullptr) {
+            *cameraDevicePath = "/dev/video" + std::to_string(std::stoi(sm[2]) - cameraIdOffset);
         }
         return true;
     }
@@ -146,8 +148,9 @@
         const hidl_string& cameraDeviceName,
         ICameraProvider::getCameraDeviceInterface_V3_x_cb _hidl_cb) {
 
-    std::string cameraId, deviceVersion;
-    bool match = matchDeviceName(cameraDeviceName, &deviceVersion, &cameraId);
+    std::string cameraDevicePath, deviceVersion;
+    bool match = matchDeviceName(mCfg.cameraIdOffset, cameraDeviceName,
+                                 &deviceVersion, &cameraDevicePath);
     if (!match) {
         _hidl_cb(Status::ILLEGAL_ARGUMENT, nullptr);
         return Void();
@@ -164,19 +167,19 @@
         case 4: {
             ALOGV("Constructing v3.4 external camera device");
             deviceImpl = new device::V3_4::implementation::ExternalCameraDevice(
-                    cameraId, mCfg);
+                    cameraDevicePath, mCfg);
             break;
         }
         case 5: {
             ALOGV("Constructing v3.5 external camera device");
             deviceImpl = new device::V3_5::implementation::ExternalCameraDevice(
-                    cameraId, mCfg);
+                    cameraDevicePath, mCfg);
             break;
         }
         case 6: {
             ALOGV("Constructing v3.6 external camera device");
             deviceImpl = new device::V3_6::implementation::ExternalCameraDevice(
-                    cameraId, mCfg);
+                    cameraDevicePath, mCfg);
             break;
         }
         default:
@@ -186,7 +189,7 @@
     }
 
     if (deviceImpl == nullptr || deviceImpl->isInitFailed()) {
-        ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraId.c_str());
+        ALOGE("%s: camera device %s init failed!", __FUNCTION__, cameraDevicePath.c_str());
         _hidl_cb(Status::INTERNAL_ERROR, nullptr);
         return Void();
     }
@@ -210,12 +213,14 @@
     ALOGI("ExtCam: adding %s to External Camera HAL!", devName);
     Mutex::Autolock _l(mLock);
     std::string deviceName;
+    std::string cameraId = std::to_string(mCfg.cameraIdOffset +
+                                          std::atoi(devName + kDevicePrefixLen));
     if (mPreferredHal3MinorVersion == 6) {
-        deviceName = std::string("device@3.6/external/") + devName;
+        deviceName = std::string("device@3.6/external/") + cameraId;
     } else if (mPreferredHal3MinorVersion == 5) {
-        deviceName = std::string("device@3.5/external/") + devName;
+        deviceName = std::string("device@3.5/external/") + cameraId;
     } else {
-        deviceName = std::string("device@3.4/external/") + devName;
+        deviceName = std::string("device@3.4/external/") + cameraId;
     }
     mCameraStatusMap[deviceName] = CameraDeviceStatus::PRESENT;
     if (mCallbacks != nullptr) {
@@ -259,12 +264,14 @@
 void ExternalCameraProviderImpl_2_4::deviceRemoved(const char* devName) {
     Mutex::Autolock _l(mLock);
     std::string deviceName;
+    std::string cameraId = std::to_string(mCfg.cameraIdOffset +
+                                          std::atoi(devName + kDevicePrefixLen));
     if (mPreferredHal3MinorVersion == 6) {
-        deviceName = std::string("device@3.6/external/") + devName;
+        deviceName = std::string("device@3.6/external/") + cameraId;
     } else if (mPreferredHal3MinorVersion == 5) {
-        deviceName = std::string("device@3.5/external/") + devName;
+        deviceName = std::string("device@3.5/external/") + cameraId;
     } else {
-        deviceName = std::string("device@3.4/external/") + devName;
+        deviceName = std::string("device@3.4/external/") + cameraId;
     }
     if (mCameraStatusMap.find(deviceName) != mCameraStatusMap.end()) {
         mCameraStatusMap.erase(deviceName);
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
index f1db89d..de1ee84 100644
--- a/compatibility_matrices/compatibility_matrix.current.xml
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -56,6 +56,14 @@
         </interface>
     </hal>
     <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.sv</name>
+        <version>1.0</version>
+        <interface>
+            <name>ISurroundView</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
         <name>android.hardware.automotive.vehicle</name>
         <version>2.0</version>
         <interface>
diff --git a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
index 278d1f4..fb01ad0 100644
--- a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
+++ b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
@@ -336,7 +336,7 @@
     ASSERT_EQ(0U, result.args->formattedMessage_.size());
 }
 
-// Simulates the framework candelling an ongoing prompt
+// Simulates the framework cancelling an ongoing prompt
 TEST_F(ConfirmationUIHidlTest, AbortTest) {
     static constexpr char test_prompt[] = "Me first, gimme gimme!";
     static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
@@ -354,6 +354,92 @@
     ASSERT_EQ(0U, result.args->formattedMessage_.size());
 }
 
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest1) {
+    static constexpr char test_prompt[] =
+            "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+            "WWWWWWWWWWWWWW";
+    static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+    sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+    hidl_string prompt_text(test_prompt);
+    hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+    ASSERT_HAL_CALL(ResponseCode::OK,
+                    confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
+
+    confirmator().abort();
+
+    auto result = conf_cb->WaitForCallback();
+    ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+    ASSERT_EQ(0U, result.args->confirmationToken_.size());
+    ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 100 'W' characters as required by
+// the design guidelines in magnified mode.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest1Magnified) {
+    static constexpr char test_prompt[] =
+            "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
+            "WWWWWWWWWWWWWW";
+    static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+    sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+    hidl_string prompt_text(test_prompt);
+    hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+    ASSERT_HAL_CALL(ResponseCode::OK,
+                    confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+                                                         {UIOption::AccessibilityMagnified}));
+
+    confirmator().abort();
+
+    auto result = conf_cb->WaitForCallback();
+    ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+    ASSERT_EQ(0U, result.args->confirmationToken_.size());
+    ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest2) {
+    static constexpr char test_prompt[] =
+            "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+            "WWWWWWWWWWWW WWWWWWWWWWWW";
+    static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+    sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+    hidl_string prompt_text(test_prompt);
+    hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+    ASSERT_HAL_CALL(ResponseCode::OK,
+                    confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
+
+    confirmator().abort();
+
+    auto result = conf_cb->WaitForCallback();
+    ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+    ASSERT_EQ(0U, result.args->confirmationToken_.size());
+    ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
+// Tests if the confirmation dialog can successfully render 8 groups of 12 'W' characters as
+// required by the design guidelines in magnified mode.
+TEST_F(ConfirmationUIHidlTest, PortableMessageTest2Magnified) {
+    static constexpr char test_prompt[] =
+            "WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW WWWWWWWWWWWW "
+            "WWWWWWWWWWWW WWWWWWWWWWWW";
+    static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
+    sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
+    hidl_string prompt_text(test_prompt);
+    hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
+    ASSERT_HAL_CALL(ResponseCode::OK,
+                    confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en",
+                                                         {UIOption::AccessibilityMagnified}));
+
+    confirmator().abort();
+
+    auto result = conf_cb->WaitForCallback();
+    ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
+    ASSERT_EQ(0U, result.args->confirmationToken_.size());
+    ASSERT_EQ(0U, result.args->formattedMessage_.size());
+}
+
 // Passing malformed UTF-8 to the confirmation UI
 // This test passes a string that ends in the middle of a multibyte character
 TEST_F(ConfirmationUIHidlTest, MalformedUTF8Test1) {
diff --git a/current.txt b/current.txt
index 09fdd69..5f5758c 100644
--- a/current.txt
+++ b/current.txt
@@ -605,15 +605,15 @@
 5751f230e86a36111e7c5b995577cbf89d8df76c8e6c7641199198f3db3a93f7 android.hardware.wifi@1.3::IWifiStaIface
 
 # HALs released in Android R
-e966a3437d6a98d9d9e14e9d672088771716031900c0deb55a0946c751a03a44 android.hardware.audio@6.0::types
+822369cf4dc16a6f6b9622bcf86cbdc0b692dc82193fc15e967767175cbfdd8f android.hardware.audio@6.0::types
 7241bd4596a927cd46d4b82f5e29e2cbe57f194aa1b25555f1d1d352e8b15c61 android.hardware.audio@6.0::IDevice
 2402876cbc23c0de3690a665eca84fd3857d1808dba5cad25ce272f81ecef8c9 android.hardware.audio@6.0::IDevicesFactory
 bca5379d5065e2e08b6ad7308ffc8a71a972fc0698bec678ea32eea786d01cb5 android.hardware.audio@6.0::IPrimaryDevice
 fd1f1b29f26b42e886220f04a08086c00e5ade9d7b53f095438e578ab9d42a93 android.hardware.audio@6.0::IStream
 2df5d5866b37776f25079c0e54b54350a2abe4e025a59c9e02a7d3abe8ca00e8 android.hardware.audio@6.0::IStreamIn
-78e4138cc8307c11fc777c3bd376e581ba4ba48196b05ca1d7cdfa515c87b48a android.hardware.audio@6.0::IStreamOut
+e6cd2b7c1a86b6ca683c0224ffde3b73aa14f6487de9f46833e539d26d1b3b5c android.hardware.audio@6.0::IStreamOut
 997fdaad7a9d17ee7e01feb7031a753e2365e72ad30b11d950e9183fabdf3844 android.hardware.audio@6.0::IStreamOutCallback
-167ed5cfb7d91db2e2bf20f1320c1a9004eeb768e26f535e0f7db94a21867d21 android.hardware.audio.common@6.0::types
+bee662c62d997d8065e2bcb5c1e7a9578931f22ce28fd02c219fdb4d0630abf7 android.hardware.audio.common@6.0::types
 817930d58412d662cb45e641c50cb62c727e4a3e3ffe7029a53cad9677b97d58 android.hardware.audio.effect@6.0::types
 525bec6b44f1103869c269a128d51b8dccd73af5340ba863c8886c68357c7faf android.hardware.audio.effect@6.0::IAcousticEchoCancelerEffect
 8d76bbe3719d051a8e9a1dcf9244f37f5b0a491feb249fa48391edf7cb4f3131 android.hardware.audio.effect@6.0::IAutomaticGainControlEffect
@@ -640,6 +640,7 @@
 b8c63679e1a3874b356f3e691aecce1191d38f59063cf2ed2dce8a9d4cabf00e android.hardware.camera.device@3.6::ICameraDevice
 daad72a2f482d8a1f660d0e99ac1b78fedb414a4cfbe3fa56b2cd480e5d6f0cb android.hardware.camera.provider@2.6::ICameraProvider
 8f8d9463508ff9cae88eb35c429fd0e2dbca0ca8f5de7fdf836cc0c4370becb6 android.hardware.camera.provider@2.6::ICameraProviderCallback
+a35d5151b48505f06a775b38c0e2e265f80a845d92802324c643565807f81c53 android.hardware.camera.device@3.6::types
 c1aa508d00b66ed5feefea398fd5edf28fa651ac89773adad7dfda4e0a73a952 android.hardware.cas@1.2::ICas
 9811f867def49b420d8c707f7e38d3bdd64f835244e1d2a5e9762ab9835672dc android.hardware.cas@1.2::ICasListener
 f18695dd36ee205640b8326a17453858a7b4596653aaa6ef0016b0aef1bd4dac android.hardware.cas@1.2::IMediaCasService
@@ -678,7 +679,20 @@
 6e904be0ddca5ae1de8eba020e6c38ed935ea7d80cd08f47787f137a0ca58555 android.hardware.neuralnetworks@1.3::IFencedExecutionCallback
 2b0b10d2ea7a18a4048cd0eb83d35c19a817aeee95f65807fc31f4ef21381397 android.hardware.neuralnetworks@1.3::IPreparedModel
 eee3430cc86c97c7b407495863d8fb61da6f1a64b7721e77b9b4909b11b174e9 android.hardware.neuralnetworks@1.3::IPreparedModelCallback
-dd39887aa4fb60ce60ea9cc043edeadbbae6e922d09d3946311b0b410024ae14 android.hardware.neuralnetworks@1.3::types
+c9320b04ec302624985180a02d591bea5e435601fc411a6cabb58878e4e1ad68 android.hardware.neuralnetworks@1.3::types
+b335c3c732c799b299fa61c6de6260ab4d20cbd0ec21acd6db14d8156c745d0b android.hardware.tv.tuner@1.0::types
+adab52e647d1a1ccfbdabdfc9c73352f8e834b61322e505bc6e3d3a0d3acc259 android.hardware.tv.tuner@1.0::IDemux
+548e1a16fc4f779346e14968a63cd6f160e1e2c8b8c99256b2bac24a24b52a9a android.hardware.tv.tuner@1.0::IDescrambler
+b84597d59f0f1d03c9997d60eb15280f3950c287d46016240d89859789db4d47 android.hardware.tv.tuner@1.0::IDvr
+e512a8b4ddef0d0c29d9f68101363dca7616033a24bab86bfca0829661e7484c android.hardware.tv.tuner@1.0::IDvrCallback
+c113b5bed45b3a67ee3f15de1482742a8fd8d96e45ec72e628ee9be6301d51d7 android.hardware.tv.tuner@1.0::IFilter
+8bbc6bde44573edf800cda2b457852175786a3c73f36666bfb2d3afdce3d0dfa android.hardware.tv.tuner@1.0::IFilterCallback
+ccd985e820ed92a5cb55f524b3549462483d21824ca2df0276f5bc2f42878ea3 android.hardware.tv.tuner@1.0::IFrontend
+818587bab10f2534b5a1ef70ed9bae99019f7d453b2fcb2dda01c6db91c3a805 android.hardware.tv.tuner@1.0::IFrontendCallback
+83de3964a800a0e707731adcbcbfbaa56868549233e4c8dcccc8969fab727d38 android.hardware.tv.tuner@1.0::ILnb
+b2310785bdb55f97bbbb2176e2ee73ed8d2a7ce5895bd20c997b90c5f2868ad8 android.hardware.tv.tuner@1.0::ILnbCallback
+4788787e662293d526ff7789fc24e82533e7f6ff99a967ebc3e3ec6b17628796 android.hardware.tv.tuner@1.0::ITimeFilter
+c350c7783843e0c7cf30f90c918770b0d3c09fc0fe5e532e2f2e7210fcfe71c9 android.hardware.tv.tuner@1.0::ITuner
 3e01d4446cd69fd1c48f8572efd97487bc179564b32bd795800b97bbe10be37b android.hardware.wifi@1.4::IWifi
 c67aaf26a7a40d14ea61e70e20afacbd0bb906df1704d585ac8599fbb69dd44b android.hardware.wifi.hostapd@1.2::IHostapd
 2b5a7ea572b736030c64a3b4043af244425477c4672301780fe15aba5ed393d9 android.hardware.wifi.hostapd@1.2::types
@@ -688,13 +702,9 @@
 77531c8d048f8f8ae532babd0ca86332a865ec9aace1b051226ef2b21123e645 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
 98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
 99f5c26b952271d1246c957e1d0271fa39445ee65cc93aa7c187834f98914a33 android.hardware.radio@1.5::types
-7fefa2cc5b3b3be10b5cff5c5dc195385f491d4bf23ca65f9c6b3c30c8753a33 android.hardware.radio@1.5::IRadio
+890ecacaaa6660802bac01bbbe5f16b1eb1d6a3a3f0e5b398be5cec76a5ab673 android.hardware.radio@1.5::IRadio
 e96ae1c3a9c0689002ec2318e9c587f4f607c16a75a3cd38788b77eb91072021 android.hardware.radio@1.5::IRadioIndication
 829d3827eeb5a8f563e80fe627419b3231012fc02bc2e79782ec5e9ad9f799a4 android.hardware.radio@1.5::IRadioResponse
-4c4ce691df02faa28c0729e2a033ec464e1d72699be8bcde4dfb141313dbeba8 android.hardware.radio.config@1.3::types
-a2977755bc5f1ef47f04b7f2400632efda6218e1515dba847da487145cfabc4f android.hardware.radio.config@1.3::IRadioConfig
-742360c775313438b0f82256eac62fb5bbc76a6ae6f388573f3aa142fb2c1eea android.hardware.radio.config@1.3::IRadioConfigIndication
-0006ab8e8b0910cbd3bbb08d5f17d5fac7d65a2bdad5f2334e4851db9d1e6fa8 android.hardware.radio.config@1.3::IRadioConfigResponse
 3ca6616381080bdd6c08141ad12775a94ae868c58b02b1274ae3326f7de724ab android.hardware.sensors@2.1::ISensors
 3d4141c6373cd9ca02fe221a7d12343840de2255d032c38248fe8e35816b58b2 android.hardware.sensors@2.1::ISensorsCallback
 8051cc50fc90ed447f058a8b15d81f35a65f1bd9004b1de4f127edeb89b47978 android.hardware.sensors@2.1::types
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
index cbdd87b..1bef663 100644
--- a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -20,6 +20,7 @@
 #include <unistd.h>
 
 #include <functional>
+#include <tuple>
 #include <vector>
 
 #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
@@ -27,6 +28,7 @@
 #include <cutils/native_handle.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
+#include <hidl/HidlSupport.h>
 #include <hidl/ServiceManagement.h>
 #include <log/log.h>
 
@@ -39,13 +41,18 @@
 using ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
 using ::android::hardware::dumpstate::V1_1::toString;
 
-class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> {
+// Base class common to all dumpstate HAL v1.1 tests.
+template <typename T>
+class DumpstateHidl1_1TestBase : public ::testing::TestWithParam<T> {
   protected:
     virtual void SetUp() override { GetService(); }
 
+    virtual std::string GetInstanceName() = 0;
+
     void GetService() {
-        dumpstate = IDumpstateDevice::getService(GetParam());
-        ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance";
+        const std::string instance_name = GetInstanceName();
+        dumpstate = IDumpstateDevice::getService(instance_name);
+        ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance " << instance_name;
     }
 
     void ToggleVerboseLogging(bool enable) {
@@ -78,77 +85,76 @@
     sp<IDumpstateDevice> dumpstate;
 };
 
-#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \
-    TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); }
+// Tests that don't need to iterate every single DumpstateMode value for dumpstateBoard_1_1.
+class DumpstateHidl1_1GeneralTest : public DumpstateHidl1_1TestBase<std::string> {
+  protected:
+    virtual std::string GetInstanceName() override { return GetParam(); }
+};
 
-// We use a macro to define individual test cases instead of hidl_enum_range<> because some HAL
-// implementations are lazy and may call exit() at the end of dumpstateBoard(), which would cause
-// DEAD_OBJECT errors after the first iteration. Separate cases re-get the service each time as part
-// of SetUp(), and also provide better separation of concerns when specific modes are problematic.
-#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body)       \
-    TEST_FOR_DUMPSTATE_MODE(name, body, FULL);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE);  \
-    TEST_FOR_DUMPSTATE_MODE(name, body, REMOTE);       \
-    TEST_FOR_DUMPSTATE_MODE(name, body, WEAR);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
-    TEST_FOR_DUMPSTATE_MODE(name, body, WIFI);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);      \
-    TEST_FOR_DUMPSTATE_MODE(name, body, PROTO);
+// Tests that iterate every single DumpstateMode value for dumpstateBoard_1_1.
+class DumpstateHidl1_1PerModeTest
+    : public DumpstateHidl1_1TestBase<std::tuple<std::string, DumpstateMode>> {
+  protected:
+    virtual std::string GetInstanceName() override { return std::get<0>(GetParam()); }
+
+    DumpstateMode GetMode() { return std::get<1>(GetParam()); }
+
+    // Will only execute additional_assertions when status == expected.
+    void AssertStatusForMode(const Return<DumpstateStatus>& status, const DumpstateStatus expected,
+                             std::function<void()> additional_assertions = nullptr) {
+        ASSERT_TRUE(status.isOk())
+                << "Status should be ok and return a more specific DumpstateStatus: "
+                << status.description();
+        if (GetMode() == DumpstateMode::DEFAULT) {
+            ASSERT_EQ(expected, status)
+                    << "Required mode (DumpstateMode::" << toString(GetMode())
+                    << "): status should be DumpstateStatus::" << toString(expected)
+                    << ", but got DumpstateStatus::" << toString(status);
+        } else {
+            // The rest of the modes are optional to support, but they MUST return either the
+            // expected value or UNSUPPORTED_MODE.
+            ASSERT_TRUE(status == expected || status == DumpstateStatus::UNSUPPORTED_MODE)
+                    << "Optional mode (DumpstateMode::" << toString(GetMode())
+                    << "): status should be DumpstateStatus::" << toString(expected)
+                    << " or DumpstateStatus::UNSUPPORTED_MODE, but got DumpstateStatus::"
+                    << toString(status);
+        }
+        if (status == expected && additional_assertions != nullptr) {
+            additional_assertions();
+        }
+    }
+};
 
 constexpr uint64_t kDefaultTimeoutMillis = 30 * 1000;  // 30 seconds
 
-// Will only execute additional_assertions when status == expected.
-void AssertStatusForMode(const DumpstateMode mode, const Return<DumpstateStatus>& status,
-                         const DumpstateStatus expected,
-                         std::function<void()> additional_assertions = nullptr) {
-    ASSERT_TRUE(status.isOk()) << "Status should be ok and return a more specific DumpstateStatus: "
-                               << status.description();
-    if (mode == DumpstateMode::DEFAULT) {
-        ASSERT_EQ(expected, status) << "Required mode (DumpstateMode::" << toString(mode)
-                                    << "): status should be DumpstateStatus::" << toString(expected)
-                                    << ", but got DumpstateStatus::" << toString(status);
-    } else {
-        // The rest of the modes are optional to support, but they MUST return either the expected
-        // value or UNSUPPORTED_MODE.
-        ASSERT_TRUE(status == expected || status == DumpstateStatus::UNSUPPORTED_MODE)
-                << "Optional mode (DumpstateMode::" << toString(mode)
-                << "): status should be DumpstateStatus::" << toString(expected)
-                << " or DumpstateStatus::UNSUPPORTED_MODE, but got DumpstateStatus::"
-                << toString(status);
-    }
-    if (status == expected && additional_assertions != nullptr) {
-        additional_assertions();
-    }
-}
-
 // Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestNullHandle) {
     EnableVerboseLogging();
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(nullptr, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
-});
+    AssertStatusForMode(status, DumpstateStatus::ILLEGAL_ARGUMENT);
+}
 
 // Negative test: make sure dumpstateBoard() ignores a handle with no FD.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestHandleWithNoFd) {
     EnableVerboseLogging();
 
     native_handle_t* handle = native_handle_create(0, 0);
     ASSERT_NE(handle, nullptr) << "Could not create native_handle";
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
+    AssertStatusForMode(status, DumpstateStatus::ILLEGAL_ARGUMENT);
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Positive test: make sure dumpstateBoard() writes something to the FD.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestOk) {
     EnableVerboseLogging();
 
     // Index 0 corresponds to the read end of the pipe; 1 to the write end.
@@ -160,9 +166,9 @@
     handle->data[0] = fds[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds]() {
+    AssertStatusForMode(status, DumpstateStatus::OK, [&fds]() {
         // Check that at least one byte was written.
         char buff;
         ASSERT_EQ(1, read(fds[0], &buff, 1)) << "Dumped nothing";
@@ -170,10 +176,10 @@
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestHandleWithTwoFds) {
     EnableVerboseLogging();
 
     int fds1[2];
@@ -187,9 +193,9 @@
     handle->data[1] = fds2[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds1, &fds2]() {
+    AssertStatusForMode(status, DumpstateStatus::OK, [&fds1, &fds2]() {
         // Check that at least one byte was written to one of the FDs.
         char buff;
         size_t read1 = read(fds1[0], &buff, 1);
@@ -200,10 +206,10 @@
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Make sure dumpstateBoard_1_1 actually validates its arguments.
-TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestInvalidModeArgument_Negative) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -225,7 +231,7 @@
     native_handle_delete(handle);
 }
 
-TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestInvalidModeArgument_Undefined) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -248,7 +254,7 @@
 }
 
 // Positive test: make sure dumpstateBoard() from 1.0 doesn't fail.
-TEST_P(DumpstateHidl1_1Test, Test1_0MethodOk) {
+TEST_P(DumpstateHidl1_1GeneralTest, Test1_0MethodOk) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -272,7 +278,7 @@
 
 // Make sure disabling verbose logging behaves correctly. Some info is still allowed to be emitted,
 // but it can't have privacy/storage/battery impacts.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestVerboseLoggingDisabled, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestDeviceLoggingDisabled) {
     DisableVerboseLogging();
 
     // Index 0 corresponds to the read end of the pipe; 1 to the write end.
@@ -284,31 +290,31 @@
     handle->data[0] = fds[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
     // We don't include additional assertions here about the file passed in. If verbose logging is
     // disabled, the OEM may choose to include nothing at all, but it is allowed to include some
     // essential information based on the mode as long as it isn't private user information.
-    AssertStatusForMode(mode, status, DumpstateStatus::OK);
+    AssertStatusForMode(status, DumpstateStatus::OK);
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Double-enable is perfectly valid, but the second call shouldn't do anything.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedEnable) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedEnable) {
     EnableVerboseLogging();
     EnableVerboseLogging();
 }
 
 // Double-disable is perfectly valid, but the second call shouldn't do anything.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedDisable) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedDisable) {
     DisableVerboseLogging();
     DisableVerboseLogging();
 }
 
 // Toggling in short order is perfectly valid.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedToggle) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedToggle) {
     EnableVerboseLogging();
     DisableVerboseLogging();
     EnableVerboseLogging();
@@ -316,8 +322,23 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-        PerInstance, DumpstateHidl1_1Test,
+        PerInstance, DumpstateHidl1_1GeneralTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
+// Includes the mode's name as part of the description string.
+static inline std::string PrintInstanceNameToStringWithMode(
+        const testing::TestParamInfo<std::tuple<std::string, DumpstateMode>>& info) {
+    return android::hardware::PrintInstanceNameToString(
+                   testing::TestParamInfo(std::get<0>(info.param), info.index)) +
+           "_" + toString(std::get<1>(info.param));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstanceAndMode, DumpstateHidl1_1PerModeTest,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IDumpstateDevice::descriptor)),
+                         testing::ValuesIn(android::hardware::hidl_enum_range<DumpstateMode>())),
+        PrintInstanceNameToStringWithMode);
+
 }  // namespace
diff --git a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
index c78c358..3becace 100644
--- a/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
+++ b/graphics/composer/2.2/utils/vts/RenderEngineVts.cpp
@@ -69,9 +69,8 @@
                    [](renderengine::LayerSettings& settings) -> renderengine::LayerSettings* {
                        return &settings;
                    });
-    mRenderEngine->drawLayers(mDisplaySettings, compositionLayerPointers,
-                              mGraphicBuffer->getNativeBuffer(), true, std::move(bufferFence),
-                              &readyFence);
+    mRenderEngine->drawLayers(mDisplaySettings, compositionLayerPointers, mGraphicBuffer, true,
+                              std::move(bufferFence), &readyFence);
     int fd = readyFence.release();
     if (fd != -1) {
         ASSERT_EQ(0, sync_wait(fd, -1));
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index 58b7ed3..1416fcc 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -1198,65 +1198,6 @@
 }
 
 /**
- * Test IMapper::set(Usage) remove flag
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageRemoveBit) {
-    uint64_t usage = static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN);
-    hidl_vec<uint8_t> vec;
-    ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
-    testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
-            [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
-                uint64_t realUsage = 0;
-                ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
-                EXPECT_EQ(usage, realUsage);
-            });
-}
-/**
- * Test IMapper::set(Usage) add flag
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageAddBit) {
-    uint64_t usage = mDummyDescriptorInfo.usage | static_cast<uint64_t>(BufferUsage::GPU_TEXTURE);
-    hidl_vec<uint8_t> vec;
-    ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
-    testSet(mDummyDescriptorInfo, gralloc4::MetadataType_Usage, vec,
-            [&](const IMapper::BufferDescriptorInfo& /*info*/, const hidl_vec<uint8_t>& vec) {
-                uint64_t realUsage = 0;
-                ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
-                EXPECT_EQ(usage, realUsage);
-            });
-}
-
-/**
- * Test IMapper::set(Usage) to test protected content
- */
-TEST_P(GraphicsMapperHidlTest, SetUsageProtected) {
-    const native_handle_t* bufferHandle = nullptr;
-    auto info = mDummyDescriptorInfo;
-    info.usage = BufferUsage::PROTECTED | BufferUsage::COMPOSER_OVERLAY;
-
-    bufferHandle = mGralloc->allocate(info, true, true);
-    if (bufferHandle) {
-        GTEST_SUCCEED() << "unable to allocate protected content";
-        return;
-    }
-
-    uint64_t usage = static_cast<uint64_t>(BufferUsage::COMPOSER_OVERLAY);
-    hidl_vec<uint8_t> vec;
-    ASSERT_EQ(NO_ERROR, gralloc4::encodeUsage(usage, &vec));
-
-    Error err = mGralloc->set(bufferHandle, gralloc4::MetadataType_Usage, vec);
-    ASSERT_EQ(err, Error::UNSUPPORTED);
-    vec.resize(0);
-
-    uint64_t realUsage = 0;
-    ASSERT_EQ(Error::NONE, mGralloc->get(bufferHandle, gralloc4::MetadataType_Usage, &vec));
-    ASSERT_EQ(NO_ERROR, gralloc4::decodeUsage(vec, &realUsage));
-    EXPECT_EQ(info.usage, realUsage);
-}
-
-/**
  * Test IMapper::set(AllocationSize)
  */
 TEST_P(GraphicsMapperHidlTest, SetAllocationSize) {
diff --git a/health/2.0/default/HealthImplDefault.cpp b/health/2.0/default/HealthImplDefault.cpp
index e3cbefd..08fee9e 100644
--- a/health/2.0/default/HealthImplDefault.cpp
+++ b/health/2.0/default/HealthImplDefault.cpp
@@ -21,18 +21,6 @@
 using android::hardware::health::V2_0::implementation::Health;
 
 static struct healthd_config gHealthdConfig = {
-    .batteryStatusPath = android::String8(android::String8::kEmptyString),
-    .batteryHealthPath = android::String8(android::String8::kEmptyString),
-    .batteryPresentPath = android::String8(android::String8::kEmptyString),
-    .batteryCapacityPath = android::String8(android::String8::kEmptyString),
-    .batteryVoltagePath = android::String8(android::String8::kEmptyString),
-    .batteryTemperaturePath = android::String8(android::String8::kEmptyString),
-    .batteryTechnologyPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentNowPath = android::String8(android::String8::kEmptyString),
-    .batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),
-    .batteryChargeCounterPath = android::String8(android::String8::kEmptyString),
-    .batteryFullChargePath = android::String8(android::String8::kEmptyString),
-    .batteryCycleCountPath = android::String8(android::String8::kEmptyString),
     .energyCounter = nullptr,
     .boot_min_cap = 0,
     .screen_on = nullptr};
diff --git a/health/utils/libhealthloop/utils.cpp b/health/utils/libhealthloop/utils.cpp
index 053fd19..cd8c7a9 100644
--- a/health/utils/libhealthloop/utils.cpp
+++ b/health/utils/libhealthloop/utils.cpp
@@ -28,21 +28,6 @@
     *healthd_config = {
             .periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
             .periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,
-            .batteryStatusPath = String8(String8::kEmptyString),
-            .batteryHealthPath = String8(String8::kEmptyString),
-            .batteryPresentPath = String8(String8::kEmptyString),
-            .batteryCapacityPath = String8(String8::kEmptyString),
-            .batteryVoltagePath = String8(String8::kEmptyString),
-            .batteryTemperaturePath = String8(String8::kEmptyString),
-            .batteryTechnologyPath = String8(String8::kEmptyString),
-            .batteryCurrentNowPath = String8(String8::kEmptyString),
-            .batteryCurrentAvgPath = String8(String8::kEmptyString),
-            .batteryChargeCounterPath = String8(String8::kEmptyString),
-            .batteryFullChargePath = String8(String8::kEmptyString),
-            .batteryCycleCountPath = String8(String8::kEmptyString),
-            .batteryCapacityLevelPath = String8(String8::kEmptyString),
-            .batteryChargeTimeToFullNowPath = String8(String8::kEmptyString),
-            .batteryFullChargeDesignCapacityUahPath = String8(String8::kEmptyString),
             .energyCounter = NULL,
             .boot_min_cap = 0,
             .screen_on = NULL,
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 08d8e6b..daaf22e 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -1584,6 +1584,17 @@
      * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
      *      Set to true to specify NCHW data layout for input0 and output0.
      *      Available since HAL version 1.2.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
      *
      * Inputs (resizing by scale, since HAL version 1.2):
      * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
@@ -1602,6 +1613,17 @@
      *      {@link OperandType::FLOAT32} otherwise.
      * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
      *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
      *
      * Outputs:
      * * 0: The output 4-D tensor, of shape
@@ -4870,6 +4892,17 @@
      *      height of the output tensor.
      * * 3: An {@link OperandType::BOOL} scalar, default to false.
      *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
      *
      * Inputs (resizing by scale):
      * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
@@ -4888,6 +4921,17 @@
      *      {@link OperandType::FLOAT32} otherwise.
      * * 3: An {@link OperandType::BOOL} scalar, default to false.
      *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
      *
      * Outputs:
      * * 0: The output 4-D tensor, of shape
diff --git a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
index 8f2d4b7..7da2da9 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateModel.cpp
@@ -578,6 +578,8 @@
     // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
     //   SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
     //   layout parameter.
+    //   RESIZE_BILINEAR and RESIZE_NEAREST_NEIGHBOR can have optional
+    //   align_corners and half_pixel_centers parameters.
     // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
     //   parameter.
     switch (op.type) {
@@ -600,7 +602,12 @@
             }
         } break;
         case OperationType::RESIZE_BILINEAR: {
-            if (op.inputs.size() == 4 && input == 3) {
+            if (op.inputs.size() >= 4 && input >= 3) {
+                return true;
+            }
+        } break;
+        case OperationType::RESIZE_NEAREST_NEIGHBOR: {
+            if (op.inputs.size() >= 5 && input >= 3) {
                 return true;
             }
         } break;
@@ -686,7 +693,9 @@
     //   parameter.
     if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
         (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
-        (op.type == OperationType::SOFTMAX && op.inputs.size() == 2)) {
+        (op.type == OperationType::SOFTMAX && op.inputs.size() == 2) ||
+        (op.type == OperationType::RESIZE_BILINEAR && op.inputs.size() < 6) ||
+        (op.type == OperationType::RESIZE_NEAREST_NEIGHBOR && op.inputs.size() < 6)) {
         return true;
     }
     return false;
diff --git a/radio/1.5/IRadio.hal b/radio/1.5/IRadio.hal
index 0b50436..87824e2 100644
--- a/radio/1.5/IRadio.hal
+++ b/radio/1.5/IRadio.hal
@@ -238,7 +238,8 @@
      * 1) Emergency call is completed; or
      * 2) Another setRadioPower_1_5 is issued with forEmergencyCall being false or
      * preferredForEmergencyCall being false; or
-     * 3) Timeout after a long period of time.
+     * 3) Timeout after 30 seconds if dial or emergencyDial is not called.
+     * Once one of these conditions is reached, the modem should move into normal operation.
      *
      * @param serial Serial number of request.
      * @param powerOn To turn on radio -> on = true, to turn off radio -> on = false.
diff --git a/tv/tuner/1.0/default/Frontend.cpp b/tv/tuner/1.0/default/Frontend.cpp
index dd2f8a6..7e206a7 100644
--- a/tv/tuner/1.0/default/Frontend.cpp
+++ b/tv/tuner/1.0/default/Frontend.cpp
@@ -81,6 +81,10 @@
 Return<Result> Frontend::scan(const FrontendSettings& /* settings */, FrontendScanType /* type */) {
     ALOGV("%s", __FUNCTION__);
 
+    FrontendScanMessage msg;
+    msg.isLocked(true);
+    mCallback->onScanMessage(FrontendScanMessageType::LOCKED, msg);
+
     return Result::SUCCESS;
 }
 
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 820c58c..f693e7c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright 2020 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,6 +44,8 @@
 #include <iostream>
 #include <map>
 
+#include "VtsHalTvTunerV1_0TestConfigurations.h"
+
 #define WAIT_TIMEOUT 3000000000
 #define WAIT_TIMEOUT_data_ready 3000000000 * 4
 
@@ -84,9 +86,11 @@
 using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
 using android::hardware::tv::tuner::V1_0::FrontendEventType;
 using android::hardware::tv::tuner::V1_0::FrontendId;
+using android::hardware::tv::tuner::V1_0::FrontendInfo;
 using android::hardware::tv::tuner::V1_0::FrontendInnerFec;
 using android::hardware::tv::tuner::V1_0::FrontendScanMessage;
 using android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
+using android::hardware::tv::tuner::V1_0::FrontendScanType;
 using android::hardware::tv::tuner::V1_0::FrontendSettings;
 using android::hardware::tv::tuner::V1_0::IDemux;
 using android::hardware::tv::tuner::V1_0::IDescrambler;
@@ -103,6 +107,8 @@
 using android::hardware::tv::tuner::V1_0::RecordStatus;
 using android::hardware::tv::tuner::V1_0::Result;
 
+using ::testing::AssertionResult;
+
 namespace {
 
 using FilterMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
@@ -150,11 +156,7 @@
 // const uint16_t FMQ_SIZE_4K = 0x1000;
 const uint32_t FMQ_SIZE_1M = 0x100000;
 const uint32_t FMQ_SIZE_16M = 0x1000000;
-
-struct FilterConf {
-    DemuxFilterType type;
-    DemuxFilterSettings setting;
-};
+const uint8_t FRONTEND_EVENT_CALLBACK_WAIT_COUNT = 4;
 
 enum FilterEventType : uint8_t {
     UNDEFINED,
@@ -172,6 +174,7 @@
     PlaybackSettings setting;
 };
 
+/******************************** Start FrontendCallback **********************************/
 class FrontendCallback : public IFrontendCallback {
   public:
     virtual Return<void> onEvent(FrontendEventType frontendEventType) override {
@@ -182,28 +185,38 @@
         return Void();
     }
 
-    virtual Return<void> onScanMessage(FrontendScanMessageType /* type */,
-                                       const FrontendScanMessage& /* message */) override {
+    virtual Return<void> onScanMessage(FrontendScanMessageType type,
+                                       const FrontendScanMessage& message) override {
         android::Mutex::Autolock autoLock(mMsgLock);
+        ALOGD("[vts] scan message. Type: %d", mScanMessageType);
         mScanMessageReceived = true;
+        mScanMessageType = type;
+        mScanLockMessageReceived =
+                mScanLockMessageReceived | (type == FrontendScanMessageType::LOCKED);
+        mScanMessage = message;
         mMsgCondition.signal();
         return Void();
     };
 
-    void testOnEvent(sp<IFrontend>& frontend, FrontendSettings settings);
-    void testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSettings settings);
+    void tuneTestOnEventReceive(sp<IFrontend>& frontend, FrontendSettings settings);
+    void tuneTestOnLock(sp<IFrontend>& frontend, FrontendSettings settings);
+    void scanTestOnMessageLock(sp<IFrontend>& frontend, FrontendSettings settings,
+                               FrontendScanType type);
 
   private:
     bool mEventReceived = false;
-    bool mDiseqcMessageReceived = false;
     bool mScanMessageReceived = false;
+    bool mScanLockMessageReceived = false;
     FrontendEventType mEventType;
+    FrontendScanMessageType mScanMessageType;
+    FrontendScanMessage mScanMessage;
     hidl_vec<uint8_t> mEventMessage;
     android::Mutex mMsgLock;
     android::Condition mMsgCondition;
+    uint8_t mOnEvenRetry = 0;
 };
 
-void FrontendCallback::testOnEvent(sp<IFrontend>& frontend, FrontendSettings settings) {
+void FrontendCallback::tuneTestOnEventReceive(sp<IFrontend>& frontend, FrontendSettings settings) {
     Result result = frontend->tune(settings);
 
     EXPECT_TRUE(result == Result::SUCCESS);
@@ -215,29 +228,77 @@
             return;
         }
     }
+    mEventReceived = false;
 }
 
-void FrontendCallback::testOnDiseqcMessage(sp<IFrontend>& frontend, FrontendSettings settings) {
+void FrontendCallback::tuneTestOnLock(sp<IFrontend>& frontend, FrontendSettings settings) {
     Result result = frontend->tune(settings);
 
     EXPECT_TRUE(result == Result::SUCCESS);
 
     android::Mutex::Autolock autoLock(mMsgLock);
-    while (!mDiseqcMessageReceived) {
+wait:
+    while (!mEventReceived) {
         if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
-            EXPECT_TRUE(false) << "diseqc message not received within timeout";
+            EXPECT_TRUE(false) << "event not received within timeout";
             return;
         }
     }
+    if (mEventType != FrontendEventType::LOCKED) {
+        ALOGD("[vts] frontend callback event received. Type: %d", mEventType);
+        mEventReceived = false;
+        if (mOnEvenRetry++ < FRONTEND_EVENT_CALLBACK_WAIT_COUNT) {
+            goto wait;
+        }
+    }
+    EXPECT_TRUE(mEventType == FrontendEventType::LOCKED) << "LOCK event not received";
+    mEventReceived = false;
+    mOnEvenRetry = 0;
 }
 
+void FrontendCallback::scanTestOnMessageLock(sp<IFrontend>& frontend, FrontendSettings settings,
+                                             FrontendScanType type) {
+    Result result = frontend->scan(settings, type);
+    EXPECT_TRUE(result == Result::SUCCESS);
+    android::Mutex::Autolock autoLock(mMsgLock);
+    int messagesCount = 0;
+
+wait:
+    int count = 0;
+    while (!mScanMessageReceived) {
+        if (-ETIMEDOUT == mMsgCondition.waitRelative(mMsgLock, WAIT_TIMEOUT)) {
+            ALOGD("[vts] waiting for scan message callback...");
+            if (count++ > 10) {
+                EXPECT_TRUE(false) << "WAITING TOO LONG!!";
+                return;
+            }
+        }
+    }
+
+    if (mScanMessageType != FrontendScanMessageType::END) {
+        ALOGD("[vts] frontend scan message received. Type: %d", mScanMessageType);
+        mScanMessageReceived = false;
+        if (messagesCount++ > 3) {
+            EXPECT_TRUE(false) << "WAITING ON TOO MANY MSGS!!";
+            return;
+        }
+        goto wait;
+    }
+
+    EXPECT_TRUE(mScanLockMessageReceived) << "scan lock message not received before scan ended";
+    mScanMessageReceived = false;
+    mScanLockMessageReceived = false;
+}
+/******************************** End FrontendCallback **********************************/
+
+/******************************** Start FilterCallback **********************************/
 class FilterCallback : public IFilterCallback {
   public:
     virtual Return<void> onFilterEvent(const DemuxFilterEvent& filterEvent) override {
         android::Mutex::Autolock autoLock(mMsgLock);
         // Temprarily we treat the first coming back filter data on the matching pid a success
         // once all of the MQ are cleared, means we got all the expected output
-        mFilterIdToEvent = filterEvent;
+        mFilterEvent = filterEvent;
         readFilterEventData();
         mPidFilterOutputCount++;
         // mFilterIdToMQ.erase(filterEvent.filterId);
@@ -276,9 +337,9 @@
 
     uint32_t mFilterId;
     FilterEventType mFilterEventType;
-    std::unique_ptr<FilterMQ> mFilterIdToMQ;
-    EventFlag* mFilterIdToMQEventFlag;
-    DemuxFilterEvent mFilterIdToEvent;
+    std::unique_ptr<FilterMQ> mFilterMQ;
+    EventFlag* mFilterMQEventFlag;
+    DemuxFilterEvent mFilterEvent;
 
     android::Mutex mMsgLock;
     android::Mutex mFilterOutputLock;
@@ -313,10 +374,10 @@
 }
 
 void FilterCallback::updateFilterMQ(MQDesc& filterMQDescriptor) {
-    mFilterIdToMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
-    EXPECT_TRUE(mFilterIdToMQ);
-    EXPECT_TRUE(EventFlag::createEventFlag(mFilterIdToMQ->getEventFlagWord(),
-                                           &mFilterIdToMQEventFlag) == android::OK);
+    mFilterMQ = std::make_unique<FilterMQ>(filterMQDescriptor, true /* resetPointers */);
+    EXPECT_TRUE(mFilterMQ);
+    EXPECT_TRUE(EventFlag::createEventFlag(mFilterMQ->getEventFlagWord(), &mFilterMQEventFlag) ==
+                android::OK);
 }
 
 void FilterCallback::updateGoldenOutputMap(string goldenOutputFile) {
@@ -332,7 +393,7 @@
 
 void FilterCallback::filterThreadLoop(DemuxFilterEvent& /* event */) {
     android::Mutex::Autolock autoLock(mFilterOutputLock);
-    // Read from mFilterIdToMQ[event.filterId] per event and filter type
+    // Read from mFilterMQ[event.filterId] per event and filter type
 
     // Assemble to filterOutput[filterId]
 
@@ -345,7 +406,7 @@
 
 bool FilterCallback::readFilterEventData() {
     bool result = false;
-    DemuxFilterEvent filterEvent = mFilterIdToEvent;
+    DemuxFilterEvent filterEvent = mFilterEvent;
     ALOGW("[vts] reading from filter FMQ %d", mFilterId);
     // todo separate filter handlers
     for (int i = 0; i < filterEvent.events.size(); i++) {
@@ -357,6 +418,7 @@
                 mDataLength = filterEvent.events[i].pes().dataLength;
                 break;
             case FilterEventType::MEDIA:
+                mDataLength = filterEvent.events[i].media().dataLength;
                 break;
             case FilterEventType::RECORD:
                 break;
@@ -371,17 +433,19 @@
         // match";
 
         mDataOutputBuffer.resize(mDataLength);
-        result = mFilterIdToMQ->read(mDataOutputBuffer.data(), mDataLength);
+        result = mFilterMQ->read(mDataOutputBuffer.data(), mDataLength);
         EXPECT_TRUE(result) << "can't read from Filter MQ";
 
         /*for (int i = 0; i < mDataLength; i++) {
             EXPECT_TRUE(goldenDataOutputBuffer[i] == mDataOutputBuffer[i]) << "data does not match";
         }*/
     }
-    mFilterIdToMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+    mFilterMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
     return result;
 }
+/******************************** End FilterCallback **********************************/
 
+/******************************** Start DvrCallback **********************************/
 class DvrCallback : public IDvrCallback {
   public:
     virtual Return<void> onRecordStatus(DemuxFilterStatus status) override {
@@ -445,11 +509,10 @@
     uint16_t mDataLength = 0;
     std::vector<uint8_t> mDataOutputBuffer;
 
-    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterIdToMQ;
+    std::map<uint32_t, std::unique_ptr<FilterMQ>> mFilterMQ;
     std::unique_ptr<FilterMQ> mPlaybackMQ;
     std::unique_ptr<FilterMQ> mRecordMQ;
-    std::map<uint32_t, EventFlag*> mFilterIdToMQEventFlag;
-    std::map<uint32_t, DemuxFilterEvent> mFilterIdToEvent;
+    std::map<uint32_t, EventFlag*> mFilterMQEventFlag;
 
     android::Mutex mMsgLock;
     android::Mutex mPlaybackThreadLock;
@@ -635,22 +698,32 @@
     mRecordThreadRunning = false;
     android::Mutex::Autolock autoLock(mRecordThreadLock);
 }
+/********************************** End DvrCallback ************************************/
 
+/***************************** Start Test Implementation ******************************/
 class TunerHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
+        initFrontendConfig();
+        initFrontendScanConfig();
+        initFilterConfig();
     }
 
     sp<ITuner> mService;
 
   protected:
+    static AssertionResult failure() { return ::testing::AssertionFailure(); }
+
+    static AssertionResult success() { return ::testing::AssertionSuccess(); }
+
     static void description(const std::string& description) {
         RecordProperty("description", description);
     }
 
     sp<IFrontend> mFrontend;
+    FrontendInfo mFrontendInfo;
     sp<FrontendCallback> mFrontendCallback;
     sp<IDescrambler> mDescrambler;
     sp<IDemux> mDemux;
@@ -661,274 +734,165 @@
     sp<FilterCallback> mFilterCallback;
     sp<DvrCallback> mDvrCallback;
     MQDesc mFilterMQDescriptor;
-    MQDesc mPlaybackMQDescriptor;
+    MQDesc mDvrMQDescriptor;
     MQDesc mRecordMQDescriptor;
     vector<uint32_t> mUsedFilterIds;
+    hidl_vec<FrontendId> mFeIds;
 
     uint32_t mDemuxId;
-    uint32_t mFilterId;
+    uint32_t mFilterId = -1;
 
     pthread_t mPlaybackshread;
     bool mPlaybackThreadRunning;
 
-    ::testing::AssertionResult createFrontend(int32_t frontendId);
-    ::testing::AssertionResult tuneFrontend(int32_t frontendId);
-    ::testing::AssertionResult stopTuneFrontend(int32_t frontendId);
-    ::testing::AssertionResult closeFrontend(int32_t frontendId);
-    ::testing::AssertionResult createDemux();
-    ::testing::AssertionResult createDemuxWithFrontend(int32_t frontendId,
-                                                       FrontendSettings settings);
-    ::testing::AssertionResult getPlaybackMQDescriptor();
-    ::testing::AssertionResult addPlaybackToDemux(PlaybackSettings setting);
-    ::testing::AssertionResult getRecordMQDescriptor();
-    ::testing::AssertionResult addRecordToDemux(RecordSettings setting);
-    ::testing::AssertionResult addFilterToDemux(DemuxFilterType type, DemuxFilterSettings setting);
-    ::testing::AssertionResult getFilterMQDescriptor();
-    ::testing::AssertionResult closeDemux();
-    ::testing::AssertionResult createDescrambler();
-    ::testing::AssertionResult closeDescrambler();
+    AssertionResult getFrontendIds();
+    AssertionResult getFrontendInfo(uint32_t frontendId);
+    AssertionResult openFrontend(uint32_t frontendId);
+    AssertionResult setFrontendCallback();
+    AssertionResult scanFrontend(FrontendConfig config, FrontendScanType type);
+    AssertionResult stopScanFrontend();
+    AssertionResult tuneFrontend(FrontendConfig config);
+    AssertionResult stopTuneFrontend();
+    AssertionResult closeFrontend();
 
-    ::testing::AssertionResult playbackDataFlowTest(vector<FilterConf> filterConf,
-                                                    PlaybackConf playbackConf,
-                                                    vector<string> goldenOutputFiles);
-    ::testing::AssertionResult recordDataFlowTest(vector<FilterConf> filterConf,
-                                                  RecordSettings recordSetting,
-                                                  vector<string> goldenOutputFiles);
-    ::testing::AssertionResult broadcastDataFlowTest(vector<FilterConf> filterConf,
-                                                     vector<string> goldenOutputFiles);
+    AssertionResult openDemux();
+    AssertionResult setDemuxFrontendDataSource(uint32_t frontendId);
+    AssertionResult closeDemux();
+
+    AssertionResult openDvrInDemux(DvrType type);
+    AssertionResult configDvr(DvrSettings setting);
+    AssertionResult getDvrMQDescriptor();
+
+    AssertionResult openFilterInDemux(DemuxFilterType type);
+    AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
+    AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
+    AssertionResult getFilterMQDescriptor(uint32_t filterId);
+    AssertionResult startFilter(uint32_t filterId);
+    AssertionResult stopFilter(uint32_t filterId);
+    AssertionResult closeFilter(uint32_t filterId);
+
+    AssertionResult createDescrambler();
+    AssertionResult closeDescrambler();
+
+    AssertionResult playbackDataFlowTest(vector<FilterConfig> filterConf, PlaybackConf playbackConf,
+                                         vector<string> goldenOutputFiles);
+    AssertionResult recordDataFlowTest(vector<FilterConfig> filterConf,
+                                       RecordSettings recordSetting,
+                                       vector<string> goldenOutputFiles);
+    AssertionResult broadcastDataFlowTest(vector<string> goldenOutputFiles);
+
+    FilterEventType getFilterEventType(DemuxFilterType type);
 };
 
-::testing::AssertionResult TunerHidlTest::createFrontend(int32_t frontendId) {
+/*========================== Start Frontend APIs Tests Implementation ==========================*/
+AssertionResult TunerHidlTest::getFrontendIds() {
     Result status;
+    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+        status = result;
+        mFeIds = frontendIds;
+    });
+    return AssertionResult(status == Result::SUCCESS);
+}
 
+AssertionResult TunerHidlTest::getFrontendInfo(uint32_t frontendId) {
+    Result status;
+    mService->getFrontendInfo(frontendId, [&](Result result, const FrontendInfo& frontendInfo) {
+        mFrontendInfo = frontendInfo;
+        status = result;
+    });
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::openFrontend(uint32_t frontendId) {
+    Result status;
     mService->openFrontendById(frontendId, [&](Result result, const sp<IFrontend>& frontend) {
         mFrontend = frontend;
         status = result;
     });
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
+    return AssertionResult(status == Result::SUCCESS);
+}
 
+AssertionResult TunerHidlTest::setFrontendCallback() {
+    EXPECT_TRUE(mFrontend) << "Test with openFrontend first.";
     mFrontendCallback = new FrontendCallback();
     auto callbackStatus = mFrontend->setCallback(mFrontendCallback);
-
-    return ::testing::AssertionResult(callbackStatus.isOk());
+    return AssertionResult(callbackStatus.isOk());
 }
 
-::testing::AssertionResult TunerHidlTest::tuneFrontend(int32_t frontendId) {
-    if (createFrontend(frontendId) == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
+AssertionResult TunerHidlTest::scanFrontend(FrontendConfig config, FrontendScanType type) {
+    EXPECT_TRUE(mFrontendCallback)
+            << "test with openFrontend/setFrontendCallback/getFrontendInfo first.";
 
-    // Frontend Settings for testing
-    FrontendSettings frontendSettings;
-    FrontendAtscSettings frontendAtscSettings{
-            .frequency = 0,
-            .modulation = FrontendAtscModulation::UNDEFINED,
-    };
-    frontendSettings.atsc(frontendAtscSettings);
-    mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
+    EXPECT_TRUE(mFrontendInfo.type == config.type)
+            << "FrontendConfig does not match the frontend info of the given id.";
 
-    FrontendDvbtSettings frontendDvbtSettings{
-            .frequency = 0,
-    };
-    frontendSettings.dvbt(frontendDvbtSettings);
-    mFrontendCallback->testOnEvent(mFrontend, frontendSettings);
-
-    return ::testing::AssertionResult(true);
+    mFrontendCallback->scanTestOnMessageLock(mFrontend, config.settings, type);
+    return AssertionResult(true);
 }
 
-::testing::AssertionResult TunerHidlTest::stopTuneFrontend(int32_t frontendId) {
+AssertionResult TunerHidlTest::stopScanFrontend() {
+    EXPECT_TRUE(mFrontend) << "Test with openFrontend first.";
     Result status;
-    if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
+    status = mFrontend->stopScan();
+    return AssertionResult(status == Result::SUCCESS);
+}
 
+AssertionResult TunerHidlTest::tuneFrontend(FrontendConfig config) {
+    EXPECT_TRUE(mFrontendCallback)
+            << "test with openFrontend/setFrontendCallback/getFrontendInfo first.";
+
+    EXPECT_TRUE(mFrontendInfo.type == config.type)
+            << "FrontendConfig does not match the frontend info of the given id.";
+
+    mFrontendCallback->tuneTestOnLock(mFrontend, config.settings);
+    return AssertionResult(true);
+}
+
+AssertionResult TunerHidlTest::stopTuneFrontend() {
+    EXPECT_TRUE(mFrontend) << "Test with openFrontend first.";
+    Result status;
     status = mFrontend->stopTune();
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+    return AssertionResult(status == Result::SUCCESS);
 }
 
-::testing::AssertionResult TunerHidlTest::closeFrontend(int32_t frontendId) {
+AssertionResult TunerHidlTest::closeFrontend() {
+    EXPECT_TRUE(mFrontend) << "Test with openFrontend first.";
     Result status;
-    if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
     status = mFrontend->close();
     mFrontend = nullptr;
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+    mFrontendCallback = nullptr;
+    return AssertionResult(status == Result::SUCCESS);
 }
+/*=========================== End Frontend APIs Tests Implementation ===========================*/
 
-::testing::AssertionResult TunerHidlTest::createDemux() {
+/*============================ Start Demux APIs Tests Implementation ============================*/
+AssertionResult TunerHidlTest::openDemux() {
     Result status;
-
     mService->openDemux([&](Result result, uint32_t demuxId, const sp<IDemux>& demux) {
         mDemux = demux;
         mDemuxId = demuxId;
         status = result;
     });
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+    return AssertionResult(status == Result::SUCCESS);
 }
 
-::testing::AssertionResult TunerHidlTest::createDemuxWithFrontend(int32_t frontendId,
-                                                                  FrontendSettings settings) {
-    Result status;
-
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    if (!mFrontend && createFrontend(frontendId) == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    mFrontendCallback->testOnEvent(mFrontend, settings);
-
-    status = mDemux->setFrontendDataSource(frontendId);
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+AssertionResult TunerHidlTest::setDemuxFrontendDataSource(uint32_t frontendId) {
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mFrontend) << "Test with openFrontend first.";
+    auto status = mDemux->setFrontendDataSource(frontendId);
+    return AssertionResult(status.isOk());
 }
 
-::testing::AssertionResult TunerHidlTest::closeDemux() {
-    Result status;
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    status = mDemux->close();
+AssertionResult TunerHidlTest::closeDemux() {
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    auto status = mDemux->close();
     mDemux = nullptr;
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+    return AssertionResult(status.isOk());
 }
 
-::testing::AssertionResult TunerHidlTest::createDescrambler() {
+AssertionResult TunerHidlTest::openFilterInDemux(DemuxFilterType type) {
     Result status;
-
-    mService->openDescrambler([&](Result result, const sp<IDescrambler>& descrambler) {
-        mDescrambler = descrambler;
-        status = result;
-    });
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    status = mDescrambler->setDemuxSource(mDemuxId);
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    // Test if demux source can be set more than once.
-    status = mDescrambler->setDemuxSource(mDemuxId);
-    return ::testing::AssertionResult(status == Result::INVALID_STATE);
-}
-
-::testing::AssertionResult TunerHidlTest::closeDescrambler() {
-    Result status;
-    if (!mDescrambler && createDescrambler() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    status = mDescrambler->close();
-    mDescrambler = nullptr;
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::addPlaybackToDemux(PlaybackSettings setting) {
-    Result status;
-
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    // Create dvr callback
-    mDvrCallback = new DvrCallback();
-
-    // Add playback input to the local demux
-    mDemux->openDvr(DvrType::PLAYBACK, FMQ_SIZE_1M, mDvrCallback,
-                    [&](Result result, const sp<IDvr>& dvr) {
-                        mDvr = dvr;
-                        status = result;
-                    });
-
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    DvrSettings dvrSetting;
-    dvrSetting.playback(setting);
-    status = mDvr->configure(dvrSetting);
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::getPlaybackMQDescriptor() {
-    Result status;
-
-    if ((!mDemux && createDemux() == ::testing::AssertionFailure()) || !mDvr) {
-        return ::testing::AssertionFailure();
-    }
-
-    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
-        mPlaybackMQDescriptor = dvrMQDesc;
-        status = result;
-    });
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::addRecordToDemux(RecordSettings setting) {
-    Result status;
-
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-
-    // Create dvr callback
-    mDvrCallback = new DvrCallback();
-
-    // Add playback input to the local demux
-    mDemux->openDvr(DvrType::RECORD, FMQ_SIZE_1M, mDvrCallback,
-                    [&](Result result, const sp<IDvr>& dvr) {
-                        mDvr = dvr;
-                        status = result;
-                    });
-
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    DvrSettings dvrSetting;
-    dvrSetting.record(setting);
-    status = mDvr->configure(dvrSetting);
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::getRecordMQDescriptor() {
-    Result status;
-
-    if ((!mDemux && createDemux() == ::testing::AssertionFailure()) || !mDvr) {
-        return ::testing::AssertionFailure();
-    }
-
-    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
-        mRecordMQDescriptor = dvrMQDesc;
-        status = result;
-    });
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::addFilterToDemux(DemuxFilterType type,
-                                                           DemuxFilterSettings setting) {
-    Result status;
-
-    if (!mDemux && createDemux() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
 
     // Create demux callback
     mFilterCallback = new FilterCallback();
@@ -940,21 +904,322 @@
                            status = result;
                        });
 
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
+    if (status == Result::SUCCESS) {
+        mFilterCallback->setFilterEventType(getFilterEventType(type));
     }
 
+    return AssertionResult(status == Result::SUCCESS);
+}
+/*============================ End Demux APIs Tests Implementation ============================*/
+
+/*=========================== Start Filter APIs Tests Implementation ===========================*/
+AssertionResult TunerHidlTest::getNewlyOpenedFilterId(uint32_t& filterId) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mFilter) << "Test with openFilterInDemux first.";
+    EXPECT_TRUE(mFilterCallback) << "Test with openFilterInDemux first.";
+
     mFilter->getId([&](Result result, uint32_t filterId) {
         mFilterId = filterId;
         status = result;
     });
 
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
+    if (status == Result::SUCCESS) {
+        mFilterCallback->setFilterId(mFilterId);
+        mUsedFilterIds.insert(mUsedFilterIds.end(), mFilterId);
+        mFilters[mFilterId] = mFilter;
+        mFilterCallbacks[mFilterId] = mFilterCallback;
+        filterId = mFilterId;
     }
 
-    mFilterCallback->setFilterId(mFilterId);
+    return AssertionResult(status == Result::SUCCESS || status == Result::UNAVAILABLE);
+}
 
+AssertionResult TunerHidlTest::configFilter(DemuxFilterSettings setting, uint32_t filterId) {
+    Result status;
+    EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    status = mFilters[filterId]->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::getFilterMQDescriptor(uint32_t filterId) {
+    Result status;
+    EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
+
+    mFilter->getQueueDesc([&](Result result, const MQDesc& filterMQDesc) {
+        mFilterMQDescriptor = filterMQDesc;
+        status = result;
+    });
+
+    if (status == Result::SUCCESS) {
+        mFilterCallbacks[filterId]->updateFilterMQ(mFilterMQDescriptor);
+    }
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::startFilter(uint32_t filterId) {
+    EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    Result status = mFilters[filterId]->start();
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::stopFilter(uint32_t filterId) {
+    EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    Result status = mFilters[filterId]->stop();
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::closeFilter(uint32_t filterId) {
+    EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
+    Result status = mFilters[filterId]->close();
+    if (status == Result::SUCCESS) {
+        for (int i = 0; i < mUsedFilterIds.size(); i++) {
+            if (mUsedFilterIds[i] == filterId) {
+                mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
+                break;
+            }
+        }
+        mFilterCallbacks.erase(filterId);
+        mFilters.erase(filterId);
+    }
+    return AssertionResult(status == Result::SUCCESS);
+}
+/*=========================== End Filter APIs Tests Implementation ===========================*/
+
+/*======================== Start Descrambler APIs Tests Implementation ========================*/
+AssertionResult TunerHidlTest::createDescrambler() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    mService->openDescrambler([&](Result result, const sp<IDescrambler>& descrambler) {
+        mDescrambler = descrambler;
+        status = result;
+    });
+    if (status != Result::SUCCESS) {
+        return failure();
+    }
+
+    status = mDescrambler->setDemuxSource(mDemuxId);
+    if (status != Result::SUCCESS) {
+        return failure();
+    }
+
+    // Test if demux source can be set more than once.
+    status = mDescrambler->setDemuxSource(mDemuxId);
+    return AssertionResult(status == Result::INVALID_STATE);
+}
+
+AssertionResult TunerHidlTest::closeDescrambler() {
+    Result status;
+    if (!mDescrambler && createDescrambler() == failure()) {
+        return failure();
+    }
+
+    status = mDescrambler->close();
+    mDescrambler = nullptr;
+    return AssertionResult(status == Result::SUCCESS);
+}
+/*========================= End Descrambler APIs Tests Implementation =========================*/
+
+/*============================ Start Dvr APIs Tests Implementation ============================*/
+AssertionResult TunerHidlTest::openDvrInDemux(DvrType type) {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+
+    // Create dvr callback
+    mDvrCallback = new DvrCallback();
+
+    mDemux->openDvr(type, FMQ_SIZE_1M, mDvrCallback, [&](Result result, const sp<IDvr>& dvr) {
+        mDvr = dvr;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::configDvr(DvrSettings setting) {
+    Result status = mDvr->configure(setting);
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+
+AssertionResult TunerHidlTest::getDvrMQDescriptor() {
+    Result status;
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mDvr) << "Test with openDvr first.";
+
+    mDvr->getQueueDesc([&](Result result, const MQDesc& dvrMQDesc) {
+        mDvrMQDescriptor = dvrMQDesc;
+        status = result;
+    });
+
+    return AssertionResult(status == Result::SUCCESS);
+}
+/*============================ End Dvr APIs Tests Implementation ============================*/
+
+/*========================== Start Data Flow Tests Implementation ==========================*/
+AssertionResult TunerHidlTest::broadcastDataFlowTest(vector<string> /*goldenOutputFiles*/) {
+    EXPECT_TRUE(mFrontend) << "Test with openFilterInDemux first.";
+    EXPECT_TRUE(mDemux) << "Test with openDemux first.";
+    EXPECT_TRUE(mFilterCallback) << "Test with getFilterMQDescriptor first.";
+
+    // Data Verify Module
+    std::map<uint32_t, sp<FilterCallback>>::iterator it;
+    for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
+        it->second->testFilterDataOutput();
+    }
+    return success();
+}
+
+/*
+ * TODO: re-enable the tests after finalizing the test refactoring.
+ */
+/*AssertionResult TunerHidlTest::playbackDataFlowTest(
+        vector<FilterConf> filterConf, PlaybackConf playbackConf,
+        vector<string> \/\*goldenOutputFiles\*\/) {
+    Result status;
+    int filterIdsSize;
+    // Filter Configuration Module
+    for (int i = 0; i < filterConf.size(); i++) {
+        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+                    failure() ||
+            // TODO use a map to save the FMQs/EvenFlags and pass to callback
+            getFilterMQDescriptor() == failure()) {
+            return failure();
+        }
+        filterIdsSize = mUsedFilterIds.size();
+        mUsedFilterIds.resize(filterIdsSize + 1);
+        mUsedFilterIds[filterIdsSize] = mFilterId;
+        mFilters[mFilterId] = mFilter;
+        mFilterCallbacks[mFilterId] = mFilterCallback;
+        mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
+        // mDemuxCallback->updateGoldenOutputMap(goldenOutputFiles[i]);
+        status = mFilter->start();
+        if (status != Result::SUCCESS) {
+            return failure();
+        }
+    }
+
+    // Playback Input Module
+    PlaybackSettings playbackSetting = playbackConf.setting;
+    if (addPlaybackToDemux(playbackSetting) == failure() ||
+        getPlaybackMQDescriptor() == failure()) {
+        return failure();
+    }
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
+            return failure();
+        }
+    }
+    mDvrCallback->startPlaybackInputThread(playbackConf, mPlaybackMQDescriptor);
+    status = mDvr->start();
+    if (status != Result::SUCCESS) {
+        return failure();
+    }
+
+    // Data Verify Module
+    std::map<uint32_t, sp<FilterCallback>>::iterator it;
+    for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
+        it->second->testFilterDataOutput();
+    }
+    mDvrCallback->stopPlaybackThread();
+
+    // Clean Up Module
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
+            return failure();
+        }
+    }
+    if (mDvr->stop() != Result::SUCCESS) {
+        return failure();
+    }
+    mUsedFilterIds.clear();
+    mFilterCallbacks.clear();
+    mFilters.clear();
+    return closeDemux();
+}
+
+AssertionResult TunerHidlTest::recordDataFlowTest(vector<FilterConf> filterConf,
+                                                  RecordSettings recordSetting,
+                                                  vector<string> goldenOutputFiles) {
+    Result status;
+    hidl_vec<FrontendId> feIds;
+
+    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
+        status = result;
+        feIds = frontendIds;
+    });
+
+    if (feIds.size() == 0) {
+        ALOGW("[   WARN   ] Frontend isn't available");
+        return failure();
+    }
+
+    FrontendDvbtSettings dvbt{
+            .frequency = 1000,
+    };
+    FrontendSettings settings;
+    settings.dvbt(dvbt);
+
+    int filterIdsSize;
+    // Filter Configuration Module
+    for (int i = 0; i < filterConf.size(); i++) {
+        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
+                    failure() ||
+            // TODO use a map to save the FMQs/EvenFlags and pass to callback
+            getFilterMQDescriptor() == failure()) {
+            return failure();
+        }
+        filterIdsSize = mUsedFilterIds.size();
+        mUsedFilterIds.resize(filterIdsSize + 1);
+        mUsedFilterIds[filterIdsSize] = mFilterId;
+        mFilters[mFilterId] = mFilter;
+    }
+
+    // Record Config Module
+    if (addRecordToDemux(recordSetting) == failure() ||
+        getRecordMQDescriptor() == failure()) {
+        return failure();
+    }
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
+            return failure();
+        }
+    }
+
+    mDvrCallback->startRecordOutputThread(recordSetting, mRecordMQDescriptor);
+    status = mDvr->start();
+    if (status != Result::SUCCESS) {
+        return failure();
+    }
+
+    if (setDemuxFrontendDataSource(feIds[0]) != success()) {
+        return failure();
+    }
+
+    // Data Verify Module
+    mDvrCallback->testRecordOutput();
+
+    // Clean Up Module
+    for (int i = 0; i <= filterIdsSize; i++) {
+        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
+            return failure();
+        }
+    }
+    if (mFrontend->stopTune() != Result::SUCCESS) {
+        return failure();
+    }
+    mUsedFilterIds.clear();
+    mFilterCallbacks.clear();
+    mFilters.clear();
+    return closeDemux();
+}*/
+/*========================= End Data Flow Tests Implementation =========================*/
+
+/*=============================== Start Helper Functions ===============================*/
+FilterEventType TunerHidlTest::getFilterEventType(DemuxFilterType type) {
     FilterEventType eventType = FilterEventType::UNDEFINED;
     switch (type.mainType) {
         case DemuxFilterMainType::TS:
@@ -998,358 +1263,151 @@
         default:
             break;
     }
-    mFilterCallback->setFilterEventType(eventType);
+    return eventType;
+}
+/*============================== End Helper Functions ==============================*/
+/***************************** End Test Implementation *****************************/
 
-    // Configure the filter
-    status = mFilter->configure(setting);
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
+/******************************** Start Test Entry **********************************/
+/*============================== Start Frontend Tests ==============================*/
+TEST_P(TunerHidlTest, getFrontendIds) {
+    description("Get Frontend ids and verify frontends exist");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
 }
 
-::testing::AssertionResult TunerHidlTest::getFilterMQDescriptor() {
-    Result status;
+TEST_P(TunerHidlTest, openFrontend) {
+    description("Open all the existing Frontends and close them");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
 
-    if (!mDemux || !mFilter) {
-        return ::testing::AssertionFailure();
-    }
-
-    mFilter->getQueueDesc([&](Result result, const MQDesc& filterMQDesc) {
-        mFilterMQDescriptor = filterMQDesc;
-        status = result;
-    });
-
-    return ::testing::AssertionResult(status == Result::SUCCESS);
-}
-
-::testing::AssertionResult TunerHidlTest::playbackDataFlowTest(
-        vector<FilterConf> filterConf, PlaybackConf playbackConf,
-        vector<string> /*goldenOutputFiles*/) {
-    Result status;
-    int filterIdsSize;
-    // Filter Configuration Module
-    for (int i = 0; i < filterConf.size(); i++) {
-        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
-                    ::testing::AssertionFailure() ||
-            // TODO use a map to save the FMQs/EvenFlags and pass to callback
-            getFilterMQDescriptor() == ::testing::AssertionFailure()) {
-            return ::testing::AssertionFailure();
-        }
-        filterIdsSize = mUsedFilterIds.size();
-        mUsedFilterIds.resize(filterIdsSize + 1);
-        mUsedFilterIds[filterIdsSize] = mFilterId;
-        mFilters[mFilterId] = mFilter;
-        mFilterCallbacks[mFilterId] = mFilterCallback;
-        mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
-        // mDemuxCallback->updateGoldenOutputMap(goldenOutputFiles[i]);
-        status = mFilter->start();
-        if (status != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-
-    // Playback Input Module
-    PlaybackSettings playbackSetting = playbackConf.setting;
-    if (addPlaybackToDemux(playbackSetting) == ::testing::AssertionFailure() ||
-        getPlaybackMQDescriptor() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-    for (int i = 0; i <= filterIdsSize; i++) {
-        if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-    mDvrCallback->startPlaybackInputThread(playbackConf, mPlaybackMQDescriptor);
-    status = mDvr->start();
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    // Data Verify Module
-    std::map<uint32_t, sp<FilterCallback>>::iterator it;
-    for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
-        it->second->testFilterDataOutput();
-    }
-    mDvrCallback->stopPlaybackThread();
-
-    // Clean Up Module
-    for (int i = 0; i <= filterIdsSize; i++) {
-        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-    if (mDvr->stop() != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-    mUsedFilterIds.clear();
-    mFilterCallbacks.clear();
-    mFilters.clear();
-    return closeDemux();
-}
-
-::testing::AssertionResult TunerHidlTest::broadcastDataFlowTest(
-        vector<FilterConf> filterConf, vector<string> /*goldenOutputFiles*/) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
-
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return ::testing::AssertionFailure();
-    }
-
-    FrontendDvbtSettings dvbt{
-            .frequency = 1000,
-    };
-    FrontendSettings settings;
-    settings.dvbt(dvbt);
-
-    if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
-        return ::testing::AssertionFailure();
-    }
-
-    int filterIdsSize;
-    // Filter Configuration Module
-    for (int i = 0; i < filterConf.size(); i++) {
-        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
-                    ::testing::AssertionFailure() ||
-            // TODO use a map to save the FMQs/EvenFlags and pass to callback
-            getFilterMQDescriptor() == ::testing::AssertionFailure()) {
-            return ::testing::AssertionFailure();
-        }
-        filterIdsSize = mUsedFilterIds.size();
-        mUsedFilterIds.resize(filterIdsSize + 1);
-        mUsedFilterIds[filterIdsSize] = mFilterId;
-        mFilters[mFilterId] = mFilter;
-        mFilterCallbacks[mFilterId] = mFilterCallback;
-        mFilterCallback->updateFilterMQ(mFilterMQDescriptor);
-        status = mFilter->start();
-        if (status != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-
-    // Data Verify Module
-    std::map<uint32_t, sp<FilterCallback>>::iterator it;
-    for (it = mFilterCallbacks.begin(); it != mFilterCallbacks.end(); it++) {
-        it->second->testFilterDataOutput();
-    }
-
-    // Clean Up Module
-    for (int i = 0; i <= filterIdsSize; i++) {
-        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-    if (mFrontend->stopTune() != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-    mUsedFilterIds.clear();
-    mFilterCallbacks.clear();
-    mFilters.clear();
-    return closeDemux();
-}
-
-::testing::AssertionResult TunerHidlTest::recordDataFlowTest(vector<FilterConf> filterConf,
-                                                             RecordSettings recordSetting,
-                                                             vector<string> /*goldenOutputFiles*/) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
-
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return ::testing::AssertionFailure();
-    }
-
-    FrontendDvbtSettings dvbt{
-            .frequency = 1000,
-    };
-    FrontendSettings settings;
-    settings.dvbt(dvbt);
-
-    int filterIdsSize;
-    // Filter Configuration Module
-    for (int i = 0; i < filterConf.size(); i++) {
-        if (addFilterToDemux(filterConf[i].type, filterConf[i].setting) ==
-                    ::testing::AssertionFailure() ||
-            // TODO use a map to save the FMQs/EvenFlags and pass to callback
-            getFilterMQDescriptor() == ::testing::AssertionFailure()) {
-            return ::testing::AssertionFailure();
-        }
-        filterIdsSize = mUsedFilterIds.size();
-        mUsedFilterIds.resize(filterIdsSize + 1);
-        mUsedFilterIds[filterIdsSize] = mFilterId;
-        mFilters[mFilterId] = mFilter;
-    }
-
-    // Record Config Module
-    if (addRecordToDemux(recordSetting) == ::testing::AssertionFailure() ||
-        getRecordMQDescriptor() == ::testing::AssertionFailure()) {
-        return ::testing::AssertionFailure();
-    }
-    for (int i = 0; i <= filterIdsSize; i++) {
-        if (mDvr->attachFilter(mFilters[mUsedFilterIds[i]]) != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-
-    mDvrCallback->startRecordOutputThread(recordSetting, mRecordMQDescriptor);
-    status = mDvr->start();
-    if (status != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-
-    if (createDemuxWithFrontend(feIds[0], settings) != ::testing::AssertionSuccess()) {
-        return ::testing::AssertionFailure();
-    }
-
-    // Data Verify Module
-    mDvrCallback->testRecordOutput();
-
-    // Clean Up Module
-    for (int i = 0; i <= filterIdsSize; i++) {
-        if (mFilters[mUsedFilterIds[i]]->stop() != Result::SUCCESS) {
-            return ::testing::AssertionFailure();
-        }
-    }
-    if (mFrontend->stopTune() != Result::SUCCESS) {
-        return ::testing::AssertionFailure();
-    }
-    mUsedFilterIds.clear();
-    mFilterCallbacks.clear();
-    mFilters.clear();
-    return closeDemux();
-}
-
-/*
- * API STATUS TESTS
- */
-TEST_P(TunerHidlTest, CreateFrontend) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
-
-    description("Create Frontends");
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return;
-    }
-
-    for (size_t i = 0; i < feIds.size(); i++) {
-        ASSERT_TRUE(createFrontend(feIds[i]));
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(closeFrontend());
     }
 }
 
 TEST_P(TunerHidlTest, TuneFrontend) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
-
-    description("Tune Frontends and check callback onEvent");
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return;
-    }
-
-    for (size_t i = 0; i < feIds.size(); i++) {
-        ASSERT_TRUE(tuneFrontend(feIds[i]));
+    description("Tune one Frontend with specific setting and check Lock event");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
+    ALOGW("[vts] expected Frontend type is %d", frontendArray[0].type);
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        ALOGW("[vts] Frontend type is %d", mFrontendInfo.type);
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(stopTuneFrontend());
+        ASSERT_TRUE(tuneFrontend(frontendArray[0]));
+        ASSERT_TRUE(stopTuneFrontend());
+        ASSERT_TRUE(closeFrontend());
+        break;
     }
 }
 
-TEST_P(TunerHidlTest, StopTuneFrontend) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
+TEST_P(TunerHidlTest, AutoScanFrontend) {
+    description("Run an auto frontend scan with specific setting and check lock scanMessage");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
 
-    description("stopTune Frontends");
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return;
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(stopScanFrontend());
+        ASSERT_TRUE(scanFrontend(frontendScanArray[0], FrontendScanType::SCAN_AUTO));
+        ASSERT_TRUE(stopScanFrontend());
+        ASSERT_TRUE(closeFrontend());
+        break;
     }
+}
+/*=============================== End Frontend Tests ===============================*/
 
-    for (size_t i = 0; i < feIds.size(); i++) {
-        ASSERT_TRUE(stopTuneFrontend(feIds[i]));
+/*============================ Start Demux/Filter Tests ============================*/
+TEST_P(TunerHidlTest, OpenDemuxWithFrontendDataSource) {
+    description("Open Demux with a Frontend as its data source.");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
+
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(openDemux());
+        ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i]));
+        ASSERT_TRUE(closeDemux());
+        ASSERT_TRUE(closeFrontend());
+        break;
     }
 }
 
-TEST_P(TunerHidlTest, CloseFrontend) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
+TEST_P(TunerHidlTest, OpenFilterInDemux) {
+    description("Open a filter in Demux.");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
 
-    description("Close Frontends");
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return;
-    }
-
-    for (size_t i = 0; i < feIds.size(); i++) {
-        ASSERT_TRUE(closeFrontend(feIds[i]));
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(openDemux());
+        ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i]));
+        ASSERT_TRUE(openFilterInDemux(filterArray[0].type));
+        uint32_t filterId;
+        ASSERT_TRUE(getNewlyOpenedFilterId(filterId));
+        ASSERT_TRUE(closeFilter(filterId));
+        ASSERT_TRUE(closeDemux());
+        ASSERT_TRUE(closeFrontend());
+        break;
     }
 }
 
-TEST_P(TunerHidlTest, CreateDemuxWithFrontend) {
-    Result status;
-    hidl_vec<FrontendId> feIds;
+TEST_P(TunerHidlTest, StartFilterInDemux) {
+    description("Open and start a filter in Demux.");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
 
-    description("Create Demux with Frontend");
-    mService->getFrontendIds([&](Result result, const hidl_vec<FrontendId>& frontendIds) {
-        status = result;
-        feIds = frontendIds;
-    });
-
-    if (feIds.size() == 0) {
-        ALOGW("[   WARN   ] Frontend isn't available");
-        return;
-    }
-
-    FrontendDvbtSettings dvbt{
-        .frequency = 1000,
-    };
-    FrontendSettings settings;
-    settings.dvbt(dvbt);
-
-    for (size_t i = 0; i < feIds.size(); i++) {
-        ASSERT_TRUE(createDemuxWithFrontend(feIds[i], settings));
-        mFrontend->stopTune();
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(openDemux());
+        ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i]));
+        ASSERT_TRUE(openFilterInDemux(filterArray[0].type));
+        uint32_t filterId;
+        ASSERT_TRUE(getNewlyOpenedFilterId(filterId));
+        ASSERT_TRUE(configFilter(filterArray[0].setting, filterId));
+        ASSERT_TRUE(getFilterMQDescriptor(filterId));
+        ASSERT_TRUE(startFilter(filterId));
+        ASSERT_TRUE(stopFilter(filterId));
+        ASSERT_TRUE(closeFilter(filterId));
+        ASSERT_TRUE(closeDemux());
+        ASSERT_TRUE(closeFrontend());
+        break;
     }
 }
+/*============================ End Demux/Filter Tests ============================*/
 
-TEST_P(TunerHidlTest, CreateDemux) {
-    description("Create Demux");
-    ASSERT_TRUE(createDemux());
-}
-
-TEST_P(TunerHidlTest, CloseDemux) {
-    description("Close Demux");
-    ASSERT_TRUE(closeDemux());
-}
-
-TEST_P(TunerHidlTest, CreateDescrambler) {
+/*============================ Start Descrambler Tests ============================*/
+/*
+ * TODO: re-enable the tests after finalizing the test refactoring.
+ */
+/*TEST_P(TunerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
     ASSERT_TRUE(createDescrambler());
 }
@@ -1357,11 +1415,44 @@
 TEST_P(TunerHidlTest, CloseDescrambler) {
     description("Close Descrambler");
     ASSERT_TRUE(closeDescrambler());
+}*/
+/*============================== End Descrambler Tests ==============================*/
+
+/*============================== Start Data Flow Tests ==============================*/
+TEST_P(TunerHidlTest, BroadcastDataFlowWithAudioFilterTest) {
+    description("Open Demux with a Frontend as its data source.");
+    ASSERT_TRUE(getFrontendIds());
+    ASSERT_TRUE(mFeIds.size() > 0);
+
+    for (size_t i = 0; i < mFeIds.size(); i++) {
+        ASSERT_TRUE(getFrontendInfo(mFeIds[i]));
+        if (mFrontendInfo.type != frontendArray[0].type) {
+            continue;
+        }
+        ASSERT_TRUE(openFrontend(mFeIds[i]));
+        ASSERT_TRUE(setFrontendCallback());
+        ASSERT_TRUE(openDemux());
+        ASSERT_TRUE(setDemuxFrontendDataSource(mFeIds[i]));
+        ASSERT_TRUE(openFilterInDemux(filterArray[0].type));
+        uint32_t filterId;
+        ASSERT_TRUE(getNewlyOpenedFilterId(filterId));
+        ASSERT_TRUE(configFilter(filterArray[0].setting, filterId));
+        ASSERT_TRUE(getFilterMQDescriptor(filterId));
+        ASSERT_TRUE(startFilter(filterId));
+        // tune test
+        ASSERT_TRUE(tuneFrontend(frontendArray[0]));
+        // broadcast data flow test
+        ASSERT_TRUE(broadcastDataFlowTest(goldenOutputFiles));
+        ASSERT_TRUE(stopTuneFrontend());
+        ASSERT_TRUE(stopFilter(filterId));
+        ASSERT_TRUE(closeFilter(filterId));
+        ASSERT_TRUE(closeDemux());
+        ASSERT_TRUE(closeFrontend());
+        break;
+    }
 }
 
 /*
- * DATA FLOW TESTS
- *
  * TODO: re-enable the tests after finalizing the testing stream.
  */
 /*TEST_P(TunerHidlTest, PlaybackDataFlowWithSectionFilterTest) {
@@ -1407,36 +1498,6 @@
     ASSERT_TRUE(playbackDataFlowTest(filterConf, playbackConf, goldenOutputFiles));
 }
 
-TEST_P(TunerHidlTest, BroadcastDataFlowWithPesFilterTest) {
-    description("Feed ts data from frontend and test with PES filter");
-
-    // todo modulize the filter conf parser
-    vector<FilterConf> filterConf;
-    filterConf.resize(1);
-
-    DemuxFilterSettings filterSetting;
-    DemuxTsFilterSettings tsFilterSetting{
-            .tpid = 119,
-    };
-    DemuxFilterPesDataSettings pesFilterSetting;
-    tsFilterSetting.filterSettings.pesData(pesFilterSetting);
-    filterSetting.ts(tsFilterSetting);
-
-    DemuxFilterType type{
-            .mainType = DemuxFilterMainType::TS,
-    };
-    type.subType.tsFilterType(DemuxTsFilterType::PES);
-    FilterConf pesFilterConf{
-            .type = type,
-            .setting = filterSetting,
-    };
-    filterConf[0] = pesFilterConf;
-
-    vector<string> goldenOutputFiles;
-
-    ASSERT_TRUE(broadcastDataFlowTest(filterConf, goldenOutputFiles));
-}
-
 TEST_P(TunerHidlTest, RecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from frontend to recording and test with ts record filter");
 
@@ -1474,7 +1535,8 @@
 
     ASSERT_TRUE(recordDataFlowTest(filterConf, recordSetting, goldenOutputFiles));
 }*/
-
+/*============================== End Data Flow Tests ==============================*/
+/******************************** End Test Entry **********************************/
 }  // namespace
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
new file mode 100644
index 0000000..55ca857
--- /dev/null
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/IDemux.h>
+#include <android/hardware/tv/tuner/1.0/IDescrambler.h>
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
+#include <android/hardware/tv/tuner/1.0/IFilter.h>
+#include <android/hardware/tv/tuner/1.0/IFilterCallback.h>
+#include <android/hardware/tv/tuner/1.0/IFrontend.h>
+#include <android/hardware/tv/tuner/1.0/IFrontendCallback.h>
+#include <android/hardware/tv/tuner/1.0/ITuner.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <binder/MemoryDealer.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <fstream>
+#include <iostream>
+#include <map>
+
+using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterPesEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterStatus;
+using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using android::hardware::tv::tuner::V1_0::DemuxTpid;
+using android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
+using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
+using android::hardware::tv::tuner::V1_0::FrontendSettings;
+using android::hardware::tv::tuner::V1_0::FrontendType;
+
+namespace {
+
+#define frontend_transponders_count 1
+#define channels_count 1
+#define frontend_scan_count 1
+#define filter_count 2
+
+struct FilterConfig {
+    DemuxFilterType type;
+    DemuxFilterSettings setting;
+};
+
+struct FrontendConfig {
+    FrontendType type;
+    FrontendSettings settings;
+};
+
+struct ChannelConfig {
+    int32_t frontendId;
+    int32_t channelId;
+    std::string channelName;
+    DemuxTpid videoPid;
+    DemuxTpid audioPid;
+};
+
+static FrontendConfig frontendArray[frontend_transponders_count];
+static FrontendConfig frontendScanArray[channels_count];
+static ChannelConfig channelArray[frontend_scan_count];
+static FilterConfig filterArray[filter_count];
+static vector<string> goldenOutputFiles;
+
+/** Configuration array for the frontend tune test */
+inline void initFrontendConfig() {
+    FrontendDvbtSettings dvbtSettings{
+            .frequency = 578000,
+            .transmissionMode = FrontendDvbtTransmissionMode::AUTO,
+            .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
+            .constellation = FrontendDvbtConstellation::AUTO,
+            .hierarchy = FrontendDvbtHierarchy::AUTO,
+            .hpCoderate = FrontendDvbtCoderate::AUTO,
+            .lpCoderate = FrontendDvbtCoderate::AUTO,
+            .guardInterval = FrontendDvbtGuardInterval::AUTO,
+            .isHighPriority = true,
+            .standard = FrontendDvbtStandard::T,
+    };
+    frontendArray[0].type = FrontendType::DVBT, frontendArray[0].settings.dvbt(dvbtSettings);
+};
+
+/** Configuration array for the frontend scan test */
+inline void initFrontendScanConfig() {
+    frontendScanArray[0].type = FrontendType::DVBT, frontendScanArray[0].settings.dvbt({
+                                                            .frequency = 577000,
+                                                    });
+};
+
+/** Configuration array for the filter test */
+inline void initFilterConfig() {
+    // TS Video filter setting
+    filterArray[0].type.mainType = DemuxFilterMainType::TS;
+    filterArray[0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+    filterArray[0].setting.ts().tpid = 49;
+    filterArray[0].setting.ts().filterSettings.av({.isPassthrough = false});
+    // TS PES filter setting
+    filterArray[1].type.mainType = DemuxFilterMainType::TS;
+    filterArray[1].type.subType.tsFilterType(DemuxTsFilterType::PES);
+    filterArray[1].setting.ts().tpid = 256;
+    filterArray[1].setting.ts().filterSettings.pesData({
+            .isRaw = true,
+            .streamId = 0xbd,
+    });
+};
+
+}  // namespace
\ No newline at end of file
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index a040c89..f596195 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -831,6 +831,16 @@
         global_handle_, mode, completion_window);
 }
 
+wifi_error WifiLegacyHal::setDscpToAccessCategoryMapping(
+    uint32_t start, uint32_t end, uint32_t access_category) {
+    return global_func_table_.wifi_map_dscp_access_category(
+        global_handle_, start, end, access_category);
+}
+
+wifi_error WifiLegacyHal::resetDscpToAccessCategoryMapping() {
+    return global_func_table_.wifi_reset_dscp_mapping(global_handle_);
+}
+
 std::pair<wifi_error, uint32_t> WifiLegacyHal::getLoggerSupportedFeatureSet(
     const std::string& iface_name) {
     uint32_t supported_feature_flags;
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index 72cf197..c21563e 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -261,6 +261,9 @@
                               wifi_latency_mode mode);
     wifi_error setThermalMitigationMode(wifi_thermal_mode mode,
                                         uint32_t completion_window);
+    wifi_error setDscpToAccessCategoryMapping(uint32_t start, uint32_t end,
+                                              uint32_t access_category);
+    wifi_error resetDscpToAccessCategoryMapping();
     // Logger/debug functions.
     std::pair<wifi_error, uint32_t> getLoggerSupportedFeatureSet(
         const std::string& iface_name);
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
index 6945b4c..153a685 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.cpp
@@ -141,6 +141,8 @@
     populateStubFor(&hal_fn->wifi_set_thermal_mitigation_mode);
     populateStubFor(&hal_fn->wifi_virtual_interface_create);
     populateStubFor(&hal_fn->wifi_virtual_interface_delete);
+    populateStubFor(&hal_fn->wifi_map_dscp_access_category);
+    populateStubFor(&hal_fn->wifi_reset_dscp_mapping);
     return true;
 }
 }  // namespace legacy_hal