Merge "cameraservice_test: Create host-side build."
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.cpp b/media/libaaudio/src/client/AAudioFlowGraph.cpp
index 5b46ae0..d0c3238 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.cpp
+++ b/media/libaaudio/src/client/AAudioFlowGraph.cpp
@@ -42,14 +42,15 @@
                           audio_format_t sinkFormat,
                           int32_t sinkChannelCount,
                           bool useMonoBlend,
-                          float audioBalance) {
+                          float audioBalance,
+                          bool isExclusive) {
     FlowGraphPortFloatOutput *lastOutput = nullptr;
 
     // TODO change back to ALOGD
     ALOGI("%s() source format = 0x%08x, channels = %d, sink format = 0x%08x, channels = %d, "
-          "useMonoBlend = %d, audioBalance = %f",
+          "useMonoBlend = %d, audioBalance = %f, isExclusive %d",
           __func__, sourceFormat, sourceChannelCount, sinkFormat, sinkChannelCount,
-          useMonoBlend, audioBalance);
+          useMonoBlend, audioBalance, isExclusive);
 
     switch (sourceFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
@@ -94,22 +95,25 @@
         return AAUDIO_ERROR_UNIMPLEMENTED;
     }
 
-    // Apply volume ramps to set the left/right audio balance and target volumes.
-    // The signals will be decoupled, volume ramps will be applied, before the signals are
-    // combined again.
-    mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
-    mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
-    lastOutput->connect(&mMultiToManyConverter->input);
-    for (int i = 0; i < sinkChannelCount; i++) {
-        mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
-        mPanningVolumes.emplace_back(1.0f);
-        lastOutput = mMultiToManyConverter->outputs[i].get();
-        lastOutput->connect(&(mVolumeRamps[i].get()->input));
-        lastOutput = &(mVolumeRamps[i].get()->output);
-        lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+    // Apply volume ramps for only exclusive streams.
+    if (isExclusive) {
+        // Apply volume ramps to set the left/right audio balance and target volumes.
+        // The signals will be decoupled, volume ramps will be applied, before the signals are
+        // combined again.
+        mMultiToManyConverter = std::make_unique<MultiToManyConverter>(sinkChannelCount);
+        mManyToMultiConverter = std::make_unique<ManyToMultiConverter>(sinkChannelCount);
+        lastOutput->connect(&mMultiToManyConverter->input);
+        for (int i = 0; i < sinkChannelCount; i++) {
+            mVolumeRamps.emplace_back(std::make_unique<RampLinear>(1));
+            mPanningVolumes.emplace_back(1.0f);
+            lastOutput = mMultiToManyConverter->outputs[i].get();
+            lastOutput->connect(&(mVolumeRamps[i].get()->input));
+            lastOutput = &(mVolumeRamps[i].get()->output);
+            lastOutput->connect(mManyToMultiConverter->inputs[i].get());
+        }
+        lastOutput = &mManyToMultiConverter->output;
+        setAudioBalance(audioBalance);
     }
-    lastOutput = &mManyToMultiConverter->output;
-    setAudioBalance(audioBalance);
 
     switch (sinkFormat) {
         case AUDIO_FORMAT_PCM_FLOAT:
diff --git a/media/libaaudio/src/client/AAudioFlowGraph.h b/media/libaaudio/src/client/AAudioFlowGraph.h
index 2056b70..00b6575 100644
--- a/media/libaaudio/src/client/AAudioFlowGraph.h
+++ b/media/libaaudio/src/client/AAudioFlowGraph.h
@@ -43,6 +43,7 @@
      * @param useMonoBlend
      * @param audioBalance
      * @param channelMask
+     * @param isExclusive
      * @return
      */
     aaudio_result_t configure(audio_format_t sourceFormat,
@@ -50,7 +51,8 @@
                               audio_format_t sinkFormat,
                               int32_t sinkChannelCount,
                               bool useMonoBlend,
-                              float audioBalance);
+                              float audioBalance,
+                              bool isExclusive);
 
     void process(const void *source, void *destination, int32_t numFrames);
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 8292573..450d390 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -54,7 +54,8 @@
                              getDeviceFormat(),
                              getDeviceChannelCount(),
                              getRequireMonoBlend(),
-                             getAudioBalance());
+                             getAudioBalance(),
+                             (getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE));
 
         if (result != AAUDIO_OK) {
             safeReleaseClose();
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index f81aa87..323e002 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -3314,4 +3314,53 @@
     return trackSecondaryOutputInfo;
 }
 
+ConversionResult<audio_direct_mode_t>
+aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl) {
+    switch (aidl) {
+        case media::AudioDirectMode::NONE:
+            return AUDIO_DIRECT_NOT_SUPPORTED;
+        case media::AudioDirectMode::OFFLOAD:
+            return AUDIO_DIRECT_OFFLOAD_SUPPORTED;
+        case media::AudioDirectMode::OFFLOAD_GAPLESS:
+            return AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED;
+        case media::AudioDirectMode::BITSTREAM:
+            return AUDIO_DIRECT_BITSTREAM_SUPPORTED;
+    }
+    return unexpected(BAD_VALUE);
+}
+ConversionResult<media::AudioDirectMode>
+legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy) {
+    switch (legacy) {
+        case AUDIO_DIRECT_NOT_SUPPORTED:
+            return media::AudioDirectMode::NONE;
+        case AUDIO_DIRECT_OFFLOAD_SUPPORTED:
+            return media::AudioDirectMode::OFFLOAD;
+        case AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED:
+            return media::AudioDirectMode::OFFLOAD_GAPLESS;
+        case AUDIO_DIRECT_BITSTREAM_SUPPORTED:
+            return media::AudioDirectMode::BITSTREAM;
+    }
+    return unexpected(BAD_VALUE);
+}
+
+ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl) {
+    using LegacyMask = std::underlying_type_t<audio_direct_mode_t>;
+
+    LegacyMask converted = VALUE_OR_RETURN(
+            (convertBitmask<LegacyMask, int32_t, audio_direct_mode_t, media::AudioDirectMode>(
+                    aidl, aidl2legacy_AudioDirectMode_audio_direct_mode_t,
+                    indexToEnum_index<media::AudioDirectMode>,
+                    enumToMask_bitmask<LegacyMask, audio_direct_mode_t>)));
+    return static_cast<audio_direct_mode_t>(converted);
+}
+ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy) {
+    using LegacyMask = std::underlying_type_t<audio_direct_mode_t>;
+
+    LegacyMask legacyMask = static_cast<LegacyMask>(legacy);
+    return convertBitmask<int32_t, LegacyMask, media::AudioDirectMode, audio_direct_mode_t>(
+            legacyMask, legacy2aidl_audio_direct_mode_t_AudioDirectMode,
+            indexToEnum_bitmask<audio_direct_mode_t>,
+            enumToMask_index<int32_t, media::AudioDirectMode>);
+}
+
 }  // namespace android
diff --git a/media/libaudioclient/Android.bp b/media/libaudioclient/Android.bp
index ab75c97..9733cb3 100644
--- a/media/libaudioclient/Android.bp
+++ b/media/libaudioclient/Android.bp
@@ -314,6 +314,7 @@
     srcs: [
         "aidl/android/media/AudioAttributesInternal.aidl",
         "aidl/android/media/AudioClient.aidl",
+        "aidl/android/media/AudioDirectMode.aidl",
         "aidl/android/media/AudioDualMonoMode.aidl",
         "aidl/android/media/AudioFlag.aidl",
         "aidl/android/media/AudioGainSys.aidl",
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 07ef246..b3c82787 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -2289,6 +2289,31 @@
     return OK;
 }
 
+status_t AudioSystem::getDirectPlaybackSupport(const audio_attributes_t *attr,
+                                               const audio_config_t *config,
+                                               audio_direct_mode_t* directMode) {
+    if (attr == nullptr || config == nullptr || directMode == nullptr) {
+        return BAD_VALUE;
+    }
+
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) {
+        return PERMISSION_DENIED;
+    }
+
+    media::AudioAttributesInternal attrAidl = VALUE_OR_RETURN_STATUS(
+            legacy2aidl_audio_attributes_t_AudioAttributesInternal(*attr));
+    AudioConfig configAidl = VALUE_OR_RETURN_STATUS(
+            legacy2aidl_audio_config_t_AudioConfig(*config, false /*isInput*/));
+
+    media::AudioDirectMode retAidl;
+    RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(
+            aps->getDirectPlaybackSupport(attrAidl, configAidl, &retAidl)));
+    *directMode = VALUE_OR_RETURN_STATUS(aidl2legacy_int32_t_audio_direct_mode_t_mask(
+            static_cast<int32_t>(retAidl)));
+    return NO_ERROR;
+}
+
 
 class CaptureStateListenerImpl : public media::BnCaptureStateListener,
                                  public IBinder::DeathRecipient {
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index da27dc8..e3b79b2 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -490,6 +490,8 @@
 status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
 {
     struct timespec total;          // total elapsed time spent waiting
+    struct timespec before;
+    bool beforeIsValid = false;
     total.tv_sec = 0;
     total.tv_nsec = 0;
     audio_track_cblk_t* cblk = mCblk;
@@ -570,17 +572,38 @@
         }
         int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
         if (!(old & CBLK_FUTEX_WAKE)) {
+            if (!beforeIsValid) {
+                clock_gettime(CLOCK_MONOTONIC, &before);
+                beforeIsValid = true;
+            }
             errno = 0;
             (void) syscall(__NR_futex, &cblk->mFutex,
                     mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
-            switch (errno) {
+            status_t error = errno; // clock_gettime can affect errno
+            {
+                struct timespec after;
+                clock_gettime(CLOCK_MONOTONIC, &after);
+                total.tv_sec += after.tv_sec - before.tv_sec;
+                // Use auto instead of long to avoid the google-runtime-int warning.
+                auto deltaNs = after.tv_nsec - before.tv_nsec;
+                if (deltaNs < 0) {
+                    deltaNs += 1000000000;
+                    total.tv_sec--;
+                }
+                if ((total.tv_nsec += deltaNs) >= 1000000000) {
+                    total.tv_nsec -= 1000000000;
+                    total.tv_sec++;
+                }
+                before = after;
+            }
+            switch (error) {
             case 0:            // normal wakeup by server, or by binderDied()
             case EWOULDBLOCK:  // benign race condition with server
             case EINTR:        // wait was interrupted by signal or other spurious wakeup
             case ETIMEDOUT:    // time-out expired
                 break;
             default:
-                status = errno;
+                status = error;
                 ALOGE("%s unexpected error %s", __func__, strerror(status));
                 goto end;
             }
diff --git a/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl
new file mode 100644
index 0000000..0da4721
--- /dev/null
+++ b/media/libaudioclient/aidl/android/media/AudioDirectMode.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 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;
+
+@Backing(type="int")
+enum AudioDirectMode {
+    NONE = 0,
+    OFFLOAD = 1,
+    OFFLOAD_GAPLESS = 2,
+    BITSTREAM = 4,
+}
diff --git a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
index 8e9ff86..7895ae3 100644
--- a/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioPolicyService.aidl
@@ -20,6 +20,7 @@
 
 import android.media.AudioAttributesEx;
 import android.media.AudioAttributesInternal;
+import android.media.AudioDirectMode;
 import android.media.AudioMix;
 import android.media.AudioOffloadMode;
 import android.media.AudioPatch;
@@ -376,4 +377,10 @@
     boolean canBeSpatialized(in @nullable AudioAttributesInternal attr,
                              in @nullable AudioConfig config,
                              in AudioDevice[] devices);
+
+    /**
+     * Query how the direct playback is currently supported on the device.
+     */
+     AudioDirectMode getDirectPlaybackSupport(in AudioAttributesInternal attr,
+                                              in AudioConfig config);
 }
diff --git a/media/libaudioclient/include/media/AidlConversion.h b/media/libaudioclient/include/media/AidlConversion.h
index a6c93cf..e769303 100644
--- a/media/libaudioclient/include/media/AidlConversion.h
+++ b/media/libaudioclient/include/media/AidlConversion.h
@@ -23,6 +23,7 @@
 
 #include <android/media/AudioAttributesInternal.h>
 #include <android/media/AudioClient.h>
+#include <android/media/AudioDirectMode.h>
 #include <android/media/AudioDualMonoMode.h>
 #include <android/media/AudioFlag.h>
 #include <android/media/AudioIoConfigEvent.h>
@@ -445,5 +446,13 @@
 legacy2aidl_TrackSecondaryOutputInfoPair_TrackSecondaryOutputInfo(
         const TrackSecondaryOutputInfoPair& legacy);
 
+ConversionResult<audio_direct_mode_t>
+aidl2legacy_AudioDirectMode_audio_direct_mode_t(media::AudioDirectMode aidl);
+ConversionResult<media::AudioDirectMode>
+legacy2aidl_audio_direct_mode_t_AudioDirectMode(audio_direct_mode_t legacy);
+
+ConversionResult<audio_direct_mode_t> aidl2legacy_int32_t_audio_direct_mode_t_mask(int32_t aidl);
+ConversionResult<int32_t> legacy2aidl_audio_direct_mode_t_int32_t_mask(audio_direct_mode_t legacy);
+
 
 }  // namespace android
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index 0e9d48c..11eb070 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -536,6 +536,19 @@
                                      const AudioDeviceTypeAddrVector &devices,
                                      bool *canBeSpatialized);
 
+    /**
+     * Query how the direct playback is currently supported on the device.
+     * @param attr audio attributes describing the playback use case
+     * @param config audio configuration for the playback
+     * @param directMode out: a set of flags describing how the direct playback is currently
+     *        supported on the device
+     * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED
+     *         in case of error.
+     */
+    static status_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+                                             const audio_config_t *config,
+                                             audio_direct_mode_t *directMode);
+
 
     // A listener for capture state changes.
     class CaptureStateListener : public virtual RefBase {
diff --git a/media/libaudiofoundation/include/media/AudioContainers.h b/media/libaudiofoundation/include/media/AudioContainers.h
index d352a96..a9c7824 100644
--- a/media/libaudiofoundation/include/media/AudioContainers.h
+++ b/media/libaudiofoundation/include/media/AudioContainers.h
@@ -111,26 +111,6 @@
     return types;
 }
 
