Merge "AudioFlinger: track metadata fix up" into sc-dev
diff --git a/media/libmediahelper/tests/typeconverter_tests.cpp b/media/libmediahelper/tests/typeconverter_tests.cpp
index d7bfb89..181d636 100644
--- a/media/libmediahelper/tests/typeconverter_tests.cpp
+++ b/media/libmediahelper/tests/typeconverter_tests.cpp
@@ -182,8 +182,9 @@
         audio_format_t format;
         EXPECT_TRUE(FormatConverter::fromString(stringVal, format))
                 << "Conversion of \"" << stringVal << "\" failed";
-        EXPECT_TRUE(audio_is_valid_format(format))
-                << "Converted format \"" << stringVal << "\" is invalid";
+        EXPECT_EQ(enumVal != xsd::AudioFormat::AUDIO_FORMAT_DEFAULT,
+                audio_is_valid_format(format))
+                << "Validity of \"" << stringVal << "\" is not as expected";
         EXPECT_EQ(stringVal, toString(format));
     }
 }
diff --git a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
index e3c0b05..994695f 100644
--- a/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
+++ b/media/libmediatranscoding/transcoder/VideoTrackTranscoder.cpp
@@ -238,7 +238,12 @@
     int32_t operatingRate = getDefaultOperatingRate(encoderFormat);
 
     if (operatingRate != -1) {
-        SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_OPERATING_RATE, encoderFormat, operatingRate);
+        float tmpf;
+        int32_t tmpi;
+        if (!AMediaFormat_getFloat(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, &tmpf) &&
+            !AMediaFormat_getInt32(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, &tmpi)) {
+            AMediaFormat_setInt32(encoderFormat, AMEDIAFORMAT_KEY_OPERATING_RATE, operatingRate);
+        }
     }
 
     SetDefaultFormatValueInt32(AMEDIAFORMAT_KEY_PRIORITY, encoderFormat, kDefaultCodecPriority);
@@ -260,8 +265,8 @@
         return AMEDIA_ERROR_INVALID_PARAMETER;
     }
 
-    // TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
-    #define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
+// TODO: replace __ANDROID_API_FUTURE__with 31 when it's official (b/178144708)
+#define __TRANSCODING_MIN_API__ __ANDROID_API_FUTURE__
 
     AMediaCodec* encoder;
     if (__builtin_available(android __TRANSCODING_MIN_API__, *)) {
diff --git a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
index e0b2050..42933e6 100644
--- a/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
+++ b/media/libmediatranscoding/transcoder/benchmark/MediaTranscoderBenchmark.cpp
@@ -33,6 +33,7 @@
 #include <binder/ProcessState.h>
 #include <fcntl.h>
 #include <media/MediaTranscoder.h>
+#include <media/NdkCommon.h>
 
 #include <iostream>
 
@@ -87,6 +88,7 @@
 
     AMediaFormat* videoFormat = AMediaFormat_new();
     AMediaFormat_setInt32(videoFormat, AMEDIAFORMAT_KEY_BIT_RATE, kVideoBitRate);
+    AMediaFormat_setString(videoFormat, AMEDIAFORMAT_KEY_MIME, AMEDIA_MIMETYPE_VIDEO_AVC);
     return videoFormat;
 }
 
@@ -222,7 +224,7 @@
 }
 
 static void SetMaxOperatingRate(AMediaFormat* format) {
-    AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
+    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_OPERATING_RATE, INT32_MAX);
     AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_PRIORITY, 1);
 }
 