-// FIXME: This is temporary helper function. Remove this when getting rid of all
-//  bit mask usages of audio device types.
-static inline DeviceTypeSet deviceTypesFromBitMask(audio_devices_t types) {
-    DeviceTypeSet deviceTypes;
-    if ((types & AUDIO_DEVICE_BIT_IN) == 0) {
-        for (auto deviceType : AUDIO_DEVICE_OUT_ALL_ARRAY) {
-            if ((types & deviceType) == deviceType) {
-                deviceTypes.insert(deviceType);
-            }
-        }
-    } else {
-        for (auto deviceType : AUDIO_DEVICE_IN_ALL_ARRAY) {
-            if ((types & deviceType) == deviceType) {
-                deviceTypes.insert(deviceType);
-            }
-        }
-    }
-    return deviceTypes;
-}
-
 std::string deviceTypesToString(const DeviceTypeSet& deviceTypes);
 
 bool deviceTypesToString(const DeviceTypeSet& deviceTypes, std::string &str);
diff --git a/media/libheadtracking/Android.bp b/media/libheadtracking/Android.bp
index 63b769e..b0563e2 100644
--- a/media/libheadtracking/Android.bp
+++ b/media/libheadtracking/Android.bp
@@ -18,6 +18,7 @@
       "PoseRateLimiter.cpp",
       "QuaternionUtil.cpp",
       "ScreenHeadFusion.cpp",