diff --git a/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
index 6d781cd..e40a507 100644
--- a/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
+++ b/media/libmediatranscoding/transcoder/tests/AndroidTestTemplate.xml
@@ -24,6 +24,7 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="{MODULE}" />
+        <option name="native-test-timeout" value="10m" />
     </test>
 </configuration>
 
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index c0a8f9d..e721a78 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1064,8 +1064,7 @@
     *output = AUDIO_IO_HANDLE_NONE;
     if (!msdDevices.isEmpty()) {
         *output = getOutputForDevices(msdDevices, session, *stream, config, flags);
-        sp<DeviceDescriptor> device = outputDevices.isEmpty() ? nullptr : outputDevices.itemAt(0);
-        if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatch(device) == NO_ERROR) {
+        if (*output != AUDIO_IO_HANDLE_NONE && setMsdPatches(&outputDevices) == NO_ERROR) {
             ALOGV("%s() Using MSD devices %s instead of devices %s",
                   __func__, msdDevices.toString().c_str(), outputDevices.toString().c_str());
         } else {
@@ -1081,6 +1080,12 @@
     }
 
     *selectedDeviceId = getFirstDeviceId(outputDevices);
+    for (auto &outputDevice : outputDevices) {
+        if (outputDevice->getId() == getConfig().getDefaultOutputDevice()->getId()) {
+            *selectedDeviceId = outputDevice->getId();
+            break;
+        }
+    }
 
     if (outputDevices.onlyContainsDevicesWithType(AUDIO_DEVICE_OUT_TELEPHONY_TX)) {
         *outputType = API_OUTPUT_TELEPHONY_TX;
@@ -1223,24 +1228,9 @@
     sp<SwAudioOutputDescriptor> outputDesc =
             new SwAudioOutputDescriptor(profile, mpClientInterface);
 
-    String8 address = getFirstDeviceAddress(devices);
-
-    // MSD patch may be using the only output stream that can service this request. Release
-    // MSD patch to prioritize this request over any active output on MSD.
-    AudioPatchCollection msdPatches = getMsdPatches();
-    for (size_t i = 0; i < msdPatches.size(); i++) {
-        const auto& patch = msdPatches[i];
-        for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) {
-            const struct audio_port_config *sink = &patch->mPatch.sinks[j];
-            if (sink->type == AUDIO_PORT_TYPE_DEVICE &&
-                    devices.containsDeviceWithType(sink->ext.device.type) &&
-                    (address.isEmpty() || strncmp(sink->ext.device.address, address.string(),
-                            AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
-                releaseAudioPatch(patch->getHandle(), mUidCached);
-                break;
-            }
-        }
-    }
+    // An MSD patch may be using the only output stream that can service this request. Release
+    // all MSD patches to prioritize this request over any active output on MSD.
+    releaseMsdPatches(devices);
 
     status_t status = outputDesc->open(config, devices, stream, flags, output);
 
@@ -1414,7 +1404,8 @@
     }
     AudioProfileVector deviceProfiles;
     for (const auto &outProfile : outputProfiles) {
-        if (hwAvSync == ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0)) {
+        if (hwAvSync == ((outProfile->getFlags() & AUDIO_OUTPUT_FLAG_HW_AV_SYNC) != 0) &&
+                outProfile->supportsDevice(outputDevice)) {
             appendAudioProfiles(deviceProfiles, outProfile->getAudioProfiles());
         }
     }
@@ -1482,40 +1473,85 @@
     return patchBuilder;
 }
 
-status_t AudioPolicyManager::setMsdPatch(const sp<DeviceDescriptor> &outputDevice) {
-    sp<DeviceDescriptor> device = outputDevice;
-    if (device == nullptr) {
+status_t AudioPolicyManager::setMsdPatches(const DeviceVector *outputDevices) {
+    DeviceVector devices;
+    if (outputDevices != nullptr && outputDevices->size() > 0) {
+        devices.add(*outputDevices);
+    } else {
         // Use media strategy for unspecified output device. This should only
         // occur on checkForDeviceAndOutputChanges(). Device connection events may
         // therefore invalidate explicit routing requests.
-        DeviceVector devices = mEngine->getOutputDevicesForAttributes(
+        devices = mEngine->getOutputDevicesForAttributes(
                     attributes_initializer(AUDIO_USAGE_MEDIA), nullptr, false /*fromCache*/);
-        LOG_ALWAYS_FATAL_IF(devices.isEmpty(), "no outpudevice to set Msd Patch");
-        device = devices.itemAt(0);
+        LOG_ALWAYS_FATAL_IF(devices.isEmpty(), "no output device to set MSD patch");
     }
-    ALOGV("%s() for device %s", __func__, device->toString().c_str());
-    PatchBuilder patchBuilder = buildMsdPatch(device);
-    const struct audio_patch* patch = patchBuilder.patch();
-    const AudioPatchCollection msdPatches = getMsdPatches();
-    if (!msdPatches.isEmpty()) {
-        LOG_ALWAYS_FATAL_IF(msdPatches.size() > 1,
-                "The current MSD prototype only supports one output patch");
-        sp<AudioPatch> currentPatch = msdPatches.valueAt(0);
-        if (audio_patches_are_equal(&currentPatch->mPatch, patch)) {
-            return NO_ERROR;
+    std::vector<PatchBuilder> patchesToCreate;
+    for (auto i = 0u; i < devices.size(); ++i) {
+        ALOGV("%s() for device %s", __func__, devices[i]->toString().c_str());
+        patchesToCreate.push_back(buildMsdPatch(devices[i]));
+    }
+    // Retain only the MSD patches associated with outputDevices request.
+    // Tear down the others, and create new ones as needed.
+    AudioPatchCollection patchesToRemove = getMsdPatches();
+    for (auto it = patchesToCreate.begin(); it != patchesToCreate.end(); ) {
+        auto retainedPatch = false;
+        for (auto i = 0u; i < patchesToRemove.size(); ++i) {
+            if (audio_patches_are_equal(it->patch(), &patchesToRemove[i]->mPatch)) {
+                patchesToRemove.removeItemsAt(i);
+                retainedPatch = true;
+                break;
+            }
         }
+        if (retainedPatch) {
+            it = patchesToCreate.erase(it);
+            continue;
+        }
+        ++it;
+    }
+    if (patchesToCreate.size() == 0 && patchesToRemove.size() == 0) {
+        return NO_ERROR;
+    }
+    for (auto i = 0u; i < patchesToRemove.size(); ++i) {
+        auto &currentPatch = patchesToRemove.valueAt(i);
         releaseAudioPatch(currentPatch->getHandle(), mUidCached);
     }
-    status_t status = installPatch(__func__, -1 /*index*/, nullptr /*patchHandle*/,
-            patch, 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/);
-    ALOGE_IF(status != NO_ERROR, "%s() error %d creating MSD audio patch", __func__, status);
-    ALOGI_IF(status == NO_ERROR, "%s() Patch created from MSD_IN to "
-           "device:%s (format:%#x channels:%#x samplerate:%d)", __func__,
-             device->toString().c_str(), patch->sources[0].format,
-             patch->sources[0].channel_mask, patch->sources[0].sample_rate);
+    status_t status = NO_ERROR;
+    for (const auto &p : patchesToCreate) {
+        auto currStatus = installPatch(__func__, -1 /*index*/, nullptr /*patchHandle*/,
+                p.patch(), 0 /*delayMs*/, mUidCached, nullptr /*patchDescPtr*/);
+        char message[256];
+        snprintf(message, sizeof(message), "%s() %s: creating MSD patch from device:IN_BUS to "
+            "device:%#x (format:%#x channels:%#x samplerate:%d)", __func__,
+                currStatus == NO_ERROR ? "Success" : "Error",
+                p.patch()->sinks[0].ext.device.type, p.patch()->sources[0].format,
+                p.patch()->sources[0].channel_mask, p.patch()->sources[0].sample_rate);
+        if (currStatus == NO_ERROR) {
+            ALOGD("%s", message);
+        } else {
+            ALOGE("%s", message);
+            if (status == NO_ERROR) {
+                status = currStatus;
+            }
+        }
+    }
     return status;
 }
 
+void AudioPolicyManager::releaseMsdPatches(const DeviceVector& devices) {
+    AudioPatchCollection msdPatches = getMsdPatches();
+    for (size_t i = 0; i < msdPatches.size(); i++) {
+        const auto& patch = msdPatches[i];
+        for (size_t j = 0; j < patch->mPatch.num_sinks; ++j) {
+            const struct audio_port_config *sink = &patch->mPatch.sinks[j];
+            if (sink->type == AUDIO_PORT_TYPE_DEVICE && devices.getDevice(sink->ext.device.type,
+                    String8(sink->ext.device.address), AUDIO_FORMAT_DEFAULT) != nullptr) {
+                releaseAudioPatch(patch->getHandle(), mUidCached);
+                break;
+            }
+        }
+    }
+}
+
 audio_io_handle_t AudioPolicyManager::selectOutput(const SortedVector<audio_io_handle_t>& outputs,
                                                    audio_output_flags_t flags,
                                                    audio_format_t format,
@@ -5319,8 +5355,13 @@
             }
         }
         if (!directOutputOpen) {
-            ALOGV("no direct outputs open, reset MSD patch");
-            setMsdPatch();
+            ALOGV("no direct outputs open, reset MSD patches");
+            // TODO: The MSD patches to be established here may differ to current MSD patches due to
+            // how output devices for patching are resolved. Avoid by caching and reusing the
+            // arguments to mEngine->getOutputDevicesForAttributes() when resolving which output
+            // devices to patch to. This may be complicated by the fact that devices may become
+            // unavailable.
+            setMsdPatches();
         }
     }
 }
@@ -5387,7 +5428,13 @@
     if (onOutputsChecked != nullptr && onOutputsChecked()) checkA2dpSuspend();
     updateDevicesAndOutputs();
     if (mHwModules.getModuleFromName(AUDIO_HARDWARE_MODULE_ID_MSD) != 0) {
-        setMsdPatch();
+        // TODO: The MSD patches to be established here may differ to current MSD patches due to how
+        // output devices for patching are resolved. Nevertheless, AudioTracks affected by device
+        // configuration changes will ultimately be rerouted correctly. We can still avoid
+        // unnecessary rerouting by caching and reusing the arguments to
+        // mEngine->getOutputDevicesForAttributes() when resolving which output devices to patch to.
+        // This may be complicated by the fact that devices may become unavailable.
+        setMsdPatches();
     }
     // an event that changed routing likely occurred, inform upper layers
     mpClientInterface->onRoutingUpdated();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 3c55b63..d3ceb1b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -848,13 +848,6 @@
         // end point.
         audio_port_handle_t mCallRxSourceClientPort = AUDIO_PORT_HANDLE_NONE;
 
-private:
-        void onNewAudioModulesAvailableInt(DeviceVector *newDevices);
-
-        // Add or remove AC3 DTS encodings based on user preferences.
-        void modifySurroundFormats(const sp<DeviceDescriptor>& devDesc, FormatVector *formatsPtr);
-        void modifySurroundChannelMasks(ChannelMaskSet *channelMasksPtr);
-
         // Support for Multi-Stream Decoder (MSD) module
         sp<DeviceDescriptor> getMsdAudioInDevice() const;
         DeviceVector getMsdAudioOutDevices() const;
@@ -864,7 +857,14 @@
                                            audio_port_config *sourceConfig,
                                            audio_port_config *sinkConfig) const;
         PatchBuilder buildMsdPatch(const sp<DeviceDescriptor> &outputDevice) const;
-        status_t setMsdPatch(const sp<DeviceDescriptor> &outputDevice = nullptr);
+        status_t setMsdPatches(const DeviceVector *outputDevices = nullptr);
+        void releaseMsdPatches(const DeviceVector& devices);
+private:
+        void onNewAudioModulesAvailableInt(DeviceVector *newDevices);
+
+        // Add or remove AC3 DTS encodings based on user preferences.
+        void modifySurroundFormats(const sp<DeviceDescriptor>& devDesc, FormatVector *formatsPtr);
+        void modifySurroundChannelMasks(ChannelMaskSet *channelMasksPtr);
 
         // If any, resolve any "dynamic" fields of an Audio Profiles collection
         void updateAudioProfiles(const sp<DeviceDescriptor>& devDesc, audio_io_handle_t ioHandle,
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index be860e5..ea95364 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -29,6 +29,8 @@
     using AudioPolicyManager::getOutputs;
     using AudioPolicyManager::getAvailableOutputDevices;
     using AudioPolicyManager::getAvailableInputDevices;
+    using AudioPolicyManager::releaseMsdPatches;
+    using AudioPolicyManager::setMsdPatches;
     using AudioPolicyManager::setSurroundFormatEnabled;
     uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
 };
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 3032589..684358f 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -349,7 +349,17 @@
 
 // TODO: Add patch creation tests that involve already existing patch
 
-class AudioPolicyManagerTestMsd : public AudioPolicyManagerTest {
+enum
+{
+    MSD_AUDIO_PATCH_COUNT_NUM_AUDIO_PATCHES_INDEX = 0,
+    MSD_AUDIO_PATCH_COUNT_NAME_INDEX = 1
+};
+using MsdAudioPatchCountSpecification = std::tuple<size_t, std::string>;
+
+class AudioPolicyManagerTestMsd : public AudioPolicyManagerTest,
+        public ::testing::WithParamInterface<MsdAudioPatchCountSpecification> {
+  public:
+    AudioPolicyManagerTestMsd();
   protected:
     void SetUpManagerConfig() override;
     void TearDown() override;
@@ -357,8 +367,26 @@
     sp<DeviceDescriptor> mMsdOutputDevice;
     sp<DeviceDescriptor> mMsdInputDevice;
     sp<DeviceDescriptor> mDefaultOutputDevice;
+
+    const size_t mExpectedAudioPatchCount;
+    sp<DeviceDescriptor> mSpdifDevice;
 };
 
+AudioPolicyManagerTestMsd::AudioPolicyManagerTestMsd()
+    : mExpectedAudioPatchCount(std::get<MSD_AUDIO_PATCH_COUNT_NUM_AUDIO_PATCHES_INDEX>(
+            GetParam())) {}
+
+INSTANTIATE_TEST_CASE_P(
+        MsdAudioPatchCount,
+        AudioPolicyManagerTestMsd,
+        ::testing::Values(
+                MsdAudioPatchCountSpecification(1u, "single"),
+                MsdAudioPatchCountSpecification(2u, "dual")
+        ),
+        [](const ::testing::TestParamInfo<MsdAudioPatchCountSpecification> &info) {
+                return std::get<MSD_AUDIO_PATCH_COUNT_NAME_INDEX>(info.param); }
+);
+
 void AudioPolicyManagerTestMsd::SetUpManagerConfig() {
     // TODO: Consider using Serializer to load part of the config from a string.
     AudioPolicyManagerTest::SetUpManagerConfig();
@@ -378,6 +406,19 @@
     config.addDevice(mMsdOutputDevice);
     config.addDevice(mMsdInputDevice);
 
+    if (mExpectedAudioPatchCount == 2) {
+        // Add SPDIF device with PCM output profile as a second device for dual MSD audio patching.
+        mSpdifDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_SPDIF);
+        mSpdifDevice->addAudioProfile(pcmOutputProfile);
+        config.addDevice(mSpdifDevice);
+
+        sp<OutputProfile> spdifOutputProfile = new OutputProfile("spdif output");
+        spdifOutputProfile->addAudioProfile(pcmOutputProfile);
+        spdifOutputProfile->addSupportedDevice(mSpdifDevice);
+        config.getHwModules().getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)->
+                addOutputProfile(spdifOutputProfile);
+    }
+
     sp<HwModule> msdModule = new HwModule(AUDIO_HARDWARE_MODULE_ID_MSD, 2 /*halVersionMajor*/);
     HwModuleCollection modules = config.getHwModules();
     modules.add(msdModule);
@@ -413,64 +454,88 @@
             addOutputProfile(primaryEncodedOutputProfile);
 
     mDefaultOutputDevice = config.getDefaultOutputDevice();
+    if (mExpectedAudioPatchCount == 2) {
+        mSpdifDevice->addAudioProfile(dtsOutputProfile);
+        primaryEncodedOutputProfile->addSupportedDevice(mSpdifDevice);
+    }
 }
 
 void AudioPolicyManagerTestMsd::TearDown() {
     mMsdOutputDevice.clear();
     mMsdInputDevice.clear();
     mDefaultOutputDevice.clear();
+    mSpdifDevice.clear();
     AudioPolicyManagerTest::TearDown();
 }
 
-TEST_F(AudioPolicyManagerTestMsd, InitSuccess) {
+TEST_P(AudioPolicyManagerTestMsd, InitSuccess) {
     ASSERT_TRUE(mMsdOutputDevice);
     ASSERT_TRUE(mMsdInputDevice);
     ASSERT_TRUE(mDefaultOutputDevice);
 }
 
-TEST_F(AudioPolicyManagerTestMsd, Dump) {
+TEST_P(AudioPolicyManagerTestMsd, Dump) {
     dumpToLog();
 }
 
-TEST_F(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) {
+TEST_P(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) {
     const PatchCountCheck patchCount = snapshotPatchCount();
     mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND,
             AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
-    ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
 }
 
-TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
+TEST_P(AudioPolicyManagerTestMsd, PatchCreationSetReleaseMsdPatches) {
+    const PatchCountCheck patchCount = snapshotPatchCount();
+    DeviceVector devices = mManager->getAvailableOutputDevices();
+    // Remove MSD output device to avoid patching to itself
+    devices.remove(mMsdOutputDevice);
+    ASSERT_EQ(mExpectedAudioPatchCount, devices.size());
+    mManager->setMsdPatches(&devices);
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
+    // Dual patch: exercise creating one new audio patch and reusing another existing audio patch.
+    DeviceVector singleDevice(devices[0]);
+    mManager->releaseMsdPatches(singleDevice);
+    ASSERT_EQ(mExpectedAudioPatchCount - 1, patchCount.deltaFromSnapshot());
+    mManager->setMsdPatches(&devices);
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
+    mManager->releaseMsdPatches(devices);
+    ASSERT_EQ(0, patchCount.deltaFromSnapshot());
+}
+
+TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
     const PatchCountCheck patchCount = snapshotPatchCount();
     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     getOutputForAttr(&selectedDeviceId,
             AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
     ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
-    ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
 }
 
-TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) {
+TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) {
     const PatchCountCheck patchCount = snapshotPatchCount();
     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     getOutputForAttr(&selectedDeviceId,
             AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
     ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
-    ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
 }
 
-TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
+TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
     const PatchCountCheck patchCount = snapshotPatchCount();
     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     getOutputForAttr(&selectedDeviceId,
             AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
     ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
-    ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
+    selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     getOutputForAttr(&selectedDeviceId,
             AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
     ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
-    ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+    ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
 }
 
-TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
+TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
     const PatchCountCheck patchCount = snapshotPatchCount();
     audio_port_handle_t selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
     getOutputForAttr(&selectedDeviceId,
@@ -479,7 +544,7 @@
     ASSERT_EQ(0, patchCount.deltaFromSnapshot());
 }
 
-TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) {
+TEST_P(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) {
     // Switch between formats that are supported and not supported by MSD.
     {
         const PatchCountCheck patchCount = snapshotPatchCount();
@@ -489,9 +554,9 @@
                 AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
                 nullptr /*output*/, &portId);
         ASSERT_EQ(selectedDeviceId, mDefaultOutputDevice->getId());
-        ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+        ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
         mManager->releaseOutput(portId);
-        ASSERT_EQ(1, patchCount.deltaFromSnapshot());
+        ASSERT_EQ(mExpectedAudioPatchCount, patchCount.deltaFromSnapshot());
     }
     {
         const PatchCountCheck patchCount = snapshotPatchCount();
@@ -501,7 +566,7 @@
                 AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
                 nullptr /*output*/, &portId);
         ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
-        ASSERT_EQ(-1, patchCount.deltaFromSnapshot());
+        ASSERT_EQ(-static_cast<int>(mExpectedAudioPatchCount), patchCount.deltaFromSnapshot());
         mManager->releaseOutput(portId);
         ASSERT_EQ(0, patchCount.deltaFromSnapshot());
     }
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index b4c0da3..adf8562 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -1686,6 +1686,11 @@
             client->setRotateAndCropOverride(mOverrideRotateAndCropMode);
         }
 
+        // Set camera muting behavior
+        if (client->supportsCameraMute()) {
+            client->setCameraMute(mOverrideCameraMuteMode);
+        }
+
         if (shimUpdateOnly) {
             // If only updating legacy shim parameters, immediately disconnect client
             mServiceLock.unlock();
@@ -3887,6 +3892,8 @@
         return handleSetImageDumpMask(args);
     } else if (args.size() >= 1 && args[0] == String16("get-image-dump-mask")) {
         return handleGetImageDumpMask(out);
+    } else if (args.size() >= 2 && args[0] == String16("set-camera-mute")) {
+        return handleSetCameraMute(args);
     } else if (args.size() == 1 && args[0] == String16("help")) {
         printHelp(out);
         return NO_ERROR;
@@ -4009,6 +4016,29 @@
     return dprintf(out, "Image dump mask: %d\n", mImageDumpMask);
 }
 
+status_t CameraService::handleSetCameraMute(const Vector<String16>& args) {
+    int muteValue = strtol(String8(args[1]), nullptr, 10);
+    if (errno != 0) return BAD_VALUE;
+
+    if (muteValue < 0 || muteValue > 1) return BAD_VALUE;
+    Mutex::Autolock lock(mServiceLock);
+
+    mOverrideCameraMuteMode = (muteValue == 1);
+
+    const auto clients = mActiveClientManager.getAll();
+    for (auto& current : clients) {
+        if (current != nullptr) {
+            const auto basicClient = current->getValue();
+            if (basicClient.get() != nullptr) {
+                if (basicClient->supportsCameraMute()) {
+                    basicClient->setCameraMute(mOverrideCameraMuteMode);
+                }
+            }
+        }
+    }
+
+    return OK;
+}
 
 status_t CameraService::printHelp(int out) {
     return dprintf(out, "Camera service commands:\n"
@@ -4021,6 +4051,7 @@
         "  set-image-dump-mask <MASK> specifies the formats to be saved to disk\n"
         "      Valid values 0=OFF, 1=ON for JPEG\n"
         "  get-image-dump-mask returns the current image-dump-mask value\n"
+        "  set-camera-mute <0/1> enable or disable camera muting\n"
         "  help print this message\n");
 }
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 43b03e6..bc6ad35 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -278,6 +278,12 @@
         // Override rotate-and-crop AUTO behavior
         virtual status_t setRotateAndCropOverride(uint8_t rotateAndCrop) = 0;
 
+        // Whether the client supports camera muting (black only output)
+        virtual bool supportsCameraMute() = 0;
+
+        // Set/reset camera mute
+        virtual status_t setCameraMute(bool enabled) = 0;
+
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
@@ -1044,6 +1050,9 @@
     // Get the mask for image dump to disk
     status_t handleGetImageDumpMask(int out);
 
+    // Set the camera mute state
+    status_t handleSetCameraMute(const Vector<String16>& args);
+
     // Prints the shell command help
     status_t printHelp(int out);
 
@@ -1088,6 +1097,9 @@
 
     // Current image dump mask
     uint8_t mImageDumpMask = 0;
+
+    // Current camera mute mode
+    bool mOverrideCameraMuteMode = false;
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 2494302..1c9e9cf 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -2296,6 +2296,14 @@
         static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
 }
 
+bool Camera2Client::supportsCameraMute() {
+    return mDevice->supportsCameraMute();
+}
+
+status_t Camera2Client::setCameraMute(bool enabled) {
+    return mDevice->setCameraMute(enabled);
+}
+
 status_t Camera2Client::waitUntilCurrentRequestIdLocked() {
     int32_t activeRequestId = mStreamingProcessor->getActiveRequestId();
     if (activeRequestId != 0) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index f8da0b6..4d667e3 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -87,6 +87,9 @@
     virtual int32_t         getGlobalAudioRestriction();
     virtual status_t        setRotateAndCropOverride(uint8_t rotateAndCrop);
 
+    virtual bool            supportsCameraMute();
+    virtual status_t        setCameraMute(bool enabled);
+
     /**
      * Interface used by CameraService
      */
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 6e1aba9..d47014e 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -1532,6 +1532,14 @@
         static_cast<camera_metadata_enum_android_scaler_rotate_and_crop_t>(rotateAndCrop));
 }
 
+bool CameraDeviceClient::supportsCameraMute() {
+    return mDevice->supportsCameraMute();
+}
+
+status_t CameraDeviceClient::setCameraMute(bool enabled) {
+    return mDevice->setCameraMute(enabled);
+}
+
 binder::Status CameraDeviceClient::switchToOffline(
         const sp<hardware::camera2::ICameraDeviceCallbacks>& cameraCb,
         const std::vector<int>& offlineOutputIds,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 57688a0..5588285 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -189,6 +189,9 @@
 
     virtual status_t      setRotateAndCropOverride(uint8_t rotateAndCrop) override;
 
+    virtual bool          supportsCameraMute();
+    virtual status_t      setCameraMute(bool enabled);
+
     virtual status_t      dump(int fd, const Vector<String16>& args);
 
     virtual status_t      dumpClient(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
index 62b5479..6765c3b 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.cpp
@@ -72,6 +72,16 @@
     return OK;
 }
 
+bool CameraOfflineSessionClient::supportsCameraMute() {
+    // Offline mode doesn't support muting
+    return false;
+}
+
+status_t CameraOfflineSessionClient::setCameraMute(bool) {
+    return INVALID_OPERATION;
+}
+
+
 status_t CameraOfflineSessionClient::dump(int fd, const Vector<String16>& args) {
     return BasicClient::dump(fd, args);
 }
diff --git a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
index 839c435..5c5fcda 100644
--- a/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
+++ b/services/camera/libcameraservice/api2/CameraOfflineSessionClient.h
@@ -76,6 +76,9 @@
 
     status_t setRotateAndCropOverride(uint8_t rotateAndCrop) override;
 
+    bool supportsCameraMute() override;
+    status_t setCameraMute(bool enabled) override;
+
     // permissions management
     status_t startCameraOps() override;
     status_t finishCameraOps() override;
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 5e46f08..1be46d6 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -399,6 +399,21 @@
             camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue) = 0;
 
     /**
+     * Whether camera muting (producing black-only output) is supported.
+     *
+     * Calling setCameraMute(true) when this returns false will return an
+     * INVALID_OPERATION error.
+     */
+    virtual bool supportsCameraMute() = 0;
+
+    /**
+     * Mute the camera.
+     *
+     * When muted, black image data is output on all output streams.
+     */
+    virtual status_t setCameraMute(bool enabled) = 0;
+
+    /**
      * Get the status tracker of the camera device
      */
     virtual wp<camera3::StatusTracker> getStatusTracker() = 0;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 385bfd6..7606d7d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -357,6 +357,15 @@
         mRotateAndCropMappers.emplace(mId.c_str(), &mDeviceInfo);
     }
 