+      "StillnessDetector.cpp",
       "Twist.cpp",
     ],
     export_include_dirs: [
@@ -70,6 +71,7 @@
         "PoseRateLimiter-test.cpp",
         "QuaternionUtil-test.cpp",
         "ScreenHeadFusion-test.cpp",
+        "StillnessDetector-test.cpp",
         "Twist-test.cpp",
     ],
     shared_libs: [
diff --git a/media/libheadtracking/HeadTrackingProcessor.cpp b/media/libheadtracking/HeadTrackingProcessor.cpp
index 47f7cf0..257ee42 100644
--- a/media/libheadtracking/HeadTrackingProcessor.cpp
+++ b/media/libheadtracking/HeadTrackingProcessor.cpp
@@ -20,6 +20,7 @@
 #include "PoseDriftCompensator.h"
 #include "QuaternionUtil.h"
 #include "ScreenHeadFusion.h"
+#include "StillnessDetector.h"
 
 namespace android {
 namespace media {
@@ -40,6 +41,16 @@
                   .translationalDriftTimeConstant = options.translationalDriftTimeConstant,
                   .rotationalDriftTimeConstant = options.rotationalDriftTimeConstant,
           }),
+          mHeadStillnessDetector(StillnessDetector::Options{
+                  .windowDuration = options.autoRecenterWindowDuration,
+                  .translationalThreshold = options.autoRecenterTranslationalThreshold,
+                  .rotationalThreshold = options.autoRecenterRotationalThreshold,
+          }),
+          mScreenStillnessDetector(StillnessDetector::Options{
+                  .windowDuration = options.screenStillnessWindowDuration,
+                  .translationalThreshold = options.screenStillnessTranslationalThreshold,
+                  .rotationalThreshold = options.screenStillnessRotationalThreshold,
+          }),
           mModeSelector(ModeSelector::Options{.freshnessTimeout = options.freshnessTimeout},
                         initialMode),
           mRateLimiter(PoseRateLimiter::Options{
@@ -77,18 +88,35 @@
     }
 
     void calculate(int64_t timestamp) override {
-        if (mWorldToHeadTimestamp.has_value()) {
-            const Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
-            mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
-            mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
-        }
-
+        // Handle the screen first, since it might trigger a recentering of the head.
         if (mWorldToScreenTimestamp.has_value()) {
             const Pose3f worldToLogicalScreen = mScreenPoseDriftCompensator.getOutput();
+            mScreenStillnessDetector.setInput(mWorldToScreenTimestamp.value(),
+                                              worldToLogicalScreen);
+            bool screenStable = mScreenStillnessDetector.calculate(timestamp);
+            mModeSelector.setScreenStable(mWorldToScreenTimestamp.value(), screenStable);
+            // Whenever the screen is unstable, recenter the head pose.
+            if (!screenStable) {
+                recenter(true, false);
+            }
             mScreenHeadFusion.setWorldToScreenPose(mWorldToScreenTimestamp.value(),
                                                    worldToLogicalScreen);
         }
 
+        // Handle head.
+        if (mWorldToHeadTimestamp.has_value()) {
+            Pose3f worldToHead = mHeadPoseDriftCompensator.getOutput();
+            mHeadStillnessDetector.setInput(mWorldToHeadTimestamp.value(), worldToHead);
+            // Auto-recenter.
+            if (mHeadStillnessDetector.calculate(timestamp)) {
+                recenter(true, false);
+                worldToHead = mHeadPoseDriftCompensator.getOutput();
+            }
+
+            mScreenHeadFusion.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
+            mModeSelector.setWorldToHeadPose(mWorldToHeadTimestamp.value(), worldToHead);
+        }
+
         auto maybeScreenToHead = mScreenHeadFusion.calculate();
         if (maybeScreenToHead.has_value()) {
             mModeSelector.setScreenToHeadPose(maybeScreenToHead->timestamp,
@@ -114,9 +142,11 @@
     void recenter(bool recenterHead, bool recenterScreen) override {
         if (recenterHead) {
             mHeadPoseDriftCompensator.recenter();
+            mHeadStillnessDetector.reset();
         }
         if (recenterScreen) {
             mScreenPoseDriftCompensator.recenter();
+            mScreenStillnessDetector.reset();
         }
 
         // If a sensor being recentered is included in the current mode, apply rate limiting to
@@ -140,6 +170,8 @@
     Pose3f mHeadToStagePose;
     PoseDriftCompensator mHeadPoseDriftCompensator;
     PoseDriftCompensator mScreenPoseDriftCompensator;
+    StillnessDetector mHeadStillnessDetector;
+    StillnessDetector mScreenStillnessDetector;
     ScreenHeadFusion mScreenHeadFusion;
     ModeSelector mModeSelector;
     PoseRateLimiter mRateLimiter;
diff --git a/media/libheadtracking/ModeSelector-test.cpp b/media/libheadtracking/ModeSelector-test.cpp
index 6247d84..a136e6b 100644
--- a/media/libheadtracking/ModeSelector-test.cpp
+++ b/media/libheadtracking/ModeSelector-test.cpp
@@ -44,6 +44,7 @@
     ModeSelector selector(options, HeadTrackingMode::WORLD_RELATIVE);
 
     selector.setWorldToHeadPose(0, worldToHead);
+    selector.setScreenStable(0, true);
     selector.calculate(0);
     EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
     EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse());
@@ -69,14 +70,46 @@
     ModeSelector selector(options);
 
     selector.setScreenToStagePose(screenToStage);
-
     selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
     selector.setWorldToHeadPose(0, worldToHead);
+    selector.setScreenStable(0, true);
     selector.calculate(0);
     EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
     EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
 }
 
+TEST(ModeSelector, WorldRelativeUnstable) {
+    const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
+
+    ModeSelector::Options options{.freshnessTimeout = 100};
+    ModeSelector selector(options);
+
+    selector.setScreenToStagePose(screenToStage);
+    selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
+    selector.setWorldToHeadPose(0, worldToHead);
+    selector.setScreenStable(0, false);
+    selector.calculate(10);
+    EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode());
+    EXPECT_EQ(selector.getHeadToStagePose(), screenToStage);
+}
+
+TEST(ModeSelector, WorldRelativeStableStale) {
+    const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
+
+    ModeSelector::Options options{.freshnessTimeout = 100};
+    ModeSelector selector(options);
+
+    selector.setScreenToStagePose(screenToStage);
+    selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
+    selector.setWorldToHeadPose(100, worldToHead);
+    selector.setScreenStable(0, true);
+    selector.calculate(101);
+    EXPECT_EQ(HeadTrackingMode::STATIC, selector.getActualMode());
+    EXPECT_EQ(selector.getHeadToStagePose(), screenToStage);
+}
+
 TEST(ModeSelector, WorldRelativeStale) {
     const Pose3f worldToHead({1, 2, 3}, Quaternionf::UnitRandom());
     const Pose3f screenToStage({4, 5, 6}, Quaternionf::UnitRandom());
@@ -85,7 +118,6 @@
     ModeSelector selector(options);
 
     selector.setScreenToStagePose(screenToStage);
-
     selector.setDesiredMode(HeadTrackingMode::WORLD_RELATIVE);
     selector.setWorldToHeadPose(0, worldToHead);
     selector.calculate(101);
@@ -101,7 +133,6 @@
     ModeSelector selector(options);
 
     selector.setScreenToStagePose(screenToStage);
-
     selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
     selector.setScreenToHeadPose(0, screenToHead);
     selector.calculate(0);
@@ -118,10 +149,10 @@
     ModeSelector selector(options);
 
     selector.setScreenToStagePose(screenToStage);
-
     selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
     selector.setScreenToHeadPose(0, screenToHead);
     selector.setWorldToHeadPose(50, worldToHead);
+    selector.setScreenStable(50, true);
     selector.calculate(101);
     EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
     EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
@@ -139,6 +170,7 @@
     selector.setDesiredMode(HeadTrackingMode::SCREEN_RELATIVE);
     selector.setScreenToHeadPose(50, std::nullopt);
     selector.setWorldToHeadPose(50, worldToHead);
+    selector.setScreenStable(50, true);
     selector.calculate(101);
     EXPECT_EQ(HeadTrackingMode::WORLD_RELATIVE, selector.getActualMode());
     EXPECT_EQ(selector.getHeadToStagePose(), worldToHead.inverse() * screenToStage);
diff --git a/media/libheadtracking/ModeSelector.cpp b/media/libheadtracking/ModeSelector.cpp
index 16e1712..cb3a27f 100644
--- a/media/libheadtracking/ModeSelector.cpp
+++ b/media/libheadtracking/ModeSelector.cpp
@@ -41,11 +41,18 @@
     mWorldToHeadTimestamp = timestamp;
 }
 
+void ModeSelector::setScreenStable(int64_t timestamp, bool stable) {
+    mScreenStable = stable;
+    mScreenStableTimestamp = timestamp;
+}
+
 void ModeSelector::calculateActualMode(int64_t timestamp) {
     bool isValidScreenToHead = mScreenToHead.has_value() &&
                                timestamp - mScreenToHeadTimestamp < mOptions.freshnessTimeout;
     bool isValidWorldToHead = mWorldToHead.has_value() &&
                               timestamp - mWorldToHeadTimestamp < mOptions.freshnessTimeout;
+    bool isValidScreenStable = mScreenStable.has_value() &&
+                              timestamp - mScreenStableTimestamp < mOptions.freshnessTimeout;
 
     HeadTrackingMode mode = mDesiredMode;
 
@@ -58,7 +65,7 @@
 
     // Optional downgrade from world-relative to static.
     if (mode == HeadTrackingMode::WORLD_RELATIVE) {
-        if (!isValidWorldToHead) {
+        if (!isValidWorldToHead || !isValidScreenStable || !mScreenStable.value()) {
             mode = HeadTrackingMode::STATIC;
         }
     }
diff --git a/media/libheadtracking/ModeSelector.h b/media/libheadtracking/ModeSelector.h
index 17a5142..e537040 100644
--- a/media/libheadtracking/ModeSelector.h
+++ b/media/libheadtracking/ModeSelector.h
@@ -56,6 +56,7 @@
  *   from screen-relative to world-relative.
  * - When we cannot get a fresh estimate of the world-to-head pose, we will fall back from
  *   world-relative to static.
+ * - In world-relative mode, if the screen is unstable, we will fall back to static.
  *
  * All the timestamps used here are of arbitrary units and origin. They just need to be consistent
  * between all the calls and with the Options provided for determining freshness and rate limiting.
@@ -92,6 +93,12 @@
     void setWorldToHeadPose(int64_t timestamp, const Pose3f& worldToHead);
 
     /**
+     * Set whether the screen is considered stable.
+     * The timestamp needs to reflect how fresh the sample is.
+     */
+     void setScreenStable(int64_t timestamp, bool stable);
+
+    /**
      * Process all the previous inputs and update the outputs.
      */
     void calculate(int64_t timestamp);
@@ -116,6 +123,8 @@
     int64_t mScreenToHeadTimestamp;
     std::optional<Pose3f> mWorldToHead;
     int64_t mWorldToHeadTimestamp;
+    std::optional<bool> mScreenStable;
+    int64_t mScreenStableTimestamp;
 
     HeadTrackingMode mActualMode;
     Pose3f mHeadToStage;
diff --git a/media/libheadtracking/StillnessDetector-test.cpp b/media/libheadtracking/StillnessDetector-test.cpp
new file mode 100644
index 0000000..a53ba8c
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector-test.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include "QuaternionUtil.h"
+#include "StillnessDetector.h"
+#include "TestUtil.h"
+
+namespace android {
+namespace media {
+namespace {
+
+using Eigen::Quaternionf;
+using Eigen::Vector3f;
+using Options = StillnessDetector::Options;
+
+TEST(StillnessDetectorTest, Still) {
+    StillnessDetector detector(Options{
+            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f withinThreshold =
+            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(0, baseline);
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(300, withinThreshold);
+    EXPECT_FALSE(detector.calculate(300));
+    detector.setInput(600, baseline);
+    EXPECT_FALSE(detector.calculate(600));
+    detector.setInput(999, withinThreshold);
+    EXPECT_FALSE(detector.calculate(999));
+    detector.setInput(1000, baseline);
+    EXPECT_TRUE(detector.calculate(1000));
+}
+
+TEST(StillnessDetectorTest, ZeroDuration) {
+    StillnessDetector detector(Options{.windowDuration = 0});
+    EXPECT_TRUE(detector.calculate(0));
+    EXPECT_TRUE(detector.calculate(1000));
+}
+
+TEST(StillnessDetectorTest, NotStillTranslation) {
+    StillnessDetector detector(Options{
+            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f withinThreshold =
+            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+    const Pose3f outsideThreshold = baseline * Pose3f(Vector3f(1, 1, 0));
+
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(0, baseline);
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(300, outsideThreshold);
+    EXPECT_FALSE(detector.calculate(300));
+    detector.setInput(600, baseline);
+    EXPECT_FALSE(detector.calculate(600));
+    detector.setInput(900, withinThreshold);
+    EXPECT_FALSE(detector.calculate(900));
+    detector.setInput(1299, baseline);
+    EXPECT_FALSE(detector.calculate(1299));
+    EXPECT_TRUE(detector.calculate(1300));
+}
+
+TEST(StillnessDetectorTest, NotStillRotation) {
+    StillnessDetector detector(Options{
+            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f withinThreshold =
+            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+    const Pose3f outsideThreshold = baseline * Pose3f(rotateZ(0.08));
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(0, baseline);
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(300, outsideThreshold);
+    EXPECT_FALSE(detector.calculate(300));
+    detector.setInput(600, baseline);
+    EXPECT_FALSE(detector.calculate(600));
+    detector.setInput(900, withinThreshold);
+    EXPECT_FALSE(detector.calculate(900));
+    detector.setInput(1299, baseline);
+    EXPECT_FALSE(detector.calculate(1299));
+    EXPECT_TRUE(detector.calculate(1300));
+}
+
+TEST(StillnessDetectorTest, Reset) {
+    StillnessDetector detector(Options{
+            .windowDuration = 1000, .translationalThreshold = 1, .rotationalThreshold = 0.05});
+
+    const Pose3f baseline(Vector3f{1, 2, 3}, Quaternionf::UnitRandom());
+    const Pose3f withinThreshold =
+            baseline * Pose3f(Vector3f(0.3, -0.3, 0), rotateX(0.01) * rotateY(-0.01));
+    EXPECT_FALSE(detector.calculate(0));
+    detector.setInput(0, baseline);
+    EXPECT_FALSE(detector.calculate(0));
+    detector.reset();
+    detector.setInput(600, baseline);
+    EXPECT_FALSE(detector.calculate(600));
+    detector.setInput(900, withinThreshold);
+    EXPECT_FALSE(detector.calculate(900));
+    detector.setInput(1200, baseline);
+    EXPECT_FALSE(detector.calculate(1200));
+    detector.setInput(1599, withinThreshold);
+    EXPECT_FALSE(detector.calculate(1599));
+    detector.setInput(1600, baseline);
+    EXPECT_TRUE(detector.calculate(1600));
+}
+
+}  // namespace
+}  // namespace media
+}  // namespace android
diff --git a/media/libheadtracking/StillnessDetector.cpp b/media/libheadtracking/StillnessDetector.cpp
new file mode 100644
index 0000000..832351d
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "StillnessDetector.h"
+
+namespace android {
+namespace media {
+
+StillnessDetector::StillnessDetector(const Options& options) : mOptions(options) {}
+
+void StillnessDetector::reset() {
+    mFifo.clear();
+    mWindowFull = false;
+}
+
+void StillnessDetector::setInput(int64_t timestamp, const Pose3f& input) {
+    mFifo.push_back(TimestampedPose{timestamp, input});
+    discardOld(timestamp);
+}
+
+bool StillnessDetector::calculate(int64_t timestamp) {
+    discardOld(timestamp);
+
+    // If the window has not been full, we don't consider ourselves still.
+    if (!mWindowFull) {
+        return false;
+    }
+
+    // An empty FIFO and window full is considered still (this will happen in the unlikely case when
+    // the window duration is shorter than the gap between samples).
+    if (mFifo.empty()) {
+        return true;
+    }
+
+    // Otherwise, check whether all the poses remaining in the queue are in the proximity of the new
+    // one.
+    for (auto iter = mFifo.begin(); iter != mFifo.end() - 1; ++iter) {
+        const auto& event = *iter;
+        if (!areNear(event.pose, mFifo.back().pose)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+void StillnessDetector::discardOld(int64_t timestamp) {
+    // Handle the special case of the window duration being zero (always considered full).
+    if (mOptions.windowDuration == 0) {
+        mFifo.clear();
+        mWindowFull = true;
+    }
+
+    // Remove any events from the queue that are older than the window. If there were any such
+    // events we consider the window full.
+    const int64_t windowStart = timestamp - mOptions.windowDuration;
+    while (!mFifo.empty() && mFifo.front().timestamp <= windowStart) {
+        mWindowFull = true;
+        mFifo.pop_front();
+    }
+}
+
+bool StillnessDetector::areNear(const Pose3f& pose1, const Pose3f& pose2) const {
+    // Check translation. We use the L1 norm to reduce computational load on expense of accuracy.
+    // The L1 norm is an upper bound for the actual (L2) norm, so this approach will err on the side
+    // of "not near".
+    if ((pose1.translation() - pose2.translation()).lpNorm<1>() >=
+        mOptions.translationalThreshold) {
+        return false;
+    }
+
+    // Check orientation. We use the L1 norm of the imaginary components of the quaternion to reduce
+    // computational load on expense of accuracy. For small angles, those components are approx.
+    // equal to the angle of rotation and so the norm is approx. the total angle of rotation. The
+    // L1 norm is an upper bound, so this approach will err on the side of "not near".
+    if ((pose1.rotation().vec() - pose2.rotation().vec()).lpNorm<1>() >=
+        mOptions.rotationalThreshold) {
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace media
+}  // namespace android
diff --git a/media/libheadtracking/StillnessDetector.h b/media/libheadtracking/StillnessDetector.h
new file mode 100644
index 0000000..fd26aa9
--- /dev/null
+++ b/media/libheadtracking/StillnessDetector.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 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.
+ */
+#pragma once
+
+#include <deque>
+
+#include <media/Pose.h>
+
+namespace android {
+namespace media {
+
+/**
+ * Given a stream of poses, determines if the pose is stable ("still").
+ * Stillness is defined as all poses in the recent history ("window") being near the most recent
+ * sample.
+ *
+ * Typical usage:
+ *
+ * StillnessDetector detector(StilnessDetector::Options{...});
+ *
+ * while (...) {
+ *    detector.setInput(timestamp, pose);
+ *    bool still = detector.calculate(timestamp);
+ * }
+ *
+ * The stream is considered not still until a sufficient number of samples has been provided for an
+ * initial fill-up of the window. In the special case of the window size being 0, this is not
+ * required and the state is considered always "still". The reset() method can be used to empty the
+ * window again and get back to this initial state.
+ */
+class StillnessDetector {
+  public:
+    /**
+     * Configuration options for the detector.
+     */
+    struct Options {
+        /**
+         * How long is the window, in ticks. The special value of 0 indicates that the stream is
+         * always considered still.
+         */
+        int64_t windowDuration;
+        /**
+         * How much of a translational deviation from the target (in meters) is considered motion.
+         * This is an approximate quantity - the actual threshold might be a little different as we
+         * trade-off accuracy with computational efficiency.
+         */
+        float translationalThreshold;
+        /**
+         * How much of a rotational deviation from the target (in radians) is considered motion.
+         * This is an approximate quantity - the actual threshold might be a little different as we
+         * trade-off accuracy with computational efficiency.
+         */
+        float rotationalThreshold;
+    };
+
+    /** Ctor. */
+    explicit StillnessDetector(const Options& options);
+
+    /** Clear the window. */
+    void reset();
+    /** Push a new sample. */
+    void setInput(int64_t timestamp, const Pose3f& input);
+    /** Calculate whether the stream is still at the given timestamp. */
+    bool calculate(int64_t timestamp);
+
+  private:
+    struct TimestampedPose {
+        int64_t timestamp;
+        Pose3f pose;
+    };
+
+    const Options mOptions;
+    std::deque<TimestampedPose> mFifo;
+    bool mWindowFull = false;
+
+    bool areNear(const Pose3f& pose1, const Pose3f& pose2) const;
+    void discardOld(int64_t timestamp);
+};
+
+}  // namespace media
+}  // namespace android
diff --git a/media/libheadtracking/include/media/HeadTrackingProcessor.h b/media/libheadtracking/include/media/HeadTrackingProcessor.h
index 9fea273..2af560e 100644
--- a/media/libheadtracking/include/media/HeadTrackingProcessor.h
+++ b/media/libheadtracking/include/media/HeadTrackingProcessor.h
@@ -42,6 +42,12 @@
         float rotationalDriftTimeConstant = std::numeric_limits<float>::infinity();
         int64_t freshnessTimeout = std::numeric_limits<int64_t>::max();
         float predictionDuration = 0;
+        int64_t autoRecenterWindowDuration = std::numeric_limits<int64_t>::max();
+        float autoRecenterTranslationalThreshold = std::numeric_limits<float>::infinity();
+        float autoRecenterRotationalThreshold = std::numeric_limits<float>::infinity();
+        int64_t screenStillnessWindowDuration = 0;
+        float screenStillnessTranslationalThreshold = std::numeric_limits<float>::infinity();
+        float screenStillnessRotationalThreshold = std::numeric_limits<float>::infinity();
     };
 
     /** Sets the desired head-tracking mode. */
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index ca7ffdb..b748f9d 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -2413,6 +2413,7 @@
                 if (i == size - 1 && i != 0) {
                     mEffects[i - 1]->configure();
                     mEffects[i - 1]->setOutBuffer(mOutBuffer);
+                    mEffects[i - 1]->updateAccessMode();      // reconfig if neeeded.
                 }
             }
             mEffects.removeAt(i);
@@ -2422,6 +2423,7 @@
             if (i == 0 && size > 1) {
                 mEffects[0]->configure();
                 mEffects[0]->setInBuffer(mInBuffer);
+                mEffects[0]->updateAccessMode();      // reconfig if neeeded.
             }
 
             ALOGV("removeEffect_l() effect %p, removed from chain %p at rank %zu", effect.get(),
diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp
index 93118b8..45dd258 100644
--- a/services/audioflinger/PatchPanel.cpp
+++ b/services/audioflinger/PatchPanel.cpp
@@ -575,6 +575,12 @@
 
     // create a special playback track to render to playback thread.
     // this track is given the same buffer as the PatchRecord buffer
+
+    // Default behaviour is to start as soon as possible to have the lowest possible latency even if
+    // it might glitch.
+    // Disable this behavior for FM Tuner source if no fast capture/mixer available.
+    const bool isFmBridge = mAudioPatch.sources[0].ext.device.type == AUDIO_DEVICE_IN_FM_TUNER;
+    const size_t frameCountToBeReady = isFmBridge && !usePassthruPatchRecord ? frameCount / 4 : 1;
     sp<PlaybackThread::PatchTrack> tempPatchTrack = new PlaybackThread::PatchTrack(
                                            mPlayback.thread().get(),
                                            streamType,
@@ -584,7 +590,9 @@
                                            frameCount,
                                            tempRecordTrack->buffer(),
                                            tempRecordTrack->bufferSize(),
-                                           outputFlags);
+                                           outputFlags,
+                                           {} /*timeout*/,
+                                           frameCountToBeReady);
     status = mPlayback.checkTrack(tempPatchTrack.get());
     if (status != NO_ERROR) {
         return status;
diff --git a/services/audiopolicy/AudioPolicyInterface.h b/services/audiopolicy/AudioPolicyInterface.h
index 33b455f..f6f3b9a 100644
--- a/services/audiopolicy/AudioPolicyInterface.h
+++ b/services/audiopolicy/AudioPolicyInterface.h
@@ -392,6 +392,18 @@
      * @return NO_ERROR if an output was closed, INVALID_OPERATION or BAD_VALUE otherwise
      */
     virtual status_t releaseSpatializerOutput(audio_io_handle_t output) = 0;
+
+    /**
+     * Query how the direct playback is currently supported on the device.
+     * @param attr audio attributes describing the playback use case
+     * @param config audio configuration for the playback
+     * @param directMode out: a set of flags describing how the direct playback is currently
+     *        supported on the device
+     * @return NO_ERROR in case of success, DEAD_OBJECT, NO_INIT, BAD_VALUE, PERMISSION_DENIED
+     *         in case of error.
+     */
+    virtual audio_direct_mode_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+                                                         const audio_config_t *config) = 0;
 };
 
 
diff --git a/services/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index c565926..2ebb7df 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -70,7 +70,7 @@
 
 using ProductStrategies = std::vector<ProductStrategy>;
 
-using ValuePair = std::pair<uint32_t, std::string>;
+using ValuePair = std::tuple<uint64_t, uint32_t, std::string>;
 using ValuePairs = std::vector<ValuePair>;
 
 struct CriterionType
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 81e803f..6f560d5 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -80,6 +80,7 @@
     struct Attributes {
         static constexpr const char *literal = "literal";
         static constexpr const char *numerical = "numerical";
+        static constexpr const char *androidType = "android_type";
     };
 
     static android::status_t deserialize(_xmlDoc *doc, const _xmlNode *root,
@@ -349,7 +350,16 @@
         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
         return BAD_VALUE;
     }
-    uint32_t numerical = 0;
+    uint32_t androidType = 0;
+    std::string androidTypeliteral = getXmlAttribute(child, Attributes::androidType);
+    if (!androidTypeliteral.empty()) {
+        ALOGV("%s: androidType %s", __FUNCTION__, androidTypeliteral.c_str());
+        if (!convertTo(androidTypeliteral, androidType)) {
+            ALOGE("%s: : Invalid typeset value(%s)", __FUNCTION__, androidTypeliteral.c_str());
+            return BAD_VALUE;
+        }
+    }
+    uint64_t numerical = 0;
     std::string numericalTag = getXmlAttribute(child, Attributes::numerical);
     if (numericalTag.empty()) {
         ALOGE("%s: No attribute %s found", __FUNCTION__, Attributes::literal);
@@ -359,7 +369,7 @@
         ALOGE("%s: : Invalid value(%s)", __FUNCTION__, numericalTag.c_str());
         return BAD_VALUE;
     }
-    values.push_back({numerical, literal});
+    values.push_back({numerical,  androidType, literal});
     return NO_ERROR;
 }
 
diff --git a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
index 1fc2264..9fd8b8e 100644
--- a/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
+++ b/services/audiopolicy/engineconfigurable/interface/AudioPolicyPluginInterface.h
@@ -77,12 +77,12 @@
      * Set the input device to be used by an input source.
      *
      * @param[in] inputSource: name of the input source for which the device to use has to be set
-     * @param[in] devices; mask of devices to be used for the given input source.
+     * @param[in] devices: mask of devices to be used for the given input source.
      *
      * @return true if the devices were set correclty for this input source, false otherwise.
      */
     virtual bool setDeviceForInputSource(const audio_source_t &inputSource,
-                                         audio_devices_t device) = 0;
+                                         uint64_t device) = 0;
 
     virtual void setDeviceAddressForProductStrategy(product_strategy_t strategy,
                                                     const std::string &address) = 0;
@@ -91,12 +91,12 @@
      * Set the device to be used by a product strategy.
      *
      * @param[in] strategy: name of the product strategy for which the device to use has to be set
-     * @param[in] devices; mask of devices to be used for the given strategy.
+     * @param[in] devices: mask of devices to be used for the given strategy.
      *
      * @return true if the devices were set correclty for this strategy, false otherwise.
      */
     virtual bool setDeviceTypesForProductStrategy(product_strategy_t strategy,
-                                                  audio_devices_t devices) = 0;
+                                                  uint64_t devices) = 0;
 
     virtual product_strategy_t getProductStrategyByName(const std::string &address) = 0;
 
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
index 2e9f37e..2c4c7b5 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/examples/common/Structure/PolicySubsystem-CommonTypes.xml.in
@@ -10,8 +10,8 @@
      <!--#################### GLOBAL COMPONENTS END ####################-->
 
     <!-- Automatically filled from audio-base.h file -->
-    <ComponentType Name="OutputDevicesMask" Description="32th bit is not allowed as dedicated for input devices detection">
-        <BitParameterBlock Name="mask" Size="32">
+    <ComponentType Name="OutputDevicesMask" Description="64bit representation of devices">
+        <BitParameterBlock Name="mask" Size="64">
         </BitParameterBlock>
     </ComponentType>
 
@@ -19,8 +19,8 @@
     profile. It must match with the Input device enum parameter.
     -->
     <!-- Automatically filled from audio-base.h file -->
-    <ComponentType Name="InputDevicesMask">
-        <BitParameterBlock Name="mask" Size="32">
+    <ComponentType Name="InputDevicesMask" Description="64bit representation of devices">
+        <BitParameterBlock Name="mask" Size="64">
         </BitParameterBlock>
     </ComponentType>
 
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
index f8a6fc0..df4e3e9 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/InputSource.cpp
@@ -45,7 +45,7 @@
 
 bool InputSource::sendToHW(string & /*error*/)
 {
-    audio_devices_t applicableInputDevice;
+    uint64_t applicableInputDevice;
     blackboardRead(&applicableInputDevice, sizeof(applicableInputDevice));
     return mPolicyPluginInterface->setDeviceForInputSource(mId, applicableInputDevice);
 }
diff --git a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
index 6c8eb65..e65946e 100644
--- a/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
+++ b/services/audiopolicy/engineconfigurable/parameter-framework/plugin/ProductStrategy.h
@@ -32,7 +32,7 @@
 
     struct Device
     {
-        audio_devices_t applicableDevice; /**< applicable device for this strategy. */
+        uint64_t applicableDevice; /**< applicable device for this strategy. */
         char deviceAddress[mMaxStringSize]; /**< device address associated with this strategy. */
     } __attribute__((packed));
 
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.cpp b/services/audiopolicy/engineconfigurable/src/Engine.cpp
index 9a61a05..3d74920 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.cpp
+++ b/services/audiopolicy/engineconfigurable/src/Engine.cpp
@@ -36,6 +36,8 @@
 
 #include <media/TypeConverter.h>
 
+#include <cinttypes>
+
 using std::string;
 using std::map;
 
@@ -166,16 +168,13 @@
 status_t Engine::setDeviceConnectionState(const sp<DeviceDescriptor> device,
                                           audio_policy_dev_state_t state)
 {
-    mPolicyParameterMgr->setDeviceConnectionState(
-                device->type(), device->address().c_str(), state);
+    mPolicyParameterMgr->setDeviceConnectionState(device->type(), device->address(), state);
     if (audio_is_output_device(device->type())) {
-        // FIXME: Use DeviceTypeSet when the interface is ready
         return mPolicyParameterMgr->setAvailableOutputDevices(
-                    deviceTypesToBitMask(getApmObserver()->getAvailableOutputDevices().types()));
+                    getApmObserver()->getAvailableOutputDevices().types());
     } else if (audio_is_input_device(device->type())) {
-        // FIXME: Use DeviceTypeSet when the interface is ready
         return mPolicyParameterMgr->setAvailableInputDevices(
-                    deviceTypesToBitMask(getApmObserver()->getAvailableInputDevices().types()));
+                    getApmObserver()->getAvailableInputDevices().types());
     }
     return EngineBase::setDeviceConnectionState(device, state);
 }
@@ -374,17 +373,28 @@
     getProductStrategies().at(strategy)->setDeviceAddress(address);
 }
 
-bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, audio_devices_t devices)
+bool Engine::setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices)
 {
     if (getProductStrategies().find(strategy) == getProductStrategies().end()) {
-        ALOGE("%s: set device %d on invalid strategy %d", __FUNCTION__, devices, strategy);
+        ALOGE("%s: set device %" PRId64 " on invalid strategy %d", __FUNCTION__, devices, strategy);
         return false;
     }
-    // FIXME: stop using deviceTypesFromBitMask when the interface is ready
-    getProductStrategies().at(strategy)->setDeviceTypes(deviceTypesFromBitMask(devices));
+    // Here device matches the criterion value, need to rebuitd android device types;
+    DeviceTypeSet types =
+            mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(devices, true /*isOut*/);
+    getProductStrategies().at(strategy)->setDeviceTypes(types);
     return true;
 }
 
+bool Engine::setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device)
+{
+    DeviceTypeSet types = mPolicyParameterMgr->convertDeviceCriterionValueToDeviceTypes(
+                device, false /*isOut*/);
+    ALOG_ASSERT(types.size() <= 1, "one input device expected at most");
+    audio_devices_t deviceType = types.empty() ? AUDIO_DEVICE_IN_DEFAULT : *types.begin();
+    return setPropertyForKey<audio_devices_t, audio_source_t>(deviceType, inputSource);
+}
+
 template <>
 EngineInterface *Engine::queryInterface()
 {
diff --git a/services/audiopolicy/engineconfigurable/src/Engine.h b/services/audiopolicy/engineconfigurable/src/Engine.h
index f665da5..4b559f0 100644
--- a/services/audiopolicy/engineconfigurable/src/Engine.h
+++ b/services/audiopolicy/engineconfigurable/src/Engine.h
@@ -82,15 +82,12 @@
     bool setVolumeProfileForStream(const audio_stream_type_t &stream,
                                    const audio_stream_type_t &volumeProfile) override;
 
-    bool setDeviceForInputSource(const audio_source_t &inputSource, audio_devices_t device) override
-    {
-        return setPropertyForKey<audio_devices_t, audio_source_t>(device, inputSource);
-    }
+    bool setDeviceForInputSource(const audio_source_t &inputSource, uint64_t device) override;
+
     void setDeviceAddressForProductStrategy(product_strategy_t strategy,
                                                     const std::string &address) override;
 
-    bool setDeviceTypesForProductStrategy(product_strategy_t strategy,
-                                                  audio_devices_t devices) override;
+    bool setDeviceTypesForProductStrategy(product_strategy_t strategy, uint64_t devices) override;
 
     product_strategy_t getProductStrategyByName(const std::string &name) override
     {
diff --git a/services/audiopolicy/engineconfigurable/src/InputSource.cpp b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
index f4645e6..5779c00 100644
--- a/services/audiopolicy/engineconfigurable/src/InputSource.cpp
+++ b/services/audiopolicy/engineconfigurable/src/InputSource.cpp
@@ -46,12 +46,6 @@
 template <>
 status_t Element<audio_source_t>::set(audio_devices_t devices)
 {
-    if (devices == AUDIO_DEVICE_NONE) {
-        // Reset
-        mApplicableDevices = devices;
-        return NO_ERROR;
-    }
-    devices = static_cast<audio_devices_t>(devices | AUDIO_DEVICE_BIT_IN);
     if (!audio_is_input_device(devices)) {
         ALOGE("%s: trying to set an invalid device 0x%X for input source %s",
               __FUNCTION__, devices, getName().c_str());
diff --git a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
index 43b3dd2..86ac76f 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildCommonTypesStructureFile.py
@@ -55,7 +55,7 @@
     while i < decimal:
         i = i << 1
         pos = pos + 1
-        if pos == 32:
+        if pos == 64:
             return -1
 
     # TODO: b/168065706. This is just to fix the build. That the problem of devices with
@@ -132,6 +132,9 @@
 
     logging.info("Checking Android Header file {}".format(androidaudiobaseheaderFile))
 
+    multi_bit_output_device_shift = 32
+    multi_bit_input_device_shift = 32
+
     for line_number, line in enumerate(androidaudiobaseheaderFile):
         match = criteria_pattern.match(line)
         if match:
@@ -143,16 +146,36 @@
 
             component_type_numerical_value = match.groupdict()['values']
 
-            # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
+            # for AUDIO_DEVICE_IN: rename default to stub
             if component_type_name == "InputDevicesMask":
-                component_type_numerical_value = str(int(component_type_numerical_value, 0) & ~2147483648)
+                component_type_numerical_value = str(int(component_type_numerical_value, 0))
                 if component_type_literal == "default":
                     component_type_literal = "stub"
 
+                string_int = int(component_type_numerical_value, 0)
+                num_bits = bin(string_int).count("1")
+                if num_bits > 1:
+                    logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
+                        .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
+                    string_int = 2**multi_bit_input_device_shift
+                    logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+                    multi_bit_input_device_shift += 1
+                    component_type_numerical_value = str(string_int)
+
             if component_type_name == "OutputDevicesMask":
                 if component_type_literal == "default":
                     component_type_literal = "stub"
 
+                string_int = int(component_type_numerical_value, 0)
+                num_bits = bin(string_int).count("1")
+                if num_bits > 1:
+                    logging.info("The value {} is for criterion {} binary rep {} has {} bits sets"
+                        .format(component_type_numerical_value, component_type_name, bin(string_int), num_bits))
+                    string_int = 2**multi_bit_output_device_shift
+                    logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+                    multi_bit_output_device_shift += 1
+                    component_type_numerical_value = str(string_int)
+
             # Remove duplicated numerical values
             if int(component_type_numerical_value, 0) in all_component_types[component_type_name].values():
                 logging.info("The value {}:{} is duplicated for criterion {}, KEEPING LATEST".format(component_type_numerical_value, component_type_literal, component_type_name))
diff --git a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
index 76c35c1..a15a6ba 100755
--- a/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
+++ b/services/audiopolicy/engineconfigurable/tools/buildPolicyCriterionTypes.py
@@ -85,6 +85,9 @@
     return argparser.parse_args()
 
 
+output_devices_type_value = {}
+input_devices_type_value = {}
+
 def generateXmlCriterionTypesFile(criterionTypes, addressCriteria, criterionTypesFile, outputFile):
 
     logging.info("Importing criterionTypesFile {}".format(criterionTypesFile))
@@ -102,6 +105,11 @@
                     value_node.set('numerical', str(value))
                     value_node.set('literal', key)
 
+                    if criterion_type.get('name') == "OutputDevicesMaskType":
+                        value_node.set('android_type', output_devices_type_value[key])
+                    if criterion_type.get('name') == "InputDevicesMaskType":
+                        value_node.set('android_type', input_devices_type_value[key])
+
     if addressCriteria:
         for criterion_name, values_list in addressCriteria.items():
             for criterion_type in criterion_types_root.findall('criterion_type'):
@@ -200,10 +208,8 @@
     #
     ignored_values = ['CNT', 'MAX', 'ALL', 'NONE']
 
-    #
-    # Reaching 32 bit limit for inclusive criterion out devices: removing
-    #
-    ignored_output_device_values = ['BleSpeaker', 'BleHeadset']
+    multi_bit_outputdevice_shift = 32
+    multi_bit_inputdevice_shift = 32
 
     criteria_pattern = re.compile(
         r"\s*V\((?P<type>(?:"+'|'.join(criterion_mapping_table.keys()) + "))_" \
@@ -223,28 +229,59 @@
                 ''.join((w.capitalize() for w in match.groupdict()['literal'].split('_')))
             criterion_numerical_value = match.groupdict()['values']
 
-            # for AUDIO_DEVICE_IN: need to remove sign bit / rename default to stub
+            # for AUDIO_DEVICE_IN: rename default to stub
             if criterion_name == "InputDevicesMaskType":
                 if criterion_literal == "Default":
                     criterion_numerical_value = str(int("0x40000000", 0))
+                    input_devices_type_value[criterion_literal] = "0xC0000000"
                 else:
                     try:
                         string_int = int(criterion_numerical_value, 0)
+                        # Append AUDIO_DEVICE_IN for android type tag
+                        input_devices_type_value[criterion_literal] = hex(string_int | 2147483648)
+
+                        num_bits = bin(string_int).count("1")
+                        if num_bits > 1:
+                            logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+                                .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+                            string_int = 2**multi_bit_inputdevice_shift
+                            logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+                            multi_bit_inputdevice_shift += 1
+                            criterion_numerical_value = str(string_int)
+
                     except ValueError:
                         # Handle the exception
                         logging.info("value {}:{} for criterion {} is not a number, ignoring"
                             .format(criterion_numerical_value, criterion_literal, criterion_name))
                         continue
-                    criterion_numerical_value = str(int(criterion_numerical_value, 0) & ~2147483648)
 
             if criterion_name == "OutputDevicesMaskType":
                 if criterion_literal == "Default":
                     criterion_numerical_value = str(int("0x40000000", 0))
-                if criterion_literal in ignored_output_device_values:
-                    logging.info("OutputDevicesMaskType skipping {}".format(criterion_literal))
-                    continue
+                    output_devices_type_value[criterion_literal] = "0x40000000"
+                else:
+                    try:
+                        string_int = int(criterion_numerical_value, 0)
+                        output_devices_type_value[criterion_literal] = criterion_numerical_value
+
+                        num_bits = bin(string_int).count("1")
+                        if num_bits > 1:
+                            logging.info("The value {}:{} is for criterion {} binary rep {} has {} bits sets"
+                                .format(criterion_numerical_value, criterion_literal, criterion_name, bin(string_int), num_bits))
+                            string_int = 2**multi_bit_outputdevice_shift
+                            logging.info("new val assigned is {} {}" .format(string_int, bin(string_int)))
+                            multi_bit_outputdevice_shift += 1
+                            criterion_numerical_value = str(string_int)
+
+                    except ValueError:
+                        # Handle the exception
+                        logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
+                            .format(criterion_numerical_value, criterion_literal, criterion_name))
+                        continue
+
             try:
                 string_int = int(criterion_numerical_value, 0)
+
             except ValueError:
                 # Handle the exception
                 logging.info("The value {}:{} is for criterion {} is not a number, ignoring"
diff --git a/services/audiopolicy/engineconfigurable/wrapper/Android.bp b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
index 3e04b68..0ef0b82 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/Android.bp
+++ b/services/audiopolicy/engineconfigurable/wrapper/Android.bp
@@ -19,6 +19,7 @@
     header_libs: [
         "libbase_headers",
         "libaudiopolicycommon",
+        "libaudiofoundation_headers",
     ],
     shared_libs: [
         "liblog",
diff --git a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
index 63990ac..099d55d 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
+++ b/services/audiopolicy/engineconfigurable/wrapper/ParameterManagerWrapper.cpp
@@ -23,6 +23,7 @@
 #include <SelectionCriterionInterface.h>
 #include <media/convert.h>
 #include <algorithm>
+#include <cutils/bitops.h>
 #include <cutils/config_utils.h>
 #include <cutils/misc.h>
 #include <fstream>
@@ -31,6 +32,7 @@
 #include <string>
 #include <vector>
 #include <stdint.h>
+#include <cinttypes>
 #include <cmath>
 #include <utils/Log.h>
 
@@ -124,9 +126,22 @@
 
     for (auto pair : pairs) {
         std::string error;
-        ALOGV("%s: Adding pair %d,%s for criterionType %s", __FUNCTION__, pair.first,
-              pair.second.c_str(), name.c_str());
-        criterionType->addValuePair(pair.first, pair.second, error);
+        ALOGV("%s: Adding pair %" PRIu64", %s for criterionType %s", __func__, std::get<0>(pair),
+              std::get<2>(pair).c_str(), name.c_str());
+        criterionType->addValuePair(std::get<0>(pair), std::get<2>(pair), error);
+
+        if (name == gOutputDeviceCriterionName) {
+            ALOGV("%s: Adding mOutputDeviceToCriterionTypeMap %d %" PRIu64" for criterionType %s",
+                  __func__, std::get<1>(pair), std::get<0>(pair), name.c_str());
+            audio_devices_t androidType = static_cast<audio_devices_t>(std::get<1>(pair));
+            mOutputDeviceToCriterionTypeMap[androidType] = std::get<0>(pair);
+        }
+        if (name == gInputDeviceCriterionName) {
+            ALOGV("%s: Adding mInputDeviceToCriterionTypeMap %d %" PRIu64" for criterionType %s",
+                  __func__, std::get<1>(pair), std::get<0>(pair), name.c_str());
+            audio_devices_t androidType = static_cast<audio_devices_t>(std::get<1>(pair));
+            mInputDeviceToCriterionTypeMap[androidType] = std::get<0>(pair);
+        }
     }
     ALOG_ASSERT(mPolicyCriteria.find(name) == mPolicyCriteria.end(),
                 "%s: Criterion %s already added", __FUNCTION__, name.c_str());
@@ -135,7 +150,7 @@
     mPolicyCriteria[name] = criterion;
 
     if (not defaultValue.empty()) {
-        int numericalValue = 0;
+        uint64_t numericalValue = 0;
         if (not criterionType->getNumericalValue(defaultValue.c_str(), numericalValue)) {
             ALOGE("%s; trying to apply invalid default literal value (%s)", __FUNCTION__,
                   defaultValue.c_str());
@@ -263,7 +278,7 @@
 }
 
 status_t ParameterManagerWrapper::setDeviceConnectionState(
-        audio_devices_t type, const std::string address, audio_policy_dev_state_t state)
+        audio_devices_t type, const std::string &address, audio_policy_dev_state_t state)
 {
     std::string criterionName = audio_is_output_device(type) ?
                 gOutputDeviceAddressCriterionName : gInputDeviceAddressCriterionName;
@@ -279,7 +294,7 @@
     }
 
     auto criterionType = criterion->getCriterionType();
-    int deviceAddressId;
+    uint64_t deviceAddressId;
     if (not criterionType->getNumericalValue(address.c_str(), deviceAddressId)) {
         ALOGW("%s: unknown device address reported (%s) for criterion %s", __FUNCTION__,
               address.c_str(), criterionName.c_str());
@@ -296,28 +311,28 @@
     return NO_ERROR;
 }
 
-status_t ParameterManagerWrapper::setAvailableInputDevices(audio_devices_t inputDevices)
+status_t ParameterManagerWrapper::setAvailableInputDevices(const DeviceTypeSet &types)
 {
     ISelectionCriterionInterface *criterion =
             getElement<ISelectionCriterionInterface>(gInputDeviceCriterionName, mPolicyCriteria);
     if (criterion == NULL) {
-        ALOGE("%s: no criterion found for %s", __FUNCTION__, gInputDeviceCriterionName);
+        ALOGE("%s: no criterion found for %s", __func__, gInputDeviceCriterionName);
         return DEAD_OBJECT;
     }
-    criterion->setCriterionState(inputDevices & ~AUDIO_DEVICE_BIT_IN);
+    criterion->setCriterionState(convertDeviceTypesToCriterionValue(types));
     applyPlatformConfiguration();
     return NO_ERROR;
 }
 
-status_t ParameterManagerWrapper::setAvailableOutputDevices(audio_devices_t outputDevices)
+status_t ParameterManagerWrapper::setAvailableOutputDevices(const DeviceTypeSet &types)
 {
     ISelectionCriterionInterface *criterion =
             getElement<ISelectionCriterionInterface>(gOutputDeviceCriterionName, mPolicyCriteria);
     if (criterion == NULL) {
-        ALOGE("%s: no criterion found for %s", __FUNCTION__, gOutputDeviceCriterionName);
+        ALOGE("%s: no criterion found for %s", __func__, gOutputDeviceCriterionName);
         return DEAD_OBJECT;
     }
-    criterion->setCriterionState(outputDevices);
+    criterion->setCriterionState(convertDeviceTypesToCriterionValue(types));
     applyPlatformConfiguration();
     return NO_ERROR;
 }
@@ -327,5 +342,45 @@
     mPfwConnector->applyConfigurations();
 }
 
+uint64_t ParameterManagerWrapper::convertDeviceTypeToCriterionValue(audio_devices_t type) const {
+    bool isOut = audio_is_output_devices(type);
+    uint32_t typeMask = isOut ? type : (type & ~AUDIO_DEVICE_BIT_IN);
+
+    const auto &adapters = isOut ? mOutputDeviceToCriterionTypeMap : mInputDeviceToCriterionTypeMap;
+    // Only multibit devices need adaptation.
+    if (popcount(typeMask) > 1) {
+        const auto &adapter = adapters.find(type);
+        if (adapter != adapters.end()) {
+            ALOGV("%s: multibit device %d converted to criterion %" PRIu64, __func__, type,
+                  adapter->second);
+            return adapter->second;
+        }
+        ALOGE("%s: failed to find map for multibit device %d", __func__, type);
+        return 0;
+    }
+    return typeMask;
+}
+
+uint64_t ParameterManagerWrapper::convertDeviceTypesToCriterionValue(
+        const DeviceTypeSet &types) const {
+    uint64_t criterionValue = 0;
+    for (const auto &type : types) {
+        criterionValue += convertDeviceTypeToCriterionValue(type);
+    }
+    return criterionValue;
+}
+
+DeviceTypeSet ParameterManagerWrapper::convertDeviceCriterionValueToDeviceTypes(
+        uint64_t criterionValue, bool isOut) const {
+    DeviceTypeSet deviceTypes;
+    const auto &adapters = isOut ? mOutputDeviceToCriterionTypeMap : mInputDeviceToCriterionTypeMap;
+    for (const auto &adapter : adapters) {
+        if ((adapter.second & criterionValue) == adapter.second) {
+            deviceTypes.insert(adapter.first);
+        }
+    }
+    return deviceTypes;
+}
+
 } // namespace audio_policy
 } // namespace android
diff --git a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
index 62b129a..fa4ae1e 100644
--- a/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
+++ b/services/audiopolicy/engineconfigurable/wrapper/include/ParameterManagerWrapper.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <media/AudioContainers.h>
 #include <system/audio.h>
 #include <system/audio_policy.h>
 #include <utils/Errors.h>
@@ -35,7 +36,8 @@
 namespace android {
 namespace audio_policy {
 
-using ValuePair = std::pair<uint32_t, std::string>;
+using ValuePair = std::tuple<uint64_t, uint32_t, std::string>;
+using DeviceToCriterionTypeAdapter = std::map<audio_devices_t, uint64_t>;
 using ValuePairs = std::vector<ValuePair>;
 
 class ParameterManagerWrapper
@@ -105,7 +107,7 @@
      *
      * @return NO_ERROR if devices criterion updated correctly, error code otherwise.
      */
-    status_t setAvailableInputDevices(audio_devices_t inputDevices);
+    status_t setAvailableInputDevices(const DeviceTypeSet &inputDeviceTypes);
 
     /**
      * Set the available output devices i.e. set the associated policy parameter framework criterion
@@ -114,7 +116,7 @@
      *
      * @return NO_ERROR if devices criterion updated correctly, error code otherwise.
      */
-    status_t setAvailableOutputDevices(audio_devices_t outputDevices);
+    status_t setAvailableOutputDevices(const DeviceTypeSet &outputDeviceTypes);
 
     /**
      * @brief setDeviceConnectionState propagates a state event on a given device(s)
@@ -124,7 +126,7 @@
      * @return NO_ERROR if new state corretly propagated to Engine Parameter-Framework, error
      * code otherwise.
      */
-    status_t setDeviceConnectionState(audio_devices_t type, const std::string address,
+    status_t setDeviceConnectionState(audio_devices_t type, const std::string &address,
                                       audio_policy_dev_state_t state);
 
     /**
@@ -138,6 +140,13 @@
     status_t addCriterion(const std::string &name, bool isInclusive, ValuePairs pairs,
                           const std::string &defaultValue);
 
+    uint64_t convertDeviceTypeToCriterionValue(audio_devices_t type) const;
+
+    uint64_t convertDeviceTypesToCriterionValue(const DeviceTypeSet &types) const;
+
+    DeviceTypeSet convertDeviceCriterionValueToDeviceTypes(
+            uint64_t criterionValue, bool isOut) const;
+
 private:
     /**
      * Apply the configuration of the platform on the policy parameter manager.
@@ -211,6 +220,9 @@
     template <typename T>
     struct parameterManagerElementSupported;
 
+    DeviceToCriterionTypeAdapter mOutputDeviceToCriterionTypeMap;
+    DeviceToCriterionTypeAdapter mInputDeviceToCriterionTypeMap;
+
     static const char *const mPolicyPfwDefaultConfFileName; /**< Default Policy PFW top file name.*/
     static const char *const mPolicyPfwVendorConfFileName; /**< Vendor Policy PFW top file name.*/
 };
diff --git a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
index 8584702..654e4bf 100644
--- a/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
+++ b/services/audiopolicy/fuzzer/audiopolicy_fuzzer.cpp
@@ -841,6 +841,8 @@
         : AudioPolicyManagerFuzzerWithConfigurationFile(fdp){};
     void process() override;
 
+    void fuzzGetDirectPlaybackSupport();
+
    protected:
     void setDeviceConnectionState();
     void explicitlyRoutingAfterConnection();
@@ -891,10 +893,27 @@
     }
 }
 
+void AudioPolicyManagerFuzzerDeviceConnection::fuzzGetDirectPlaybackSupport() {
+    const uint32_t numTestCases = mFdp->ConsumeIntegralInRange<uint32_t>(1, 10);
+    for (int i = 0; i < numTestCases; ++i) {
+        audio_attributes_t attr = AUDIO_ATTRIBUTES_INITIALIZER;
+        attr.content_type = getValueFromVector<audio_content_type_t>(mFdp, kAudioContentTypes);
+        attr.usage = getValueFromVector<audio_usage_t>(mFdp, kAudioUsages);
+        attr.source = getValueFromVector<audio_source_t>(mFdp, kAudioSources);
+        attr.flags = getValueFromVector<audio_flags_mask_t>(mFdp, kAudioFlagMasks);
+        audio_config_t config = AUDIO_CONFIG_INITIALIZER;
+        config.channel_mask = getValueFromVector<audio_channel_mask_t>(mFdp, kAudioChannelOutMasks);
+        config.format = getValueFromVector<audio_format_t>(mFdp, kAudioFormats);
+        config.sample_rate = getValueFromVector<uint32_t>(mFdp, kSamplingRates);
+        mManager->getDirectPlaybackSupport(&attr, &config);
+    }
+}
+
 void AudioPolicyManagerFuzzerDeviceConnection::process() {
     if (initialize()) {
         setDeviceConnectionState();
         explicitlyRoutingAfterConnection();
+        fuzzGetDirectPlaybackSupport();
         fuzzPatchCreation();
     }
 }
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 05eae98..cc1012c 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3662,53 +3662,7 @@
      offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
      offloadInfo.has_video);
 
-    if (mMasterMono) {
-        return AUDIO_OFFLOAD_NOT_SUPPORTED; // no offloading if mono is set.
-    }
-
-    // Check if offload has been disabled
-    if (property_get_bool("audio.offload.disable", false /* default_value */)) {
-        ALOGV("%s: offload disabled by audio.offload.disable", __func__);
-        return AUDIO_OFFLOAD_NOT_SUPPORTED;
-    }
-
-    // Check if stream type is music, then only allow offload as of now.
-    if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
-    {
-        ALOGV("%s: stream_type != MUSIC, returning false", __func__);
-        return AUDIO_OFFLOAD_NOT_SUPPORTED;
-    }
-
-    //TODO: enable audio offloading with video when ready
-    const bool allowOffloadWithVideo =
-            property_get_bool("audio.offload.video", false /* default_value */);
-    if (offloadInfo.has_video && !allowOffloadWithVideo) {
-        ALOGV("%s: has_video == true, returning false", __func__);
-        return AUDIO_OFFLOAD_NOT_SUPPORTED;
-    }
-
-    //If duration is less than minimum value defined in property, return false
-    const int min_duration_secs = property_get_int32(
-            "audio.offload.min.duration.secs", -1 /* default_value */);
-    if (min_duration_secs >= 0) {
-        if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
-            ALOGV("%s: Offload denied by duration < audio.offload.min.duration.secs(=%d)",
-                    __func__, min_duration_secs);
-            return AUDIO_OFFLOAD_NOT_SUPPORTED;
-        }
-    } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
-        ALOGV("%s: Offload denied by duration < default min(=%u)",
-                __func__, OFFLOAD_DEFAULT_MIN_DURATION_SECS);
-        return AUDIO_OFFLOAD_NOT_SUPPORTED;
-    }
-
-    // Do not allow offloading if one non offloadable effect is enabled. This prevents from
-    // creating an offloaded track and tearing it down immediately after start when audioflinger
-    // detects there is an active non offloadable effect.
-    // FIXME: We should check the audio session here but we do not have it in this context.
-    // This may prevent offloading in rare situations where effects are left active by apps
-    // in the background.
-    if (mEffects.isNonOffloadableEffectEnabled()) {
+    if (!isOffloadPossible(offloadInfo)) {
         return AUDIO_OFFLOAD_NOT_SUPPORTED;
     }
 
@@ -3750,6 +3704,122 @@
     return (profile != 0);
 }
 
+bool AudioPolicyManager::isOffloadPossible(const audio_offload_info_t &offloadInfo,
+                                           bool durationIgnored) {
+    if (mMasterMono) {
+        return false; // no offloading if mono is set.
+    }
+
+    // Check if offload has been disabled
+    if (property_get_bool("audio.offload.disable", false /* default_value */)) {
+        ALOGV("%s: offload disabled by audio.offload.disable", __func__);
+        return false;
+    }
+
+    // Check if stream type is music, then only allow offload as of now.
+    if (offloadInfo.stream_type != AUDIO_STREAM_MUSIC)
+    {
+        ALOGV("%s: stream_type != MUSIC, returning false", __func__);
+        return false;
+    }
+
+    //TODO: enable audio offloading with video when ready
+    const bool allowOffloadWithVideo =
+            property_get_bool("audio.offload.video", false /* default_value */);
+    if (offloadInfo.has_video && !allowOffloadWithVideo) {
+        ALOGV("%s: has_video == true, returning false", __func__);
+        return false;
+    }
+
+    //If duration is less than minimum value defined in property, return false
+    const int min_duration_secs = property_get_int32(
+            "audio.offload.min.duration.secs", -1 /* default_value */);
+    if (!durationIgnored) {
+        if (min_duration_secs >= 0) {
+            if (offloadInfo.duration_us < min_duration_secs * 1000000LL) {
+                ALOGV("%s: Offload denied by duration < audio.offload.min.duration.secs(=%d)",
+                      __func__, min_duration_secs);
+                return false;
+            }
+        } else if (offloadInfo.duration_us < OFFLOAD_DEFAULT_MIN_DURATION_SECS * 1000000) {
+            ALOGV("%s: Offload denied by duration < default min(=%u)",
+                  __func__, OFFLOAD_DEFAULT_MIN_DURATION_SECS);
+            return false;
+        }
+    }
+
+    // Do not allow offloading if one non offloadable effect is enabled. This prevents from
+    // creating an offloaded track and tearing it down immediately after start when audioflinger
+    // detects there is an active non offloadable effect.
+    // FIXME: We should check the audio session here but we do not have it in this context.
+    // This may prevent offloading in rare situations where effects are left active by apps
+    // in the background.
+    if (mEffects.isNonOffloadableEffectEnabled()) {
+        return false;
+    }
+
+    return true;
+}
+
+audio_direct_mode_t AudioPolicyManager::getDirectPlaybackSupport(const audio_attributes_t *attr,
+                                                                 const audio_config_t *config) {
+    audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
+    offloadInfo.format = config->format;
+    offloadInfo.sample_rate = config->sample_rate;
+    offloadInfo.channel_mask = config->channel_mask;
+    offloadInfo.stream_type = mEngine->getStreamTypeForAttributes(*attr);
+    offloadInfo.has_video = false;
+    offloadInfo.is_streaming = false;
+    const bool offloadPossible = isOffloadPossible(offloadInfo, true /*durationIgnored*/);
+
+    audio_direct_mode_t directMode = AUDIO_DIRECT_NOT_SUPPORTED;
+    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+    audio_flags_to_audio_output_flags(attr->flags, &flags);
+    // only retain flags that will drive compressed offload or passthrough
+    uint32_t relevantFlags = AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+    if (offloadPossible) {
+        relevantFlags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+    }
+    flags = (audio_output_flags_t)((flags & relevantFlags) | AUDIO_OUTPUT_FLAG_DIRECT);
+
+    for (const auto& hwModule : mHwModules) {
+        for (const auto& curProfile : hwModule->getOutputProfiles()) {
+            if (!curProfile->isCompatibleProfile(DeviceVector(),
+                    config->sample_rate, nullptr /*updatedSamplingRate*/,
+                    config->format, nullptr /*updatedFormat*/,
+                    config->channel_mask, nullptr /*updatedChannelMask*/,
+                    flags)) {
+                continue;
+            }
+            // reject profiles not corresponding to a device currently available
+            if (!mAvailableOutputDevices.containsAtLeastOne(curProfile->getSupportedDevices())) {
+                continue;
+            }
+            if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
+                        != AUDIO_OUTPUT_FLAG_NONE) {
+                if ((directMode & AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED)
+                        != AUDIO_DIRECT_NOT_SUPPORTED) {
+                    // Already reports offload gapless supported. No need to report offload support.
+                    continue;
+                }
+                if ((curProfile->getFlags() & AUDIO_OUTPUT_FLAG_GAPLESS_OFFLOAD)
+                        != AUDIO_OUTPUT_FLAG_NONE) {
+                    // If offload gapless is reported, no need to report offload support.
+                    directMode = (audio_direct_mode_t) ((directMode &
+                            ~AUDIO_DIRECT_OFFLOAD_SUPPORTED) |
+                            AUDIO_DIRECT_OFFLOAD_GAPLESS_SUPPORTED);
+                } else {
+                    directMode = (audio_direct_mode_t)(directMode |AUDIO_DIRECT_OFFLOAD_SUPPORTED);
+                }
+            } else {
+                directMode = (audio_direct_mode_t) (directMode |
+                                                    AUDIO_DIRECT_BITSTREAM_SUPPORTED);
+            }
+        }
+    }
+    return directMode;
+}
+
 status_t AudioPolicyManager::listAudioPorts(audio_port_role_t role,
                                             audio_port_type_t type,
                                             unsigned int *num_ports,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 8a85b95..bdeba3d 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -366,6 +366,9 @@
 
         virtual status_t releaseSpatializerOutput(audio_io_handle_t output);
 
+        virtual audio_direct_mode_t getDirectPlaybackSupport(const audio_attributes_t *attr,
+                                                             const audio_config_t *config);
+
         bool isCallScreenModeSupported() override;
 
         void onNewAudioModulesAvailable() override;
@@ -1059,6 +1062,9 @@
         sp<SwAudioOutputDescriptor> openOutputWithProfileAndDevice(const sp<IOProfile>& profile,
                                                                    const DeviceVector& devices);
 
+        bool isOffloadPossible(const audio_offload_info_t& offloadInfo,
+                               bool durationIgnored = false);
+
 };
 
 };
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 1fbea7d..8ddd2a4 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -2334,4 +2334,24 @@
     return Status::ok();
 }
 
+Status AudioPolicyService::getDirectPlaybackSupport(const media::AudioAttributesInternal &attrAidl,
+                                                    const AudioConfig &configAidl,
+                                                    media::AudioDirectMode *_aidl_return) {
+    if (mAudioPolicyManager == nullptr) {
+        return binderStatusFromStatusT(NO_INIT);
+    }
+    if (_aidl_return == nullptr) {
+        return binderStatusFromStatusT(BAD_VALUE);
+    }
+    audio_attributes_t attr = VALUE_OR_RETURN_BINDER_STATUS(
+            aidl2legacy_AudioAttributesInternal_audio_attributes_t(attrAidl));
+    audio_config_t config = VALUE_OR_RETURN_BINDER_STATUS(
+            aidl2legacy_AudioConfig_audio_config_t(configAidl, false /*isInput*/));
+    Mutex::Autolock _l(mLock);
+    *_aidl_return = static_cast<media::AudioDirectMode>(
+            VALUE_OR_RETURN_BINDER_STATUS(legacy2aidl_audio_direct_mode_t_int32_t_mask(
+                    mAudioPolicyManager->getDirectPlaybackSupport(&attr, &config))));
+    return Status::ok();
+}
+
 } // namespace android
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index b3ac21b..84b1e50 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -272,6 +272,10 @@
             const std::vector<AudioDevice>& devices,
             bool* _aidl_return) override;
 