+    camera_metadata_entry_t availableTestPatternModes = mDeviceInfo.find(
+            ANDROID_SENSOR_AVAILABLE_TEST_PATTERN_MODES);
+    for (size_t i = 0; i < availableTestPatternModes.count; i++) {
+        if (availableTestPatternModes.data.i32[i] == ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
+            mSupportCameraMute = true;
+            break;
+        }
+    }
+
     return OK;
 }
 
@@ -2388,6 +2397,26 @@
         newRequest->mZoomRatioIs1x = false;
     }
 
+    if (mSupportCameraMute) {
+        auto testPatternModeEntry =
+                newRequest->mSettingsList.begin()->metadata.find(ANDROID_SENSOR_TEST_PATTERN_MODE);
+        newRequest->mOriginalTestPatternMode = testPatternModeEntry.count > 0 ?
+                testPatternModeEntry.data.i32[0] :
+                ANDROID_SENSOR_TEST_PATTERN_MODE_OFF;
+
+        auto testPatternDataEntry =
+                newRequest->mSettingsList.begin()->metadata.find(ANDROID_SENSOR_TEST_PATTERN_DATA);
+        if (testPatternDataEntry.count > 0) {
+            memcpy(newRequest->mOriginalTestPatternData, testPatternModeEntry.data.i32,
+                    sizeof(newRequest->mOriginalTestPatternData));
+        } else {
+            newRequest->mOriginalTestPatternData[0] = 0;
+            newRequest->mOriginalTestPatternData[1] = 0;
+            newRequest->mOriginalTestPatternData[2] = 0;
+            newRequest->mOriginalTestPatternData[3] = 0;
+        }
+    }
+
     return newRequest;
 }
 