+    binder::Status getDirectPlaybackSupport(const media::AudioAttributesInternal& attr,
+                                            const AudioConfig& config,
+                                            media::AudioDirectMode* _aidl_return) override;
+
     status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) override;
 
     // IBinder::DeathRecipient
diff --git a/services/audiopolicy/service/SpatializerPoseController.cpp b/services/audiopolicy/service/SpatializerPoseController.cpp
index ffedf63..80a3d29 100644
--- a/services/audiopolicy/service/SpatializerPoseController.cpp
+++ b/services/audiopolicy/service/SpatializerPoseController.cpp
@@ -45,14 +45,14 @@
 // determine the time constants used for high-pass filtering those readings. If the value is set
 // too high, we may experience drift. If it is set too low, we may experience poses tending toward
 // identity too fast.
-constexpr auto kTranslationalDriftTimeConstant = 20s;
+constexpr auto kTranslationalDriftTimeConstant = 40s;
 
 // This should be set to the typical time scale that the rotation sensors used drift in. This
 // means, loosely, for how long we can trust the reading to be "accurate enough". This would
 // determine the time constants used for high-pass filtering those readings. If the value is set
 // too high, we may experience drift. If it is set too low, we may experience poses tending toward
 // identity too fast.
-constexpr auto kRotationalDriftTimeConstant = 20s;
+constexpr auto kRotationalDriftTimeConstant = 60s;
 
 // This is how far into the future we predict the head pose, using linear extrapolation based on
 // twist (velocity). It should be set to a value that matches the characteristic durations of moving