@@ -3860,6 +3889,8 @@
         mCurrentAfTriggerId(0),
         mCurrentPreCaptureTriggerId(0),
         mRotateAndCropOverride(ANDROID_SCALER_ROTATE_AND_CROP_NONE),
+        mCameraMute(false),
+        mCameraMuteChanged(false),
         mRepeatingLastFrameNumber(
             hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
         mPrepareVideoStream(false),
@@ -4484,10 +4515,13 @@
         mPrevTriggers = triggerCount;
 
         bool rotateAndCropChanged = overrideAutoRotateAndCrop(captureRequest);
+        bool testPatternChanged = overrideTestPattern(captureRequest);
 
-        // If the request is the same as last, or we had triggers last time
+        // If the request is the same as last, or we had triggers now or last time or
+        // changing overrides this time
         bool newRequest =
-                (mPrevRequest != captureRequest || triggersMixedIn || rotateAndCropChanged) &&
+                (mPrevRequest != captureRequest || triggersMixedIn ||
+                        rotateAndCropChanged || testPatternChanged) &&
                 // Request settings are all the same within one batch, so only treat the first
                 // request in a batch as new
                 !(batchedRequest && i > 0);
@@ -4952,6 +4986,16 @@
     return OK;
 }
 
+status_t Camera3Device::RequestThread::setCameraMute(bool enabled) {
+    ATRACE_CALL();
+    Mutex::Autolock l(mTriggerMutex);
+    if (enabled != mCameraMute) {
+        mCameraMute = enabled;
+        mCameraMuteChanged = true;
+    }
+    return OK;
+}
+
 nsecs_t Camera3Device::getExpectedInFlightDuration() {
     ATRACE_CALL();
     std::lock_guard<std::mutex> l(mInFlightLock);
@@ -5504,6 +5548,61 @@
     return false;
 }
 
+bool Camera3Device::RequestThread::overrideTestPattern(
+        const sp<CaptureRequest> &request) {
+    ATRACE_CALL();
+
+    Mutex::Autolock l(mTriggerMutex);
+
+    bool changed = false;
+
+    int32_t testPatternMode = request->mOriginalTestPatternMode;
+    int32_t testPatternData[4] = {
+        request->mOriginalTestPatternData[0],
+        request->mOriginalTestPatternData[1],
+        request->mOriginalTestPatternData[2],
+        request->mOriginalTestPatternData[3]
+    };
+
+    if (mCameraMute) {
+        testPatternMode = ANDROID_SENSOR_TEST_PATTERN_MODE_SOLID_COLOR;
+        testPatternData[0] = 0;
+        testPatternData[1] = 0;
+        testPatternData[2] = 0;
+        testPatternData[3] = 0;
+    }
+
+    CameraMetadata &metadata = request->mSettingsList.begin()->metadata;
+
+    auto testPatternEntry = metadata.find(ANDROID_SENSOR_TEST_PATTERN_MODE);
+    if (testPatternEntry.count > 0) {
+        if (testPatternEntry.data.i32[0] != testPatternMode) {
+            testPatternEntry.data.i32[0] = testPatternMode;
+            changed = true;
+        }
+    } else {
+        metadata.update(ANDROID_SENSOR_TEST_PATTERN_MODE,
+                &testPatternMode, 1);
+        changed = true;
+    }
+
+    auto testPatternColor = metadata.find(ANDROID_SENSOR_TEST_PATTERN_DATA);
+    if (testPatternColor.count > 0) {
+        for (size_t i = 0; i < 4; i++) {
+            if (testPatternColor.data.i32[i] != (int32_t)testPatternData[i]) {
+                testPatternColor.data.i32[i] = testPatternData[i];
+                changed = true;
+            }
+        }
+    } else {
+        metadata.update(ANDROID_SENSOR_TEST_PATTERN_DATA,
+                (int32_t*)testPatternData, 4);
+        changed = true;
+    }
+
+    return changed;
+}
+
 /**
  * PreparerThread inner class methods
  */
@@ -6129,4 +6228,22 @@
     return mRequestThread->setRotateAndCropAutoBehavior(rotateAndCropValue);
 }
 
+bool Camera3Device::supportsCameraMute() {
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    return mSupportCameraMute;
+}
+
+status_t Camera3Device::setCameraMute(bool enabled) {
+    ATRACE_CALL();
+    Mutex::Autolock il(mInterfaceLock);
+    Mutex::Autolock l(mLock);
+
+    if (mRequestThread == nullptr || !mSupportCameraMute) {
+        return INVALID_OPERATION;
+    }
+    return mRequestThread->setCameraMute(enabled);
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b06ce45..567b3ad 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -238,6 +238,21 @@
     status_t setRotateAndCropAutoBehavior(
             camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
 
+    /**
+     * Whether camera muting (producing black-only output) is supported.
+     *
+     * Calling setCameraMute(true) when this returns false will return an
+     * INVALID_OPERATION error.
+     */
+    bool supportsCameraMute();
+
+    /**
+     * Mute the camera.
+     *
+     * When muted, black image data is output on all output streams.
+     */
+    status_t setCameraMute(bool enabled);
+
     // Get the status trackeer for the camera device
     wp<camera3::StatusTracker> getStatusTracker() { return mStatusTracker; }
 
@@ -525,6 +540,11 @@
         // overriding of ROTATE_AND_CROP value and adjustment of coordinates
         // in several other controls in both the request and the result
         bool                                mRotateAndCropAuto;
+        // Original value of TEST_PATTERN_MODE and DATA so that they can be
+        // restored when sensor muting is turned off
+        int32_t                             mOriginalTestPatternMode;
+        int32_t                             mOriginalTestPatternData[4];
+
         // Whether this capture request has its zoom ratio set to 1.0x before
         // the framework overrides it for camera HAL consumption.
         bool                                mZoomRatioIs1x;
@@ -868,6 +888,7 @@
         status_t setRotateAndCropAutoBehavior(
                 camera_metadata_enum_android_scaler_rotate_and_crop_t rotateAndCropValue);
 
+        status_t setCameraMute(bool enabled);
       protected:
 
         virtual bool threadLoop();
@@ -889,6 +910,10 @@
         // Override rotate_and_crop control if needed; returns true if the current value was changed
         bool               overrideAutoRotateAndCrop(const sp<CaptureRequest> &request);
 
+        // Override test_pattern control if needed for camera mute; returns true
+        // if the current value was changed
+        bool               overrideTestPattern(const sp<CaptureRequest> &request);
+
         static const nsecs_t kRequestTimeout = 50e6; // 50 ms
 
         // TODO: does this need to be adjusted for long exposure requests?
@@ -1011,6 +1036,8 @@
         uint32_t           mCurrentAfTriggerId;
         uint32_t           mCurrentPreCaptureTriggerId;
         camera_metadata_enum_android_scaler_rotate_and_crop_t mRotateAndCropOverride;
+        bool               mCameraMute;
+        bool               mCameraMuteChanged;
 
         int64_t            mRepeatingLastFrameNumber;
 
@@ -1276,6 +1303,10 @@
 
     // Whether HAL supports offline processing capability.
     bool mSupportOfflineProcessing = false;
+
+    // Whether the HAL supports camera muting via test pattern
+    bool mSupportCameraMute = false;
+
 }; // class Camera3Device
 
 }; // namespace android
diff --git a/services/tuner/TunerDemux.cpp b/services/tuner/TunerDemux.cpp
index 0e0cd3b..1f8b70d 100644
--- a/services/tuner/TunerDemux.cpp
+++ b/services/tuner/TunerDemux.cpp
@@ -124,4 +124,17 @@
     *_aidl_return = ::ndk::SharedRefBase::make<TunerDvr>(hidlDvr, dvrType);
     return Status::ok();
 }
+
+Status TunerDemux::close() {
+    if (mDemux == nullptr) {
+        ALOGE("IDemux is not initialized.");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result res = mDemux->close();
+    if (res != Result::SUCCESS) {
+        return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+    }
+    return Status::ok();
+}
 }  // namespace android
diff --git a/services/tuner/TunerDemux.h b/services/tuner/TunerDemux.h
index 675bb7c..594fd66 100644
--- a/services/tuner/TunerDemux.h
+++ b/services/tuner/TunerDemux.h
@@ -47,6 +47,7 @@
     Status openDvr(
         int dvbType, int bufferSize, const shared_ptr<ITunerDvrCallback>& cb,
         shared_ptr<ITunerDvr>* _aidl_return) override;
+    Status close() override;
 
 private:
     sp<IDemux> mDemux;
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
index 722d36d..edfc291 100644
--- a/services/tuner/TunerFilter.cpp
+++ b/services/tuner/TunerFilter.cpp
@@ -16,16 +16,19 @@
 
 #define LOG_TAG "TunerFilter"
 
-#include <aidlcommonsupport/NativeHandle.h>
 #include "TunerFilter.h"
 
+using ::android::hardware::hidl_handle;
+using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
+using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
 using ::android::hardware::tv::tuner::V1_0::Result;
-
 namespace android {
 
-TunerFilter::TunerFilter(sp<IFilter> filter, sp<IFilterCallback> callback) {
+TunerFilter::TunerFilter(
+        sp<IFilter> filter, sp<IFilterCallback> callback) {
     mFilter = filter;
     mFilter_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(filter);
     mFilterCallback = callback;
@@ -103,6 +106,7 @@
                     break;
                 }
             }
+            halSettings.ts(ts);
             break;
         }
     }
@@ -113,6 +117,44 @@
     return Status::ok();
 }
 