@@ -64,6 +64,25 @@
 // stale;
 constexpr auto kMaxLostSamples = 4;
 
+// Auto-recenter kicks in after the head has been still for this long.
+constexpr auto kAutoRecenterWindowDuration = 10s;
+
+// Auto-recenter considers head not still if translated by this much (in meters, approx).
+constexpr float kAutoRecenterTranslationThreshold = 0.1f;
+
+// Auto-recenter considers head not still if rotated by this much (in radians, approx).
+constexpr float kAutoRecenterRotationThreshold = 5.0f / 180 * M_PI;
+
+// Screen is considered to be unstable (not still) if it has moved significantly within the last
+// time window of this duration.
+constexpr auto kScreenStillnessWindowDuration = 10s;
+
+// Screen is considered to have moved significantly if translated by this much (in meter, approx).
+constexpr float kScreenStillnessTranslationThreshold = 0.1f;
+
+// Screen is considered to have moved significantly if rotated by this much (in radians, approx).
+constexpr float kScreenStillnessRotationThreshold = 5.0f / 180 * M_PI;
+
 // Time units for system clock ticks. This is what the Sensor Framework timestamps represent and
 // what we use for pose filtering.
 using Ticks = std::chrono::nanoseconds;
@@ -81,10 +100,17 @@
       mProcessor(createHeadTrackingProcessor(HeadTrackingProcessor::Options{
               .maxTranslationalVelocity = kMaxTranslationalVelocity / kTicksPerSecond,
               .maxRotationalVelocity = kMaxRotationalVelocity / kTicksPerSecond,
-              .translationalDriftTimeConstant = Ticks(kTranslationalDriftTimeConstant).count(),
-              .rotationalDriftTimeConstant = Ticks(kRotationalDriftTimeConstant).count(),
+              .translationalDriftTimeConstant =
+                      double(Ticks(kTranslationalDriftTimeConstant).count()),
+              .rotationalDriftTimeConstant = double(Ticks(kRotationalDriftTimeConstant).count()),
               .freshnessTimeout = Ticks(sensorPeriod * kMaxLostSamples).count(),
               .predictionDuration = Ticks(kPredictionDuration).count(),
+              .autoRecenterWindowDuration = Ticks(kAutoRecenterWindowDuration).count(),
+              .autoRecenterTranslationalThreshold = kAutoRecenterTranslationThreshold,
+              .autoRecenterRotationalThreshold = kAutoRecenterRotationThreshold,
+              .screenStillnessWindowDuration = Ticks(kScreenStillnessWindowDuration).count(),
+              .screenStillnessTranslationalThreshold = kScreenStillnessTranslationThreshold,
+              .screenStillnessRotationalThreshold = kScreenStillnessRotationThreshold,
       })),
       mPoseProvider(SensorPoseProvider::create("headtracker", this)),
       mThread([this, maxUpdatePeriod] {
diff --git a/services/audiopolicy/tests/AudioPolicyTestManager.h b/services/audiopolicy/tests/AudioPolicyTestManager.h
index 7f67940..9d0d558 100644
--- a/services/audiopolicy/tests/AudioPolicyTestManager.h
+++ b/services/audiopolicy/tests/AudioPolicyTestManager.h
@@ -33,6 +33,7 @@
     using AudioPolicyManager::releaseMsdOutputPatches;
     using AudioPolicyManager::setMsdOutputPatches;
     using AudioPolicyManager::getAudioPatches;
+    using AudioPolicyManager::getDirectPlaybackSupport;
     uint32_t getAudioPortGeneration() const { return mAudioPortGeneration; }
 };
 
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index b9c1260..046b84b 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -242,6 +242,14 @@
     setFormat(config.format);
     setSampleRate(config.sample_rate);
 
+    // If the position is not updated while the timestamp is updated for more than a certain amount,
+    // the timestamp reported from the HAL may not be accurate. Here, a timestamp grace period is
+    // set as 5 burst size. We may want to update this value if there is any report from OEMs saying
+    // that is too short.
+    static constexpr int kTimestampGraceBurstCount = 5;
+    mTimestampGracePeriodMs = ((int64_t) kTimestampGraceBurstCount * mFramesPerBurst
+            * AAUDIO_MILLIS_PER_SECOND) / getSampleRate();
+
     ALOGD("%s() actual rate = %d, channels = %d channelMask = %#x, deviceId = %d, capacity = %d\n",
           __func__, getSampleRate(), getSamplesPerFrame(), getChannelMask(),
           deviceId, getBufferCapacity());
@@ -418,14 +426,79 @@
 aaudio_result_t AAudioServiceEndpointMMAP::getExternalPosition(uint64_t *positionFrames,
                                                                int64_t *timeNanos)
 {
-    if (!mExternalPositionSupported) {
-        return AAUDIO_ERROR_INVALID_STATE;
+    if (mHalExternalPositionStatus != AAUDIO_OK) {
+        return mHalExternalPositionStatus;
     }
-    status_t status = mMmapStream->getExternalPosition(positionFrames, timeNanos);
-    if (status == INVALID_OPERATION) {
-        // getExternalPosition is not supported. Set mExternalPositionSupported as false
+    uint64_t tempPositionFrames;
+    int64_t tempTimeNanos;
+    status_t status = mMmapStream->getExternalPosition(&tempPositionFrames, &tempTimeNanos);
+    if (status != OK) {
+        // getExternalPosition reports error. The HAL may not support the API. Cache the result
         // so that the call will not go to the HAL next time.
-        mExternalPositionSupported = false;
+        mHalExternalPositionStatus = AAudioConvert_androidToAAudioResult(status);
+        return mHalExternalPositionStatus;
     }
-    return AAudioConvert_androidToAAudioResult(status);
+
+    // If the HAL keeps reporting the same position or timestamp, the HAL may be having some issues
+    // to report correct external position. In that case, we will not trust the values reported from
+    // the HAL. Ideally, we may want to stop querying external position if the HAL cannot report
+    // correct position within a period. But it may not be a good idea to get system time too often.
+    // In that case, a maximum number of frozen external position is defined so that if the
+    // count of the same timestamp or position is reported by the HAL continuously, the values from
+    // the HAL will no longer be trusted.
+    static constexpr int kMaxFrozenCount = 20;
+    // If the HAL version is less than 7.0, the getPresentationPosition is an optional API.
+    // If the HAL version is 7.0 or later, the getPresentationPosition is a mandatory API.
+    // In that case, even the returned status is NO_ERROR, it doesn't indicate the returned
+    // position is a valid one. Do a simple validation, which is checking if the position is
+    // forward within half a second or not, here so that this function can return error if
+    // the validation fails. Note that we don't only apply this validation logic to HAL API
+    // less than 7.0. The reason is that there is a chance the HAL is not reporting the
+    // timestamp and position correctly.
+    if (mLastPositionFrames > tempPositionFrames) {
+        // If the position is going backwards, there must be something wrong with the HAL.
+        // In that case, we do not trust the values reported by the HAL.
+        ALOGW("%s position is going backwards, last position(%jd) current position(%jd)",
+              __func__, mLastPositionFrames, tempPositionFrames);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    } else if (mLastPositionFrames == tempPositionFrames) {
+        if (tempTimeNanos - mTimestampNanosForLastPosition >
+                AAUDIO_NANOS_PER_MILLISECOND * mTimestampGracePeriodMs) {
+            ALOGW("%s, the reported position is not changed within %d msec. "
+                  "Set the external position as not supported", __func__, mTimestampGracePeriodMs);
+            mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+            return mHalExternalPositionStatus;
+        }
+        mFrozenPositionCount++;
+    } else {
+        mFrozenPositionCount = 0;
+    }
+
+    if (mTimestampNanosForLastPosition > tempTimeNanos) {
+        // If the timestamp is going backwards, there must be something wrong with the HAL.
+        // In that case, we do not trust the values reported by the HAL.
+        ALOGW("%s timestamp is going backwards, last timestamp(%jd), current timestamp(%jd)",
+              __func__, mTimestampNanosForLastPosition, tempTimeNanos);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    } else if (mTimestampNanosForLastPosition == tempTimeNanos) {
+        mFrozenTimestampCount++;
+    } else {
+        mFrozenTimestampCount = 0;
+    }
+
+    if (mFrozenTimestampCount + mFrozenPositionCount > kMaxFrozenCount) {
+        ALOGW("%s too many frozen external position from HAL.", __func__);
+        mHalExternalPositionStatus = AAUDIO_ERROR_INTERNAL;
+        return mHalExternalPositionStatus;
+    }
+
+    mLastPositionFrames = tempPositionFrames;
+    mTimestampNanosForLastPosition = tempTimeNanos;
+
+    // Only update the timestamp and position when they looks valid.
+    *positionFrames = tempPositionFrames;
+    *timeNanos = tempTimeNanos;
+    return mHalExternalPositionStatus;
 }
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
index ddfac63..6314e5e 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.h
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -106,7 +106,12 @@
 
     int64_t                                   mHardwareTimeOffsetNanos = 0; // TODO get from HAL
 
-    bool                                      mExternalPositionSupported = true;
+    aaudio_result_t                           mHalExternalPositionStatus = AAUDIO_OK;
+    uint64_t                                  mLastPositionFrames = 0;
+    int64_t                                   mTimestampNanosForLastPosition = 0;
+    int32_t                                   mTimestampGracePeriodMs;
+    int32_t                                   mFrozenPositionCount = 0;
+    int32_t                                   mFrozenTimestampCount = 0;
 
 };
 
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 05b7f7d..ffc16ac 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -167,7 +167,6 @@
 // If it fails, get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp_l(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
-
     sp<AAudioServiceEndpoint> endpoint = mServiceEndpointWeak.promote();
     if (endpoint == nullptr) {
         ALOGE("%s() has no endpoint", __func__);
@@ -176,17 +175,17 @@
     sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP =
             static_cast<AAudioServiceEndpointMMAP *>(endpoint.get());
 
-    // Disable this code temporarily because the HAL is not returning
-    // a useful result.
-#if 0
     uint64_t position;
-    if (serviceEndpointMMAP->getExternalPosition(&position, timeNanos) == AAUDIO_OK) {
-        ALOGD("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
+    aaudio_result_t result = serviceEndpointMMAP->getExternalPosition(&position, timeNanos);
+    if (result == AAUDIO_OK) {
+        ALOGV("%s() getExternalPosition() says pos = %" PRIi64 ", time = %" PRIi64,
                 __func__, position, *timeNanos);
         *positionFrames = (int64_t) position;
         return AAUDIO_OK;
-    } else
-#endif
+    } else {
+        ALOGV("%s() getExternalPosition() returns error %d", __func__, result);
+    }
+
     if (mAtomicStreamTimestamp.isValid()) {
         Timestamp timestamp = mAtomicStreamTimestamp.read();
         *positionFrames = timestamp.getPosition();
diff --git a/services/tuner/TunerFrontend.cpp b/services/tuner/TunerFrontend.cpp
index e86e8e1..a5ef2bb 100644
--- a/services/tuner/TunerFrontend.cpp
+++ b/services/tuner/TunerFrontend.cpp
@@ -115,16 +115,6 @@
     return mFrontend->setLnb(static_cast<TunerLnb*>(lnb.get())->getId());
 }
 
-::ndk::ScopedAStatus TunerFrontend::setLna(bool bEnable) {
-    if (mFrontend == nullptr) {
-        ALOGD("IFrontend is not initialized");
-        return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                static_cast<int32_t>(Result::UNAVAILABLE));
-    }
-
-    return mFrontend->setLna(bEnable);
-}
-
 ::ndk::ScopedAStatus TunerFrontend::linkCiCamToFrontend(int32_t ciCamId, int32_t* _aidl_return) {
     if (mFrontend == nullptr) {
         ALOGD("IFrontend is not initialized");
@@ -174,6 +164,16 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus TunerFrontend::getHardwareInfo(std::string* _aidl_return) {
+    if (mFrontend == nullptr) {
+        ALOGD("IFrontend is not initialized");
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    return mFrontend->getHardwareInfo(_aidl_return);
+}
+
 /////////////// FrontendCallback ///////////////////////
 ::ndk::ScopedAStatus TunerFrontend::FrontendCallback::onEvent(FrontendEventType frontendEventType) {
     ALOGV("FrontendCallback::onEvent, type=%d", frontendEventType);
diff --git a/services/tuner/TunerFrontend.h b/services/tuner/TunerFrontend.h
index 417d969..418a751 100644
--- a/services/tuner/TunerFrontend.h
+++ b/services/tuner/TunerFrontend.h
@@ -56,13 +56,13 @@
                               FrontendScanType in_frontendScanType) override;
     ::ndk::ScopedAStatus stopScan() override;
     ::ndk::ScopedAStatus setLnb(const shared_ptr<ITunerLnb>& in_lnb) override;
-    ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
     ::ndk::ScopedAStatus linkCiCamToFrontend(int32_t in_ciCamId, int32_t* _aidl_return) override;
     ::ndk::ScopedAStatus unlinkCiCamToFrontend(int32_t in_ciCamId) override;
     ::ndk::ScopedAStatus close() override;
     ::ndk::ScopedAStatus getStatus(const vector<FrontendStatusType>& in_statusTypes,
                                    vector<FrontendStatus>* _aidl_return) override;
     ::ndk::ScopedAStatus getFrontendId(int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus getHardwareInfo(std::string* _aidl_return) override;
 
     struct FrontendCallback : public BnFrontendCallback {
         FrontendCallback(const shared_ptr<ITunerFrontendCallback> tunerFrontendCallback)
diff --git a/services/tuner/TunerService.cpp b/services/tuner/TunerService.cpp
index 36e4cd1..335578d 100644
--- a/services/tuner/TunerService.cpp
+++ b/services/tuner/TunerService.cpp
@@ -260,6 +260,16 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus TunerService::setLna(bool bEnable) {
+    if (!hasITuner()) {
+        ALOGD("get ITuner failed");
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    return mTuner->setLna(bEnable);
+}
+
 string TunerService::addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter) {
     Mutex::Autolock _l(mSharedFiltersLock);
 
diff --git a/services/tuner/TunerService.h b/services/tuner/TunerService.h
index 7bf50b6..f144744 100644
--- a/services/tuner/TunerService.h
+++ b/services/tuner/TunerService.h
@@ -76,6 +76,7 @@
     ::ndk::ScopedAStatus openSharedFilter(const string& in_filterToken,
                                           const shared_ptr<ITunerFilterCallback>& in_cb,
                                           shared_ptr<ITunerFilter>* _aidl_return) override;
+    ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
 
     string addFilterToShared(const shared_ptr<TunerFilter>& sharedFilter);
     void removeSharedFilter(const shared_ptr<TunerFilter>& sharedFilter);
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
index 771a647..96f285f 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerFrontend.aidl
@@ -69,13 +69,6 @@
     void setLnb(in ITunerLnb lnb);
 
     /**
-     * Enable or Disable Low Noise Amplifier (LNA).
-     *
-     * @param bEnable enable Lna or not.
-     */
-    void setLna(in boolean bEnable);
-
-    /**
      * Link Frontend to the cicam with given id.
      *
      * @return lts id
@@ -101,4 +94,9 @@
      * Gets the id of the frontend.
      */
     int getFrontendId();
+
+    /**
+     * Request hardware information about the frontend.
+     */
+    String getHardwareInfo();
 }
diff --git a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
index e6a1a5c..248077b 100644
--- a/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
+++ b/services/tuner/aidl/android/media/tv/tuner/ITunerService.aidl
@@ -104,4 +104,11 @@
      * @return a newly created ITunerFilter interface.
      */
     ITunerFilter openSharedFilter(in String filterToken, in ITunerFilterCallback cb);
+
+    /**
+     * Enable or Disable Low Noise Amplifier (LNA).
+     *
+     * @param bEnable enable Lna or not.
+     */
+    void setLna(in boolean bEnable);
 }
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index b738b57..a5bbf39 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -29,6 +29,7 @@
 #include "TunerHidlService.h"
 
 using ::aidl::android::hardware::tv::tuner::AudioExtraMetaData;
+using ::aidl::android::hardware::tv::tuner::AudioStreamType;
 using ::aidl::android::hardware::tv::tuner::Constant;
 using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettings;
 using ::aidl::android::hardware::tv::tuner::DemuxAlpFilterSettingsFilterSettings;
@@ -604,7 +605,11 @@
 
 bool TunerHidlFilter::getHidlAvStreamType(const AvStreamType avStreamType, HidlAvStreamType& type) {
     if (isAudioFilter()) {
-        type.audio(static_cast<HidlAudioStreamType>(avStreamType.get<AvStreamType::audio>()));
+        AudioStreamType audio = avStreamType.get<AvStreamType::audio>();
+        if (static_cast<int32_t>(audio) > static_cast<int32_t>(HidlAudioStreamType::DRA)) {
+            return false;
+        }
+        type.audio(static_cast<HidlAudioStreamType>(audio));
         return true;
     }
 
@@ -1044,6 +1049,8 @@
         media.avDataId = static_cast<int64_t>(mediaEvent.avDataId);
         media.mpuSequenceNumber = static_cast<int32_t>(mediaEvent.mpuSequenceNumber);
         media.isPesPrivateData = mediaEvent.isPesPrivateData;
+        media.scIndexMask.set<DemuxFilterScIndexMask::scIndex>(
+                static_cast<int32_t>(DemuxScIndex::UNDEFINED));
 
         if (mediaEvent.extraMetaData.getDiscriminator() ==
             HidlDemuxFilterMediaEvent::ExtraMetaData::hidl_discriminator::audio) {
diff --git a/services/tuner/hidl/TunerHidlFrontend.cpp b/services/tuner/hidl/TunerHidlFrontend.cpp
index 1f28406..057f24a 100644
--- a/services/tuner/hidl/TunerHidlFrontend.cpp
+++ b/services/tuner/hidl/TunerHidlFrontend.cpp
@@ -22,6 +22,7 @@
 #include <aidl/android/hardware/tv/tuner/Result.h>
 
 #include "TunerHidlLnb.h"
+#include "TunerHidlService.h"
 
 using ::aidl::android::hardware::tv::tuner::FrontendAnalogSettings;
 using ::aidl::android::hardware::tv::tuner::FrontendAnalogSifStandard;
@@ -309,21 +310,6 @@
     return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
 }
 
-::ndk::ScopedAStatus TunerHidlFrontend::setLna(bool bEnable) {
-    if (mFrontend == nullptr) {
-        ALOGD("IFrontend is not initialized");
-        return ::ndk::ScopedAStatus::fromServiceSpecificError(
-                static_cast<int32_t>(Result::UNAVAILABLE));
-    }
-
-    HidlResult status = mFrontend->setLna(bEnable);
-    if (status == HidlResult::SUCCESS) {
-        return ::ndk::ScopedAStatus::ok();
-    }
-
-    return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
-}
-
 ::ndk::ScopedAStatus TunerHidlFrontend::linkCiCamToFrontend(int32_t ciCamId,
                                                             int32_t* _aidl_return) {
     if (mFrontend_1_1 == nullptr) {
@@ -369,6 +355,8 @@
                 static_cast<int32_t>(Result::UNAVAILABLE));
     }
 
+    TunerHidlService::getTunerService()->removeFrontend(this->ref<TunerHidlFrontend>());
+
     HidlResult status = mFrontend->close();
     mFrontend = nullptr;
     mFrontend_1_1 = nullptr;
@@ -434,6 +422,21 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus TunerHidlFrontend::getHardwareInfo(std::string* _aidl_return) {
+    _aidl_return->clear();
+    return ::ndk::ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(Result::UNAVAILABLE));
+}
+
+void TunerHidlFrontend::setLna(bool bEnable) {
+    if (mFrontend == nullptr) {
+        ALOGD("IFrontend is not initialized");
+        return;
+    }
+
+    mFrontend->setLna(bEnable);
+}
+
 /////////////// FrontendCallback ///////////////////////
 Return<void> TunerHidlFrontend::FrontendCallback::onEvent(HidlFrontendEventType frontendEventType) {
     ALOGV("FrontendCallback::onEvent, type=%d", frontendEventType);
diff --git a/services/tuner/hidl/TunerHidlFrontend.h b/services/tuner/hidl/TunerHidlFrontend.h
index 6a3a04a..7ff278c 100644
--- a/services/tuner/hidl/TunerHidlFrontend.h
+++ b/services/tuner/hidl/TunerHidlFrontend.h
@@ -76,13 +76,15 @@
                               FrontendScanType in_frontendScanType) override;
     ::ndk::ScopedAStatus stopScan() override;
     ::ndk::ScopedAStatus setLnb(const shared_ptr<ITunerLnb>& in_lnb) override;
-    ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
     ::ndk::ScopedAStatus linkCiCamToFrontend(int32_t in_ciCamId, int32_t* _aidl_return) override;
     ::ndk::ScopedAStatus unlinkCiCamToFrontend(int32_t in_ciCamId) override;
     ::ndk::ScopedAStatus close() override;
     ::ndk::ScopedAStatus getStatus(const vector<FrontendStatusType>& in_statusTypes,
                                    vector<FrontendStatus>* _aidl_return) override;
     ::ndk::ScopedAStatus getFrontendId(int32_t* _aidl_return) override;
+    ::ndk::ScopedAStatus getHardwareInfo(std::string* _aidl_return) override;
+
+    void setLna(bool in_bEnable);
 
     struct FrontendCallback : public HidlIFrontendCallback {
         FrontendCallback(const shared_ptr<ITunerFrontendCallback> tunerFrontendCallback)
diff --git a/services/tuner/hidl/TunerHidlService.cpp b/services/tuner/hidl/TunerHidlService.cpp
index f4b0cde..f4aa61a 100644
--- a/services/tuner/hidl/TunerHidlService.cpp
+++ b/services/tuner/hidl/TunerHidlService.cpp
@@ -74,7 +74,10 @@
     updateTunerResources();
 }
 
-TunerHidlService::~TunerHidlService() {}
+TunerHidlService::~TunerHidlService() {
+    mOpenedFrontends.clear();
+    mLnaStatus = -1;
+}
 
 binder_status_t TunerHidlService::instantiate() {
     if (HidlITuner::getService() == nullptr) {
@@ -237,7 +240,17 @@
     if (status != HidlResult::SUCCESS) {
         return ::ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(status));
     }
-    *_aidl_return = ::ndk::SharedRefBase::make<TunerHidlFrontend>(frontend, id);
+
+    shared_ptr<TunerHidlFrontend> tunerFrontend =
+            ::ndk::SharedRefBase::make<TunerHidlFrontend>(frontend, id);
+    if (mLnaStatus != -1) {
+        tunerFrontend->setLna(mLnaStatus == 1);
+    }
+    {
+        Mutex::Autolock _l(mOpenedFrontendsLock);
+        mOpenedFrontends.insert(tunerFrontend);
+    }
+    *_aidl_return = tunerFrontend;
     return ::ndk::ScopedAStatus::ok();
 }
 
@@ -355,6 +368,25 @@
     return ::ndk::ScopedAStatus::ok();
 }
 
+::ndk::ScopedAStatus TunerHidlService::setLna(bool bEnable) {
+    if (!hasITuner()) {
+        ALOGE("get ITuner failed");
+        return ::ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(Result::UNAVAILABLE));
+    }
+
+    mLnaStatus = bEnable ? 1 : 0;
+
+    {
+        Mutex::Autolock _l(mOpenedFrontendsLock);
+        for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
+            (*it)->setLna(mLnaStatus == 1);
+        }
+    }
+
+    return ::ndk::ScopedAStatus::ok();
+}
+
 string TunerHidlService::addFilterToShared(const shared_ptr<TunerHidlFilter>& sharedFilter) {
     Mutex::Autolock _l(mSharedFiltersLock);
 
@@ -372,6 +404,16 @@
     mSharedFilters.erase(to_string(reinterpret_cast<std::uintptr_t>(sharedFilter.get())));
 }
 
+void TunerHidlService::removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend) {
+    Mutex::Autolock _l(mOpenedFrontendsLock);
+    for (auto it = mOpenedFrontends.begin(); it != mOpenedFrontends.end(); ++it) {
+        if (it->get() == frontend.get()) {
+            mOpenedFrontends.erase(it);
+            break;
+        }
+    }
+}
+
 void TunerHidlService::updateTunerResources() {
     if (!hasITuner()) {
         ALOGE("Failed to updateTunerResources");
diff --git a/services/tuner/hidl/TunerHidlService.h b/services/tuner/hidl/TunerHidlService.h
index 2b8750e..4cff7cf 100644
--- a/services/tuner/hidl/TunerHidlService.h
+++ b/services/tuner/hidl/TunerHidlService.h
@@ -24,8 +24,11 @@
 #include <android/hardware/tv/tuner/1.1/ITuner.h>
 #include <utils/Mutex.h>
 
+#include <unordered_set>
+
 #include "TunerHelper.h"
 #include "TunerHidlFilter.h"
+#include "TunerHidlFrontend.h"
 
 using ::aidl::android::hardware::tv::tuner::DemuxCapabilities;
 using ::aidl::android::hardware::tv::tuner::DemuxFilterEvent;
@@ -85,9 +88,11 @@
     ::ndk::ScopedAStatus openSharedFilter(const string& in_filterToken,
                                           const shared_ptr<ITunerFilterCallback>& in_cb,
                                           shared_ptr<ITunerFilter>* _aidl_return) override;
+    ::ndk::ScopedAStatus setLna(bool in_bEnable) override;
 
     string addFilterToShared(const shared_ptr<TunerHidlFilter>& sharedFilter);
     void removeSharedFilter(const shared_ptr<TunerHidlFilter>& sharedFilter);
+    void removeFrontend(const shared_ptr<TunerHidlFrontend>& frontend);
 
     static shared_ptr<TunerHidlService> getTunerService();
 
@@ -108,6 +113,9 @@
     int mTunerVersion = TUNER_HAL_VERSION_UNKNOWN;
     Mutex mSharedFiltersLock;
     map<string, shared_ptr<TunerHidlFilter>> mSharedFilters;
+    Mutex mOpenedFrontendsLock;
+    unordered_set<shared_ptr<TunerHidlFrontend>> mOpenedFrontends;
+    int mLnaStatus = -1;
 
     static shared_ptr<TunerHidlService> sTunerService;
 };