+Status TunerFilter::getAvSharedHandleInfo(TunerFilterSharedHandleInfo* _aidl_return) {
+    if (mFilter_1_1 == nullptr) {
+        ALOGE("IFilter_1_1 is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result res;
+    mFilter_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) {
+        res = r;
+        if (res == Result::SUCCESS) {
+            TunerFilterSharedHandleInfo info{
+                .handle = dupToAidl(hidl_handle(avMemory.getNativeHandle())),
+                .size = static_cast<int64_t>(avMemSize),
+            };
+            *_aidl_return = std::move(info);
+        } else {
+            _aidl_return = NULL;
+        }
+    });
+
+    return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+}
+
+Status TunerFilter::releaseAvHandle(
+        const ::aidl::android::hardware::common::NativeHandle& handle, int64_t avDataId) {
+    if (mFilter == nullptr) {
+        ALOGE("IFilter is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result res = mFilter->releaseAvHandle(hidl_handle(makeFromAidl(handle)), avDataId);
+    if (res != Result::SUCCESS) {
+        return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+    }
+    return Status::ok();
+}
+
+
 Status TunerFilter::start() {
     if (mFilter == nullptr) {
         ALOGE("IFilter is not initialized");
@@ -149,6 +191,18 @@
     return Status::ok();
 }
 
+Status TunerFilter::close() {
+    if (mFilter == nullptr) {
+        ALOGE("IFilter is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+    Result res = mFilter->close();
+    if (res != Result::SUCCESS) {
+        return Status::fromServiceSpecificError(static_cast<int32_t>(res));
+    }
+    return Status::ok();
+}
+
 sp<IFilter> TunerFilter::getHalFilter() {
     return mFilter;
 }
@@ -164,8 +218,8 @@
         tunerMedia.streamId = static_cast<int>(mediaEvent.streamId);
         tunerMedia.isPtsPresent = mediaEvent.isPtsPresent;
         tunerMedia.pts = static_cast<long>(mediaEvent.pts);
-        tunerMedia.dataLength = static_cast<long>(mediaEvent.dataLength);
-        tunerMedia.offset = static_cast<long>(mediaEvent.offset);
+        tunerMedia.dataLength = static_cast<int>(mediaEvent.dataLength);
+        tunerMedia.offset = static_cast<int>(mediaEvent.offset);
         tunerMedia.isSecureMemory = mediaEvent.isSecureMemory;
         tunerMedia.avDataId = static_cast<long>(mediaEvent.avDataId);
         tunerMedia.mpuSequenceNumber = static_cast<int>(mediaEvent.mpuSequenceNumber);
@@ -187,7 +241,6 @@
 }
 
 Return<void> TunerFilter::FilterCallback::onFilterEvent(const DemuxFilterEvent& filterEvent) {
-    ALOGD("FilterCallback::onFilterEvent");
     std::vector<DemuxFilterEvent::Event> events = filterEvent.events;
     std::vector<TunerFilterEvent> tunerEvent;
 
@@ -203,7 +256,8 @@
             }
         }
     }
-    mTunerFilterCallback->onFilterEvent(&tunerEvent);
+
+    mTunerFilterCallback->onFilterEvent(tunerEvent);
     return Void();
 }
 
diff --git a/services/tuner/TunerFilter.h b/services/tuner/TunerFilter.h
index 7f5838c..2f10435 100644
--- a/services/tuner/TunerFilter.h
+++ b/services/tuner/TunerFilter.h
@@ -19,6 +19,7 @@
 
 #include <aidl/android/media/tv/tuner/BnTunerFilter.h>
 #include <aidl/android/media/tv/tuner/ITunerFilterCallback.h>
+#include <aidlcommonsupport/NativeHandle.h>
 #include <android/hardware/tv/tuner/1.1/IFilter.h>
 #include <android/hardware/tv/tuner/1.0/ITuner.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -29,6 +30,7 @@
 using ::aidl::android::media::tv::tuner::TunerFilterConfiguration;
 using ::aidl::android::media::tv::tuner::TunerFilterEvent;
 using ::aidl::android::media::tv::tuner::TunerFilterMediaEvent;
+using ::aidl::android::media::tv::tuner::TunerFilterSharedHandleInfo;
 using ::aidl::android::media::tv::tuner::TunerFilterSettings;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
@@ -39,7 +41,6 @@
 using ::android::hardware::tv::tuner::V1_0::IFilter;
 using ::android::hardware::tv::tuner::V1_0::IFilterCallback;
 
-
 namespace android {
 
 class TunerFilter : public BnTunerFilter {
@@ -50,9 +51,13 @@
     Status getId(int32_t* _aidl_return) override;
     Status getId64Bit(int64_t* _aidl_return) override;
     Status configure(const TunerFilterConfiguration& config) override;
+    Status getAvSharedHandleInfo(TunerFilterSharedHandleInfo* _aidl_return) override;
+    Status releaseAvHandle(const ::aidl::android::hardware::common::NativeHandle& handle,
+            int64_t avDataId) override;
     Status start() override;
     Status stop() override;
     Status flush() override;
+    Status close() override;
     sp<IFilter> getHalFilter();
 
     struct FilterCallback : public IFilterCallback {
diff --git a/services/tuner/TunerFrontend.cpp b/services/tuner/TunerFrontend.cpp
index e92489d..bb8b07d 100644
--- a/services/tuner/TunerFrontend.cpp
+++ b/services/tuner/TunerFrontend.cpp
@@ -66,7 +66,6 @@
 using ::android::hardware::tv::tuner::V1_0::FrontendIsdbtSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanAtsc3PlpInfo;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanType;
-using ::android::hardware::tv::tuner::V1_0::FrontendSettings;;
 using ::android::hardware::tv::tuner::V1_0::Result;
 using ::android::hardware::tv::tuner::V1_1::FrontendModulation;
 
@@ -80,6 +79,7 @@
 
 TunerFrontend::~TunerFrontend() {
     mFrontend = NULL;
+    mFrontend_1_1 = NULL;
     mId = -1;
 }
 
@@ -103,12 +103,33 @@
     return Status::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
-Status TunerFrontend::tune(const TunerFrontendSettings& /*settings*/) {
-    return Status::ok();
+Status TunerFrontend::tune(const TunerFrontendSettings& settings) {
+    if (mFrontend == NULL) {
+        ALOGD("IFrontend is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    FrontendSettings frontendSettings = getHidlFrontendSettings(settings);
+    Result status = mFrontend->tune(frontendSettings);
+    if (status == Result::SUCCESS) {
+        return Status::ok();
+    }
+
+    return Status::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
 Status TunerFrontend::stopTune() {
-    return Status::ok();
+    if (mFrontend == NULL) {
+        ALOGD("IFrontend is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result status = mFrontend->stopTune();
+    if (status == Result::SUCCESS) {
+        return Status::ok();
+    }
+
+    return Status::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
 Status TunerFrontend::scan(const TunerFrontendSettings& settings, int frontendScanType) {
@@ -117,165 +138,7 @@
         return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
     }
 
-    // TODO: extend TunerFrontendSettings to use 1.1 types
-    FrontendSettings frontendSettings;
-    switch (settings.getTag()) {
-        case TunerFrontendSettings::analog:
-            frontendSettings.analog({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::analog>().frequency),
-                .type = static_cast<FrontendAnalogType>(
-                        settings.get<TunerFrontendSettings::analog>().signalType),
-                .sifStandard = static_cast<FrontendAnalogSifStandard>(
-                        settings.get<TunerFrontendSettings::analog>().sifStandard),
-            });
-            break;
-        case TunerFrontendSettings::atsc:
-            frontendSettings.atsc({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::atsc>().frequency),
-                .modulation = static_cast<FrontendAtscModulation>(
-                        settings.get<TunerFrontendSettings::atsc>().modulation),
-            });
-            break;
-        case TunerFrontendSettings::atsc3:
-            frontendSettings.atsc3({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::atsc3>().frequency),
-                .bandwidth = static_cast<FrontendAtsc3Bandwidth>(
-                        settings.get<TunerFrontendSettings::atsc3>().bandwidth),
-                .demodOutputFormat = static_cast<FrontendAtsc3DemodOutputFormat>(
-                        settings.get<TunerFrontendSettings::atsc3>().demodOutputFormat),
-                .plpSettings = getAtsc3PlpSettings(settings.get<TunerFrontendSettings::atsc3>()),
-            });
-            break;
-        case TunerFrontendSettings::cable:
-            frontendSettings.dvbc({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::cable>().frequency),
-                .modulation = static_cast<FrontendDvbcModulation>(
-                        settings.get<TunerFrontendSettings::cable>().modulation),
-                .fec = static_cast<FrontendInnerFec>(
-                        settings.get<TunerFrontendSettings::cable>().innerFec),
-                .symbolRate = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::cable>().symbolRate),
-                .outerFec = static_cast<FrontendDvbcOuterFec>(
-                        settings.get<TunerFrontendSettings::cable>().outerFec),
-                .annex = static_cast<FrontendDvbcAnnex>(
-                        settings.get<TunerFrontendSettings::cable>().annex),
-                .spectralInversion = static_cast<FrontendDvbcSpectralInversion>(
-                        settings.get<TunerFrontendSettings::cable>().spectralInversion),
-            });
-            break;
-        case TunerFrontendSettings::dvbs:
-            frontendSettings.dvbs({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::dvbs>().frequency),
-                .modulation = static_cast<FrontendDvbsModulation>(
-                        settings.get<TunerFrontendSettings::dvbs>().modulation),
-                .coderate = getDvbsCodeRate(
-                        settings.get<TunerFrontendSettings::dvbs>().codeRate),
-                .symbolRate = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::dvbs>().symbolRate),
-                .rolloff = static_cast<FrontendDvbsRolloff>(
-                        settings.get<TunerFrontendSettings::dvbs>().rolloff),
-                .pilot = static_cast<FrontendDvbsPilot>(
-                        settings.get<TunerFrontendSettings::dvbs>().pilot),
-                .inputStreamId = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::dvbs>().inputStreamId),
-                .standard = static_cast<FrontendDvbsStandard>(
-                        settings.get<TunerFrontendSettings::dvbs>().standard),
-                .vcmMode = static_cast<FrontendDvbsVcmMode>(
-                        settings.get<TunerFrontendSettings::dvbs>().vcm),
-            });
-            break;
-        case TunerFrontendSettings::dvbt:
-            frontendSettings.dvbt({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::dvbt>().frequency),
-                .transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
-                        settings.get<TunerFrontendSettings::dvbt>().transmissionMode),
-                .bandwidth = static_cast<FrontendDvbtBandwidth>(
-                        settings.get<TunerFrontendSettings::dvbt>().bandwidth),
-                .constellation = static_cast<FrontendDvbtConstellation>(
-                        settings.get<TunerFrontendSettings::dvbt>().constellation),
-                .hierarchy = static_cast<FrontendDvbtHierarchy>(
-                        settings.get<TunerFrontendSettings::dvbt>().hierarchy),
-                .hpCoderate = static_cast<FrontendDvbtCoderate>(
-                        settings.get<TunerFrontendSettings::dvbt>().hpCodeRate),
-                .lpCoderate = static_cast<FrontendDvbtCoderate>(
-                        settings.get<TunerFrontendSettings::dvbt>().lpCodeRate),
-                .guardInterval = static_cast<FrontendDvbtGuardInterval>(
-                        settings.get<TunerFrontendSettings::dvbt>().guardInterval),
-                .isHighPriority = settings.get<TunerFrontendSettings::dvbt>().isHighPriority,
-                .standard = static_cast<FrontendDvbtStandard>(
-                        settings.get<TunerFrontendSettings::dvbt>().standard),
-                .isMiso = settings.get<TunerFrontendSettings::dvbt>().isMiso,
-                .plpMode = static_cast<FrontendDvbtPlpMode>(
-                        settings.get<TunerFrontendSettings::dvbt>().plpMode),
-                .plpId = static_cast<uint8_t>(
-                        settings.get<TunerFrontendSettings::dvbt>().plpId),
-                .plpGroupId = static_cast<uint8_t>(
-                        settings.get<TunerFrontendSettings::dvbt>().plpGroupId),
-            });
-            break;
-        case TunerFrontendSettings::isdbs:
-            frontendSettings.isdbs({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbs>().frequency),
-                .streamId = static_cast<uint16_t>(
-                        settings.get<TunerFrontendSettings::isdbs>().streamId),
-                .streamIdType = static_cast<FrontendIsdbsStreamIdType>(
-                        settings.get<TunerFrontendSettings::isdbs>().streamIdType),
-                .modulation = static_cast<FrontendIsdbsModulation>(
-                        settings.get<TunerFrontendSettings::isdbs>().modulation),
-                .coderate = static_cast<FrontendIsdbsCoderate>(
-                        settings.get<TunerFrontendSettings::isdbs>().codeRate),
-                .symbolRate = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbs>().symbolRate),
-                .rolloff = static_cast<FrontendIsdbsRolloff>(
-                        settings.get<TunerFrontendSettings::isdbs>().rolloff),
-            });
-            break;
-        case TunerFrontendSettings::isdbs3:
-            frontendSettings.isdbs3({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbs3>().frequency),
-                .streamId = static_cast<uint16_t>(
-                        settings.get<TunerFrontendSettings::isdbs3>().streamId),
-                .streamIdType = static_cast<FrontendIsdbsStreamIdType>(
-                        settings.get<TunerFrontendSettings::isdbs3>().streamIdType),
-                .modulation = static_cast<FrontendIsdbs3Modulation>(
-                        settings.get<TunerFrontendSettings::isdbs3>().modulation),
-                .coderate = static_cast<FrontendIsdbs3Coderate>(
-                        settings.get<TunerFrontendSettings::isdbs3>().codeRate),
-                .symbolRate = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbs3>().symbolRate),
-                .rolloff = static_cast<FrontendIsdbs3Rolloff>(
-                        settings.get<TunerFrontendSettings::isdbs3>().rolloff),
-            });
-            break;
-        case TunerFrontendSettings::isdbt:
-            frontendSettings.isdbt({
-                .frequency = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbt>().frequency),
-                .modulation = static_cast<FrontendIsdbtModulation>(
-                        settings.get<TunerFrontendSettings::isdbt>().modulation),
-                .bandwidth = static_cast<FrontendIsdbtBandwidth>(
-                        settings.get<TunerFrontendSettings::isdbt>().bandwidth),
-                .mode = static_cast<FrontendIsdbtMode>(
-                        settings.get<TunerFrontendSettings::isdbt>().mode),
-                .coderate = static_cast<FrontendIsdbtCoderate>(
-                        settings.get<TunerFrontendSettings::isdbt>().codeRate),
-                .guardInterval = static_cast<FrontendIsdbtGuardInterval>(
-                        settings.get<TunerFrontendSettings::isdbt>().guardInterval),
-                .serviceAreaId = static_cast<uint32_t>(
-                        settings.get<TunerFrontendSettings::isdbt>().serviceAreaId),
-            });
-            break;
-        default:
-            break;
-    }
+    FrontendSettings frontendSettings = getHidlFrontendSettings(settings);
     Result status = mFrontend->scan(
             frontendSettings, static_cast<FrontendScanType>(frontendScanType));
     if (status == Result::SUCCESS) {
@@ -286,7 +149,17 @@
 }
 
 Status TunerFrontend::stopScan() {
-    return Status::ok();
+    if (mFrontend == NULL) {
+        ALOGD("IFrontend is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result status = mFrontend->stopScan();
+    if (status == Result::SUCCESS) {
+        return Status::ok();
+    }
+
+    return Status::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
 Status TunerFrontend::setLnb(int /*lnbHandle*/) {
@@ -298,7 +171,17 @@
 }
 
 Status TunerFrontend::close() {
-    return Status::ok();
+    if (mFrontend == NULL) {
+        ALOGD("IFrontend is not initialized");
+        return Status::fromServiceSpecificError(static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    Result status = mFrontend->close();
+    if (status == Result::SUCCESS) {
+        return Status::ok();
+    }
+
+    return Status::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
 Status TunerFrontend::getStatus(const vector<int32_t>& /*statusTypes*/,
@@ -500,4 +383,167 @@
     };
     return coderate;
 }
+
+FrontendSettings TunerFrontend::getHidlFrontendSettings(const TunerFrontendSettings& settings) {
+    // TODO: extend TunerFrontendSettings to use 1.1 types
+    FrontendSettings frontendSettings;
+    switch (settings.getTag()) {
+        case TunerFrontendSettings::analog:
+            frontendSettings.analog({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::analog>().frequency),
+                .type = static_cast<FrontendAnalogType>(
+                        settings.get<TunerFrontendSettings::analog>().signalType),
+                .sifStandard = static_cast<FrontendAnalogSifStandard>(
+                        settings.get<TunerFrontendSettings::analog>().sifStandard),
+            });
+            break;
+        case TunerFrontendSettings::atsc:
+            frontendSettings.atsc({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::atsc>().frequency),
+                .modulation = static_cast<FrontendAtscModulation>(
+                        settings.get<TunerFrontendSettings::atsc>().modulation),
+            });
+            break;
+        case TunerFrontendSettings::atsc3:
+            frontendSettings.atsc3({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::atsc3>().frequency),
+                .bandwidth = static_cast<FrontendAtsc3Bandwidth>(
+                        settings.get<TunerFrontendSettings::atsc3>().bandwidth),
+                .demodOutputFormat = static_cast<FrontendAtsc3DemodOutputFormat>(
+                        settings.get<TunerFrontendSettings::atsc3>().demodOutputFormat),
+                .plpSettings = getAtsc3PlpSettings(settings.get<TunerFrontendSettings::atsc3>()),
+            });
+            break;
+        case TunerFrontendSettings::cable:
+            frontendSettings.dvbc({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::cable>().frequency),
+                .modulation = static_cast<FrontendDvbcModulation>(
+                        settings.get<TunerFrontendSettings::cable>().modulation),
+                .fec = static_cast<FrontendInnerFec>(
+                        settings.get<TunerFrontendSettings::cable>().innerFec),
+                .symbolRate = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::cable>().symbolRate),
+                .outerFec = static_cast<FrontendDvbcOuterFec>(
+                        settings.get<TunerFrontendSettings::cable>().outerFec),
+                .annex = static_cast<FrontendDvbcAnnex>(
+                        settings.get<TunerFrontendSettings::cable>().annex),
+                .spectralInversion = static_cast<FrontendDvbcSpectralInversion>(
+                        settings.get<TunerFrontendSettings::cable>().spectralInversion),
+            });
+            break;
+        case TunerFrontendSettings::dvbs:
+            frontendSettings.dvbs({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::dvbs>().frequency),
+                .modulation = static_cast<FrontendDvbsModulation>(
+                        settings.get<TunerFrontendSettings::dvbs>().modulation),
+                .coderate = getDvbsCodeRate(
+                        settings.get<TunerFrontendSettings::dvbs>().codeRate),
+                .symbolRate = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::dvbs>().symbolRate),
+                .rolloff = static_cast<FrontendDvbsRolloff>(
+                        settings.get<TunerFrontendSettings::dvbs>().rolloff),
+                .pilot = static_cast<FrontendDvbsPilot>(
+                        settings.get<TunerFrontendSettings::dvbs>().pilot),
+                .inputStreamId = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::dvbs>().inputStreamId),
+                .standard = static_cast<FrontendDvbsStandard>(
+                        settings.get<TunerFrontendSettings::dvbs>().standard),
+                .vcmMode = static_cast<FrontendDvbsVcmMode>(
+                        settings.get<TunerFrontendSettings::dvbs>().vcm),
+            });
+            break;
+        case TunerFrontendSettings::dvbt:
+            frontendSettings.dvbt({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::dvbt>().frequency),
+                .transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
+                        settings.get<TunerFrontendSettings::dvbt>().transmissionMode),
+                .bandwidth = static_cast<FrontendDvbtBandwidth>(
+                        settings.get<TunerFrontendSettings::dvbt>().bandwidth),
+                .constellation = static_cast<FrontendDvbtConstellation>(
+                        settings.get<TunerFrontendSettings::dvbt>().constellation),
+                .hierarchy = static_cast<FrontendDvbtHierarchy>(
+                        settings.get<TunerFrontendSettings::dvbt>().hierarchy),
+                .hpCoderate = static_cast<FrontendDvbtCoderate>(
+                        settings.get<TunerFrontendSettings::dvbt>().hpCodeRate),
+                .lpCoderate = static_cast<FrontendDvbtCoderate>(
+                        settings.get<TunerFrontendSettings::dvbt>().lpCodeRate),
+                .guardInterval = static_cast<FrontendDvbtGuardInterval>(
+                        settings.get<TunerFrontendSettings::dvbt>().guardInterval),
+                .isHighPriority = settings.get<TunerFrontendSettings::dvbt>().isHighPriority,
+                .standard = static_cast<FrontendDvbtStandard>(
+                        settings.get<TunerFrontendSettings::dvbt>().standard),
+                .isMiso = settings.get<TunerFrontendSettings::dvbt>().isMiso,
+                .plpMode = static_cast<FrontendDvbtPlpMode>(
+                        settings.get<TunerFrontendSettings::dvbt>().plpMode),
+                .plpId = static_cast<uint8_t>(
+                        settings.get<TunerFrontendSettings::dvbt>().plpId),
+                .plpGroupId = static_cast<uint8_t>(
+                        settings.get<TunerFrontendSettings::dvbt>().plpGroupId),
+            });
+            break;
+        case TunerFrontendSettings::isdbs:
+            frontendSettings.isdbs({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbs>().frequency),
+                .streamId = static_cast<uint16_t>(
+                        settings.get<TunerFrontendSettings::isdbs>().streamId),
+                .streamIdType = static_cast<FrontendIsdbsStreamIdType>(
+                        settings.get<TunerFrontendSettings::isdbs>().streamIdType),
+                .modulation = static_cast<FrontendIsdbsModulation>(
+                        settings.get<TunerFrontendSettings::isdbs>().modulation),
+                .coderate = static_cast<FrontendIsdbsCoderate>(
+                        settings.get<TunerFrontendSettings::isdbs>().codeRate),
+                .symbolRate = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbs>().symbolRate),
+                .rolloff = static_cast<FrontendIsdbsRolloff>(
+                        settings.get<TunerFrontendSettings::isdbs>().rolloff),
+            });
+            break;
+        case TunerFrontendSettings::isdbs3:
+            frontendSettings.isdbs3({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbs3>().frequency),
+                .streamId = static_cast<uint16_t>(
+                        settings.get<TunerFrontendSettings::isdbs3>().streamId),
+                .streamIdType = static_cast<FrontendIsdbsStreamIdType>(
+                        settings.get<TunerFrontendSettings::isdbs3>().streamIdType),
+                .modulation = static_cast<FrontendIsdbs3Modulation>(
+                        settings.get<TunerFrontendSettings::isdbs3>().modulation),
+                .coderate = static_cast<FrontendIsdbs3Coderate>(
+                        settings.get<TunerFrontendSettings::isdbs3>().codeRate),
+                .symbolRate = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbs3>().symbolRate),
+                .rolloff = static_cast<FrontendIsdbs3Rolloff>(
+                        settings.get<TunerFrontendSettings::isdbs3>().rolloff),
+            });
+            break;
+        case TunerFrontendSettings::isdbt:
+            frontendSettings.isdbt({
+                .frequency = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbt>().frequency),
+                .modulation = static_cast<FrontendIsdbtModulation>(
+                        settings.get<TunerFrontendSettings::isdbt>().modulation),
+                .bandwidth = static_cast<FrontendIsdbtBandwidth>(
+                        settings.get<TunerFrontendSettings::isdbt>().bandwidth),
+                .mode = static_cast<FrontendIsdbtMode>(
+                        settings.get<TunerFrontendSettings::isdbt>().mode),
+                .coderate = static_cast<FrontendIsdbtCoderate>(
+                        settings.get<TunerFrontendSettings::isdbt>().codeRate),
+                .guardInterval = static_cast<FrontendIsdbtGuardInterval>(
+                        settings.get<TunerFrontendSettings::isdbt>().guardInterval),
+                .serviceAreaId = static_cast<uint32_t>(
+                        settings.get<TunerFrontendSettings::isdbt>().serviceAreaId),
+            });
+            break;
+        default:
+            break;
+    }
+    return frontendSettings;
+}
 }  // namespace android
diff --git a/services/tuner/TunerFrontend.h b/services/tuner/TunerFrontend.h
index 99cdcdf..431022d 100644
--- a/services/tuner/TunerFrontend.h
+++ b/services/tuner/TunerFrontend.h
@@ -41,6 +41,7 @@
 using ::android::hardware::tv::tuner::V1_0::FrontendId;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage;
 using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType;
+using ::android::hardware::tv::tuner::V1_0::FrontendSettings;;
 using ::android::hardware::tv::tuner::V1_0::IFrontend;
 using ::android::hardware::tv::tuner::V1_1::IFrontendCallback;
 using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1;
@@ -85,6 +86,7 @@
     hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings(
             const TunerFrontendAtsc3Settings& settings);
     FrontendDvbsCodeRate getDvbsCodeRate(const TunerFrontendDvbsCodeRate& codeRate);
+    FrontendSettings getHidlFrontendSettings(const TunerFrontendSettings& settings);
 
     int mId;
     sp<IFrontend> mFrontend;
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl
index f6de618..fa2c1ff 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerDemux.aidl
@@ -44,4 +44,9 @@
      * Open a DVR (Digital Video Record) instance in the demux.
      */
     ITunerDvr openDvr(in int dvbType, in int bufferSize, in ITunerDvrCallback cb);
+
+    /**
+     * Releases the ITunerDemux instance.
+     */
+    void close();
 }
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
index 37166aa..1d5544f 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilter.aidl
@@ -16,7 +16,9 @@
 
 package android.media.tv.tuner;
 
+import android.hardware.common.NativeHandle;
 import android.media.tv.tuner.TunerFilterConfiguration;
+import android.media.tv.tuner.TunerFilterSharedHandleInfo;
 
 /**
  * Tuner Filter interface handles tuner related operations.
@@ -40,6 +42,16 @@
     void configure(in TunerFilterConfiguration config);
 
     /**
+     * Get the a/v shared memory handle
+     */
+    TunerFilterSharedHandleInfo getAvSharedHandleInfo();
+
+    /**
+     * Release the handle reported by the HAL for AV memory.
+     */
+    void releaseAvHandle(in NativeHandle handle, in long avDataId);
+
+    /**
      * Start the filter.
      */
     void start();
@@ -53,4 +65,9 @@
      * Flush the filter.
      */
     void flush();
+
+    /**
+     * Close the filter.
+     */
+    void close();
 }
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl
index f9f86ac..e7a52a7 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFilterCallback.aidl
@@ -32,5 +32,5 @@
     /**
      * Notify the client that a new filter event happened.
      */
-    void onFilterEvent(out TunerFilterEvent[] filterEvent);
+    void onFilterEvent(in TunerFilterEvent[] filterEvent);
 }
diff --git a/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl
new file mode 100644
index 0000000..122dfc3
--- /dev/null
+++ b/services/tuner/aidl/android/media/tv/tuner/TunerFilterSharedHandleInfo.aidl
@@ -0,0 +1,29 @@
+/**
+ * Copyright 2021, 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.media.tv.tuner;
+
+import android.hardware.common.NativeHandle;
+
+/**
+ * Filter Shared Handle Information.
+ *
+ * {@hide}
+ */
+parcelable TunerFilterSharedHandleInfo {
+    NativeHandle handle;
+    long size;
+}
\ No newline at end of file