Merge "Camera: Improve logical camera RAW capture docs" into sc-dev am: 597454da44

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/15113686

Change-Id: I6ff635c868ef86ed675793320c4958980320cf56
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 88e752b..5344756 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -480,6 +480,12 @@
     return af->systemReady();
 }
 
+status_t AudioSystem::audioPolicyReady() {
+    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+    if (af == 0) return NO_INIT;
+    return af->audioPolicyReady();
+}
+
 status_t AudioSystem::getFrameCountHAL(audio_io_handle_t ioHandle,
                                        size_t* frameCount) {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
diff --git a/media/libaudioclient/IAudioFlinger.cpp b/media/libaudioclient/IAudioFlinger.cpp
index e46b349..ef0f244 100644
--- a/media/libaudioclient/IAudioFlinger.cpp
+++ b/media/libaudioclient/IAudioFlinger.cpp
@@ -711,6 +711,10 @@
     return statusTFromBinderStatus(mDelegate->systemReady());
 }
 
+status_t AudioFlingerClientAdapter::audioPolicyReady() {
+    return statusTFromBinderStatus(mDelegate->audioPolicyReady());
+}
+
 size_t AudioFlingerClientAdapter::frameCountHAL(audio_io_handle_t ioHandle) const {
     auto result = [&]() -> ConversionResult<size_t> {
         int32_t ioHandleAidl = VALUE_OR_RETURN(legacy2aidl_audio_io_handle_t_int32_t(ioHandle));
@@ -1184,6 +1188,11 @@
     return Status::fromStatusT(mDelegate->systemReady());
 }
 
+Status AudioFlingerServerAdapter::audioPolicyReady() {
+    mDelegate->audioPolicyReady();
+    return Status::ok();
+}
+
 Status AudioFlingerServerAdapter::frameCountHAL(int32_t ioHandle, int64_t* _aidl_return) {
     audio_io_handle_t ioHandleLegacy = VALUE_OR_RETURN_BINDER(
             aidl2legacy_int32_t_audio_io_handle_t(ioHandle));
diff --git a/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
index f88fc3c..8538d8a 100644
--- a/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
+++ b/media/libaudioclient/aidl/android/media/AudioVibratorInfo.aidl
@@ -24,4 +24,5 @@
     int id;
     float resonantFrequency;
     float qFactor;
+    float maxAmplitude;
 }
diff --git a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
index d2cae6d..7ffcc33 100644
--- a/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
+++ b/media/libaudioclient/aidl/android/media/IAudioFlingerService.aidl
@@ -197,6 +197,9 @@
     /* Indicate JAVA services are ready (scheduling, power management ...) */
     oneway void systemReady();
 
+    /* Indicate audio policy service is ready */
+    oneway void audioPolicyReady();
+
     // Returns the number of frames per audio HAL buffer.
     long frameCountHAL(int /* audio_io_handle_t */ ioHandle);
 
diff --git a/media/libaudioclient/include/media/AudioSystem.h b/media/libaudioclient/include/media/AudioSystem.h
index a9109c8..626dcbf 100644
--- a/media/libaudioclient/include/media/AudioSystem.h
+++ b/media/libaudioclient/include/media/AudioSystem.h
@@ -225,6 +225,9 @@
     // Indicate JAVA services are ready (scheduling, power management ...)
     static status_t systemReady();
 
+    // Indicate audio policy service is ready
+    static status_t audioPolicyReady();
+
     // Returns the number of frames per audio HAL buffer.
     // Corresponds to audio_stream->get_buffer_size()/audio_stream_in_frame_size() for input.
     // See also getFrameCount().
diff --git a/media/libaudioclient/include/media/IAudioFlinger.h b/media/libaudioclient/include/media/IAudioFlinger.h
index 0e059f7..9e5019e 100644
--- a/media/libaudioclient/include/media/IAudioFlinger.h
+++ b/media/libaudioclient/include/media/IAudioFlinger.h
@@ -329,6 +329,9 @@
     /* Indicate JAVA services are ready (scheduling, power management ...) */
     virtual status_t systemReady() = 0;
 
+    // Indicate audio policy service is ready
+    virtual status_t audioPolicyReady() = 0;
+
     // Returns the number of frames per audio HAL buffer.
     virtual size_t frameCountHAL(audio_io_handle_t ioHandle) const = 0;
 
@@ -432,6 +435,8 @@
     status_t setAudioPortConfig(const struct audio_port_config* config) override;
     audio_hw_sync_t getAudioHwSyncForSession(audio_session_t sessionId) override;
     status_t systemReady() override;
+    status_t audioPolicyReady() override;
+
     size_t frameCountHAL(audio_io_handle_t ioHandle) const override;
     status_t getMicrophones(std::vector<media::MicrophoneInfo>* microphones) override;
     status_t setAudioHalPids(const std::vector<pid_t>& pids) override;
@@ -514,6 +519,7 @@
             SET_AUDIO_PORT_CONFIG = media::BnAudioFlingerService::TRANSACTION_setAudioPortConfig,
             GET_AUDIO_HW_SYNC_FOR_SESSION = media::BnAudioFlingerService::TRANSACTION_getAudioHwSyncForSession,
             SYSTEM_READY = media::BnAudioFlingerService::TRANSACTION_systemReady,
+            AUDIO_POLICY_READY = media::BnAudioFlingerService::TRANSACTION_audioPolicyReady,
             FRAME_COUNT_HAL = media::BnAudioFlingerService::TRANSACTION_frameCountHAL,
             GET_MICROPHONES = media::BnAudioFlingerService::TRANSACTION_getMicrophones,
             SET_MASTER_BALANCE = media::BnAudioFlingerService::TRANSACTION_setMasterBalance,
@@ -624,6 +630,7 @@
     Status setAudioPortConfig(const media::AudioPortConfig& config) override;
     Status getAudioHwSyncForSession(int32_t sessionId, int32_t* _aidl_return) override;
     Status systemReady() override;
+    Status audioPolicyReady() override;
     Status frameCountHAL(int32_t ioHandle, int64_t* _aidl_return) override;
     Status getMicrophones(std::vector<media::MicrophoneInfoData>* _aidl_return) override;
     Status setAudioHalPids(const std::vector<int32_t>& pids) override;
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
index 9c4363c..f75bbf3 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.cpp
@@ -73,7 +73,9 @@
         uint32_t index, effect_descriptor_t *pDescriptor) {
     // TODO: We need somehow to track the changes on the server side
     // or figure out how to convert everybody to query all the descriptors at once.
-    // TODO: check for nullptr
+    if (pDescriptor == nullptr) {
+        return BAD_VALUE;
+    }
     if (mLastDescriptors.size() == 0) {
         status_t queryResult = queryAllDescriptors();
         if (queryResult != OK) return queryResult;
@@ -85,7 +87,9 @@
 
 status_t EffectsFactoryHalHidl::getDescriptor(
         const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptor) {
-    // TODO: check for nullptr
+    if (pDescriptor == nullptr || pEffectUuid == nullptr) {
+        return BAD_VALUE;
+    }
     if (mEffectsFactory == 0) return NO_INIT;
     Uuid hidlUuid;
     UuidUtils::uuidFromHal(*pEffectUuid, &hidlUuid);
@@ -105,6 +109,33 @@
     return processReturn(__FUNCTION__, ret);
 }
 
+status_t EffectsFactoryHalHidl::getDescriptors(const effect_uuid_t *pEffectType,
+                                               std::vector<effect_descriptor_t> *descriptors) {
+    if (pEffectType == nullptr || descriptors == nullptr) {
+        return BAD_VALUE;
+    }
+
+    uint32_t numEffects = 0;
+    status_t status = queryNumberEffects(&numEffects);
+    if (status != NO_ERROR) {
+        ALOGW("%s error %d from FactoryHal queryNumberEffects", __func__, status);
+        return status;
+    }
+
+    for (uint32_t i = 0; i < numEffects; i++) {
+        effect_descriptor_t descriptor;
+        status = getDescriptor(i, &descriptor);
+        if (status != NO_ERROR) {
+            ALOGW("%s error %d from FactoryHal getDescriptor", __func__, status);
+            continue;
+        }
+        if (memcmp(&descriptor.type, pEffectType, sizeof(effect_uuid_t)) == 0) {
+            descriptors->push_back(descriptor);
+        }
+    }
+    return descriptors->empty() ? NAME_NOT_FOUND : NO_ERROR;
+}
+
 status_t EffectsFactoryHalHidl::createEffect(
         const effect_uuid_t *pEffectUuid, int32_t sessionId, int32_t ioId,
         int32_t deviceId __unused, sp<EffectHalInterface> *effect) {
diff --git a/media/libaudiohal/impl/EffectsFactoryHalHidl.h b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
index 5fa85e7..ff26d9f 100644
--- a/media/libaudiohal/impl/EffectsFactoryHalHidl.h
+++ b/media/libaudiohal/impl/EffectsFactoryHalHidl.h
@@ -45,6 +45,9 @@
     virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid,
             effect_descriptor_t *pDescriptor);
 
+    virtual status_t getDescriptors(const effect_uuid_t *pEffectType,
+                                    std::vector<effect_descriptor_t> *descriptors);
+
     // Creates an effect engine of the specified type.
     // To release the effect engine, it is necessary to release references
     // to the returned effect object.
diff --git a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
index 9fb56ae..3e505bd 100644
--- a/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
+++ b/media/libaudiohal/include/media/audiohal/EffectsFactoryHalInterface.h
@@ -37,6 +37,9 @@
     virtual status_t getDescriptor(const effect_uuid_t *pEffectUuid,
             effect_descriptor_t *pDescriptor) = 0;
 
+    virtual status_t getDescriptors(const effect_uuid_t *pEffectType,
+                                    std::vector<effect_descriptor_t> *descriptors) = 0;
+
     // Creates an effect engine of the specified type.
     // To release the effect engine, it is necessary to release references
     // to the returned effect object.
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index d85e2e9..e68c002 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -434,6 +434,12 @@
                 track->mHapticIntensity = hapticIntensity;
             }
             } break;
+        case HAPTIC_MAX_AMPLITUDE: {
+            const float hapticMaxAmplitude = *reinterpret_cast<float*>(value);
+            if (track->mHapticMaxAmplitude != hapticMaxAmplitude) {
+                track->mHapticMaxAmplitude = hapticMaxAmplitude;
+            }
+            } break;
         default:
             LOG_ALWAYS_FATAL("setParameter track: bad param %d", param);
         }
@@ -553,6 +559,7 @@
     // haptic
     t->mHapticPlaybackEnabled = false;
     t->mHapticIntensity = os::HapticScale::NONE;
+    t->mHapticMaxAmplitude = NAN;
     t->mMixerHapticChannelMask = AUDIO_CHANNEL_NONE;
     t->mMixerHapticChannelCount = 0;
     t->mAdjustInChannelCount = t->channelCount + t->mHapticChannelCount;
@@ -602,7 +609,8 @@
                 switch (t->mMixerFormat) {
                 // Mixer format should be AUDIO_FORMAT_PCM_FLOAT.
                 case AUDIO_FORMAT_PCM_FLOAT: {
-                    os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity);
+                    os::scaleHapticData((float*) buffer, sampleCount, t->mHapticIntensity,
+                                        t->mHapticMaxAmplitude);
                 } break;
                 default:
                     LOG_ALWAYS_FATAL("bad mMixerFormat: %#x", t->mMixerFormat);
diff --git a/media/libaudioprocessing/AudioMixerOps.h b/media/libaudioprocessing/AudioMixerOps.h
index cd47dc6..2988c67 100644
--- a/media/libaudioprocessing/AudioMixerOps.h
+++ b/media/libaudioprocessing/AudioMixerOps.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_AUDIO_MIXER_OPS_H
 #define ANDROID_AUDIO_MIXER_OPS_H
 
+#include <audio_utils/channels.h>
+#include <audio_utils/primitives.h>
 #include <system/audio.h>
 
 namespace android {
@@ -229,15 +231,26 @@
  * complexity of working on interleaved streams is now getting
  * too high, and likely limits compiler optimization.
  */
-template <int MIXTYPE, int NCHAN,
+
+// compile-time function.
+constexpr inline bool usesCenterChannel(audio_channel_mask_t mask) {
+    using namespace audio_utils::channels;
+    for (size_t i = 0; i < std::size(kSideFromChannelIdx); ++i) {
+        if ((mask & (1 << i)) != 0 && kSideFromChannelIdx[i] == AUDIO_GEOMETRY_SIDE_CENTER) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*
+ * Applies stereo volume to the audio data based on proper left right channel affinity
+ * (templated channel MASK parameter).
+ */
+template <int MIXTYPE, audio_channel_mask_t MASK,
         typename TO, typename TI, typename TV,
         typename F>
-void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
-    static_assert(NCHAN > 0 && NCHAN <= FCC_LIMIT);
-    static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
-            || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
-            || MIXTYPE == MIXTYPE_STEREOEXPAND
-            || MIXTYPE == MIXTYPE_MONOEXPAND);
+void stereoVolumeHelperWithChannelMask(TO*& out, const TI*& in, const TV *vol, F f) {
     auto proc = [](auto& a, const auto& b) {
         if constexpr (MIXTYPE == MIXTYPE_MULTI_STEREOVOL
                 || MIXTYPE == MIXTYPE_STEREOEXPAND
@@ -250,59 +263,109 @@
     auto inp = [&in]() -> const TI& {
         if constexpr (MIXTYPE == MIXTYPE_STEREOEXPAND
                 || MIXTYPE == MIXTYPE_MONOEXPAND) {
-            return *in;
+            return *in; // note STEREOEXPAND assumes replicated L/R channels (see doc below).
         } else {
             return *in++;
         }
     };
 
-    // HALs should only expose the canonical channel masks.
-    proc(*out++, f(inp(), vol[0])); // front left
-    if constexpr (NCHAN == 1) return;
-    proc(*out++, f(inp(), vol[1])); // front right
-    if constexpr (NCHAN == 2)  return;
-    if constexpr (NCHAN == 4) {
-        proc(*out++, f(inp(), vol[0])); // back left
-        proc(*out++, f(inp(), vol[1])); // back right
-        return;
-    }
-
-    // TODO: Precompute center volume if not ramping.
     std::decay_t<TV> center;
-    if constexpr (std::is_floating_point_v<TV>) {
-        center = (vol[0] + vol[1]) * 0.5;       // do not use divide
-    } else {
-        center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
-    }
-    proc(*out++, f(inp(), center)); // center (or 2.1 LFE)
-    if constexpr (NCHAN == 3) return;
-    if constexpr (NCHAN == 5) {
-        proc(*out++, f(inp(), vol[0]));  // back left
-        proc(*out++, f(inp(), vol[1]));  // back right
-        return;
-    }
-
-    proc(*out++, f(inp(), center)); // lfe
-    proc(*out++, f(inp(), vol[0])); // back left
-    proc(*out++, f(inp(), vol[1])); // back right
-    if constexpr (NCHAN == 6) return;
-    if constexpr (NCHAN == 7) {
-        proc(*out++, f(inp(), center)); // back center
-        return;
-    }
-    // NCHAN == 8
-    proc(*out++, f(inp(), vol[0])); // side left
-    proc(*out++, f(inp(), vol[1])); // side right
-    if constexpr (NCHAN > FCC_8) {
-        // Mutes to zero extended surround channels.
-        // 7.1.4 has the correct behavior.
-        // 22.2 has the behavior that FLC and FRC will be mixed instead
-        // of SL and SR and LFE will be center, not left.
-        for (int i = 8; i < NCHAN; ++i) {
-            // TODO: Consider using android::audio_utils::channels::kSideFromChannelIdx
-            proc(*out++, f(inp(), 0.f));
+    constexpr bool USES_CENTER_CHANNEL = usesCenterChannel(MASK);
+    if constexpr (USES_CENTER_CHANNEL) {
+        if constexpr (std::is_floating_point_v<TV>) {
+            center = (vol[0] + vol[1]) * 0.5;       // do not use divide
+        } else {
+            center = (vol[0] >> 1) + (vol[1] >> 1); // rounds to 0.
         }
     }
+
+    using namespace audio_utils::channels;
+
+    // if LFE and LFE2 are both present, they take left and right volume respectively.
+    constexpr unsigned LFE_LFE2 = \
+             AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2;
+    constexpr bool has_LFE_LFE2 = (MASK & LFE_LFE2) == LFE_LFE2;
+
+#pragma push_macro("DO_CHANNEL_POSITION")
+#undef DO_CHANNEL_POSITION
+#define DO_CHANNEL_POSITION(BIT_INDEX) \
+    if constexpr ((MASK & (1 << BIT_INDEX)) != 0) { \
+        constexpr auto side = kSideFromChannelIdx[BIT_INDEX]; \
+        if constexpr (side == AUDIO_GEOMETRY_SIDE_LEFT || \
+               has_LFE_LFE2 && (1 << BIT_INDEX) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY) { \
+            proc(*out++, f(inp(), vol[0])); \
+        } else if constexpr (side == AUDIO_GEOMETRY_SIDE_RIGHT || \
+               has_LFE_LFE2 && (1 << BIT_INDEX) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2) { \
+            proc(*out++, f(inp(), vol[1])); \
+        } else /* constexpr */ { \
+            proc(*out++, f(inp(), center)); \
+        } \
+    }
+
+    DO_CHANNEL_POSITION(0);
+    DO_CHANNEL_POSITION(1);
+    DO_CHANNEL_POSITION(2);
+    DO_CHANNEL_POSITION(3);
+    DO_CHANNEL_POSITION(4);
+    DO_CHANNEL_POSITION(5);
+    DO_CHANNEL_POSITION(6);
+    DO_CHANNEL_POSITION(7);
+
+    DO_CHANNEL_POSITION(8);
+    DO_CHANNEL_POSITION(9);
+    DO_CHANNEL_POSITION(10);
+    DO_CHANNEL_POSITION(11);
+    DO_CHANNEL_POSITION(12);
+    DO_CHANNEL_POSITION(13);
+    DO_CHANNEL_POSITION(14);
+    DO_CHANNEL_POSITION(15);
+
+    DO_CHANNEL_POSITION(16);
+    DO_CHANNEL_POSITION(17);
+    DO_CHANNEL_POSITION(18);
+    DO_CHANNEL_POSITION(19);
+    DO_CHANNEL_POSITION(20);
+    DO_CHANNEL_POSITION(21);
+    DO_CHANNEL_POSITION(22);
+    DO_CHANNEL_POSITION(23);
+    static_assert(FCC_LIMIT <= FCC_24); // Note: this may need to change.
+#pragma pop_macro("DO_CHANNEL_POSITION")
+}
+
+// These are the channel position masks we expect from the HAL.
+// See audio_channel_out_mask_from_count() but this is constexpr
+constexpr inline audio_channel_mask_t canonicalChannelMaskFromCount(size_t channelCount) {
+    constexpr audio_channel_mask_t canonical[] = {
+        [0] = AUDIO_CHANNEL_NONE,
+        [1] = AUDIO_CHANNEL_OUT_MONO,
+        [2] = AUDIO_CHANNEL_OUT_STEREO,
+        [3] = AUDIO_CHANNEL_OUT_2POINT1,
+        [4] = AUDIO_CHANNEL_OUT_QUAD,
+        [5] = AUDIO_CHANNEL_OUT_PENTA,
+        [6] = AUDIO_CHANNEL_OUT_5POINT1,
+        [7] = AUDIO_CHANNEL_OUT_6POINT1,
+        [8] = AUDIO_CHANNEL_OUT_7POINT1,
+        [12] = AUDIO_CHANNEL_OUT_7POINT1POINT4,
+        [24] = AUDIO_CHANNEL_OUT_22POINT2,
+    };
+    return channelCount < std::size(canonical) ? canonical[channelCount] : AUDIO_CHANNEL_NONE;
+}
+
+template <int MIXTYPE, int NCHAN,
+        typename TO, typename TI, typename TV,
+        typename F>
+void stereoVolumeHelper(TO*& out, const TI*& in, const TV *vol, F f) {
+    static_assert(NCHAN > 0 && NCHAN <= FCC_LIMIT);
+    static_assert(MIXTYPE == MIXTYPE_MULTI_STEREOVOL
+            || MIXTYPE == MIXTYPE_MULTI_SAVEONLY_STEREOVOL
+            || MIXTYPE == MIXTYPE_STEREOEXPAND
+            || MIXTYPE == MIXTYPE_MONOEXPAND);
+    constexpr audio_channel_mask_t MASK{canonicalChannelMaskFromCount(NCHAN)};
+    if constexpr (MASK == AUDIO_CHANNEL_NONE) {
+        ALOGE("%s: Invalid position count %d", __func__, NCHAN);
+        return; // not a valid system mask, ignore.
+    }
+    stereoVolumeHelperWithChannelMask<MIXTYPE, MASK, TO, TI, TV, F>(out, in, vol, f);
 }
 
 /*
diff --git a/media/libaudioprocessing/include/media/AudioMixer.h b/media/libaudioprocessing/include/media/AudioMixer.h
index 70eafe3..5a9fa07 100644
--- a/media/libaudioprocessing/include/media/AudioMixer.h
+++ b/media/libaudioprocessing/include/media/AudioMixer.h
@@ -50,6 +50,7 @@
         // for haptic
         HAPTIC_ENABLED  = 0x4007, // Set haptic data from this track should be played or not.
         HAPTIC_INTENSITY = 0x4008, // Set the intensity to play haptic data.
+        HAPTIC_MAX_AMPLITUDE = 0x4009, // Set the max amplitude allowed for haptic data.
         // for target TIMESTRETCH
         PLAYBACK_RATE   = 0x4300, // Configure timestretch on this track name;
                                   // parameter 'value' is a pointer to the new playback rate.
@@ -145,6 +146,7 @@
         // Haptic
         bool                 mHapticPlaybackEnabled;
         os::HapticScale      mHapticIntensity;
+        float                mHapticMaxAmplitude;
         audio_channel_mask_t mHapticChannelMask;
         uint32_t             mHapticChannelCount;
         audio_channel_mask_t mMixerHapticChannelMask;
diff --git a/media/libaudioprocessing/tests/Android.bp b/media/libaudioprocessing/tests/Android.bp
index 3856817..ad402db 100644
--- a/media/libaudioprocessing/tests/Android.bp
+++ b/media/libaudioprocessing/tests/Android.bp
@@ -76,6 +76,7 @@
 //
 cc_binary {
     name: "mixerops_objdump",
+    header_libs: ["libaudioutils_headers"],
     srcs: ["mixerops_objdump.cpp"],
 }
 
@@ -84,6 +85,16 @@
 //
 cc_benchmark {
     name: "mixerops_benchmark",
+    header_libs: ["libaudioutils_headers"],
     srcs: ["mixerops_benchmark.cpp"],
     static_libs: ["libgoogle-benchmark"],
 }
+
+//
+// mixerops unit test
+//
+cc_test {
+    name: "mixerops_tests",
+    defaults: ["libaudioprocessing_test_defaults"],
+    srcs: ["mixerops_tests.cpp"],
+}
diff --git a/media/libaudioprocessing/tests/mixerops_benchmark.cpp b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
index 7a4c5c7..f866b1a 100644
--- a/media/libaudioprocessing/tests/mixerops_benchmark.cpp
+++ b/media/libaudioprocessing/tests/mixerops_benchmark.cpp
@@ -16,11 +16,9 @@
 
 #include <inttypes.h>
 #include <type_traits>
-#include "../../../../system/media/audio_utils/include/audio_utils/primitives.h"
 #define LOG_ALWAYS_FATAL(...)
 
 #include <../AudioMixerOps.h>
-
 #include <benchmark/benchmark.h>
 
 using namespace android;
diff --git a/media/libaudioprocessing/tests/mixerops_tests.cpp b/media/libaudioprocessing/tests/mixerops_tests.cpp
new file mode 100644
index 0000000..2500ba9
--- /dev/null
+++ b/media/libaudioprocessing/tests/mixerops_tests.cpp
@@ -0,0 +1,175 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "mixerop_tests"
+#include <log/log.h>
+
+#include <inttypes.h>
+#include <type_traits>
+
+#include <../AudioMixerOps.h>
+#include <gtest/gtest.h>
+
+using namespace android;
+
+// Note: gtest templated tests require typenames, not integers.
+template <int MIXTYPE, int NCHAN>
+class MixerOpsBasicTest {
+public:
+    static void testStereoVolume() {
+        using namespace android::audio_utils::channels;
+
+        constexpr size_t FRAME_COUNT = 1000;
+        constexpr size_t SAMPLE_COUNT = FRAME_COUNT * NCHAN;
+
+        const float in[SAMPLE_COUNT] = {[0 ... (SAMPLE_COUNT - 1)] = 1.f};
+
+        AUDIO_GEOMETRY_SIDE sides[NCHAN];
+        size_t i = 0;
+        unsigned channel = canonicalChannelMaskFromCount(NCHAN);
+        constexpr unsigned LFE_LFE2 =
+                AUDIO_CHANNEL_OUT_LOW_FREQUENCY | AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2;
+        bool has_LFE_LFE2 = (channel & LFE_LFE2) == LFE_LFE2;
+        while (channel != 0) {
+            const int index = __builtin_ctz(channel);
+            if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY) {
+                sides[i++] = AUDIO_GEOMETRY_SIDE_LEFT; // special case
+            } else if (has_LFE_LFE2 && (1 << index) == AUDIO_CHANNEL_OUT_LOW_FREQUENCY_2) {
+                sides[i++] = AUDIO_GEOMETRY_SIDE_RIGHT; // special case
+            } else {
+                sides[i++] = sideFromChannelIdx(index);
+            }
+            channel &= ~(1 << index);
+        }
+
+        float vola[2] = {1.f, 0.f}; // left volume at max.
+        float out[SAMPLE_COUNT]{};
+        float aux[FRAME_COUNT]{};
+        float volaux = 0.5;
+        {
+            volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vola, volaux);
+            const float *outp = out;
+            const float *auxp = aux;
+            const float left = vola[0];
+            const float center = (vola[0] + vola[1]) * 0.5;
+            const float right = vola[1];
+            for (size_t i = 0; i < FRAME_COUNT; ++i) {
+                for (size_t j = 0; j < NCHAN; ++j) {
+                    const float audio = *outp++;
+                    if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) {
+                        EXPECT_EQ(left, audio);
+                    } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) {
+                        EXPECT_EQ(center, audio);
+                    } else {
+                        EXPECT_EQ(right, audio);
+                    }
+                }
+                EXPECT_EQ(volaux, *auxp++);  // works if all channels contain 1.f
+            }
+        }
+        float volb[2] = {0.f, 0.5f}; // right volume at half max.
+        {
+            // this accumulates into out, aux.
+            // float out[SAMPLE_COUNT]{};
+            // float aux[FRAME_COUNT]{};
+            volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, volb, volaux);
+            const float *outp = out;
+            const float *auxp = aux;
+            const float left = vola[0] + volb[0];
+            const float center = (vola[0] + vola[1] + volb[0] + volb[1]) * 0.5;
+            const float right = vola[1] + volb[1];
+            for (size_t i = 0; i < FRAME_COUNT; ++i) {
+                for (size_t j = 0; j < NCHAN; ++j) {
+                    const float audio = *outp++;
+                    if (sides[j] == AUDIO_GEOMETRY_SIDE_LEFT) {
+                        EXPECT_EQ(left, audio);
+                    } else if (sides[j] == AUDIO_GEOMETRY_SIDE_CENTER) {
+                        EXPECT_EQ(center, audio);
+                    } else {
+                        EXPECT_EQ(right, audio);
+                    }
+                }
+                // aux is accumulated so 2x the amplitude
+                EXPECT_EQ(volaux * 2.f, *auxp++);  // works if all channels contain 1.f
+            }
+        }
+
+        { // test aux as derived from out.
+            // AUX channel is the weighted sum of all of the output channels prior to volume
+            // adjustment.  We must set L and R to the same volume to allow computation
+            // of AUX from the output values.
+            const float volmono = 0.25f;
+            const float vollr[2] = {volmono, volmono}; // all the same.
+            float out[SAMPLE_COUNT]{};
+            float aux[FRAME_COUNT]{};
+            volumeMulti<MIXTYPE, NCHAN>(out, FRAME_COUNT, in, aux, vollr, volaux);
+            const float *outp = out;
+            const float *auxp = aux;
+            for (size_t i = 0; i < FRAME_COUNT; ++i) {
+                float accum = 0.f;
+                for (size_t j = 0; j < NCHAN; ++j) {
+                    accum += *outp++;
+                }
+                EXPECT_EQ(accum / NCHAN * volaux / volmono, *auxp++);
+            }
+        }
+    }
+};
+
+TEST(mixerops, stereovolume_1) { // Note: mono not used for output sinks yet.
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 1>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_2) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 2>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_3) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 3>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_4) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 4>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_5) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 5>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_6) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 6>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_7) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 7>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_8) {
+    MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 8>::testStereoVolume();
+}
+TEST(mixerops, stereovolume_12) {
+    if constexpr (FCC_LIMIT >= 12) { // NOTE: FCC_LIMIT is an enum, so can't #if
+        MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 12>::testStereoVolume();
+    }
+}
+TEST(mixerops, stereovolume_24) {
+    if constexpr (FCC_LIMIT >= 24) {
+        MixerOpsBasicTest<MIXTYPE_MULTI_STEREOVOL, 24>::testStereoVolume();
+    }
+}
+TEST(mixerops, channel_equivalence) {
+    // we must match the constexpr function with the system determined channel mask from count.
+    for (size_t i = 0; i < FCC_LIMIT; ++i) {
+        const audio_channel_mask_t actual = canonicalChannelMaskFromCount(i);
+        const audio_channel_mask_t system = audio_channel_out_mask_from_count(i);
+        if (system == AUDIO_CHANNEL_INVALID) continue;
+        EXPECT_EQ(system, actual);
+    }
+}
diff --git a/media/libeffects/hapticgenerator/Android.bp b/media/libeffects/hapticgenerator/Android.bp
index a660957..03ce329 100644
--- a/media/libeffects/hapticgenerator/Android.bp
+++ b/media/libeffects/hapticgenerator/Android.bp
@@ -45,6 +45,7 @@
 
     shared_libs: [
         "libaudioutils",
+        "libbase",
         "libbinder",
         "liblog",
         "libutils",
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
index 65a20a7..3137e13 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.cpp
@@ -22,12 +22,15 @@
 
 #include <algorithm>
 #include <memory>
+#include <string>
 #include <utility>
 
 #include <errno.h>
 #include <inttypes.h>
 #include <math.h>
 
+#include <android-base/parsedouble.h>
+#include <android-base/properties.h>
 #include <audio_effects/effect_hapticgenerator.h>
 #include <audio_utils/format.h>
 #include <system/audio.h>
@@ -35,6 +38,7 @@
 static constexpr float DEFAULT_RESONANT_FREQUENCY = 150.0f;
 static constexpr float DEFAULT_BSF_ZERO_Q = 8.0f;
 static constexpr float DEFAULT_BSF_POLE_Q = 4.0f;
+static constexpr float DEFAULT_DISTORTION_OUTPUT_GAIN = 1.5f;
 
 // This is the only symbol that needs to be exported
 __attribute__ ((visibility ("default")))
@@ -81,6 +85,15 @@
 
 namespace {
 
+float getFloatProperty(const std::string& key, float defaultValue) {
+    float result;
+    std::string value = android::base::GetProperty(key, "");
+    if (!value.empty() && android::base::ParseFloat(value, &result)) {
+        return result;
+    }
+    return defaultValue;
+}
+
 int HapticGenerator_Init(struct HapticGeneratorContext *context) {
     context->itfe = &gHapticGeneratorInterface;
 
@@ -114,7 +127,9 @@
     context->param.distortionCornerFrequency = 300.0f;
     context->param.distortionInputGain = 0.3f;
     context->param.distortionCubeThreshold = 0.1f;
-    context->param.distortionOutputGain = 1.5f;
+    context->param.distortionOutputGain = getFloatProperty(
+            "vendor.audio.hapticgenerator.distortion.output.gain", DEFAULT_DISTORTION_OUTPUT_GAIN);
+    ALOGD("Using distortion output gain as %f", context->param.distortionOutputGain);
 
     context->state = HAPTICGENERATOR_STATE_INITIALIZED;
     return 0;
@@ -287,15 +302,17 @@
         break;
     }
     case HG_PARAM_VIBRATOR_INFO: {
-        if (value == nullptr || size != 2 * sizeof(float)) {
+        if (value == nullptr || size != 3 * sizeof(float)) {
             return -EINVAL;
         }
         const float resonantFrequency = *(float*) value;
         const float qFactor = *((float *) value + 1);
+        const float maxAmplitude = *((float *) value + 2);
         context->param.resonantFrequency =
                 isnan(resonantFrequency) ? DEFAULT_RESONANT_FREQUENCY : resonantFrequency;
         context->param.bsfZeroQ = isnan(qFactor) ? DEFAULT_BSF_POLE_Q : qFactor;
         context->param.bsfPoleQ = context->param.bsfZeroQ / 2.0f;
+        context->param.maxHapticAmplitude = maxAmplitude;
 
         if (context->processorsRecord.bpf != nullptr) {
             context->processorsRecord.bpf->setCoefficients(
@@ -448,7 +465,8 @@
     float* hapticOutBuffer = HapticGenerator_runProcessingChain(
             context->processingChain, context->inputBuffer.data(),
             context->outputBuffer.data(), inBuffer->frameCount);
-    os::scaleHapticData(hapticOutBuffer, hapticSampleCount, context->param.maxHapticIntensity);
+    os::scaleHapticData(hapticOutBuffer, hapticSampleCount, context->param.maxHapticIntensity,
+                        context->param.maxHapticAmplitude);
 
     // For haptic data, the haptic playback thread will copy the data from effect input buffer,
     // which contains haptic data at the end of the buffer, directly to sink buffer.
diff --git a/media/libeffects/hapticgenerator/EffectHapticGenerator.h b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
index 96b744a..85e961f 100644
--- a/media/libeffects/hapticgenerator/EffectHapticGenerator.h
+++ b/media/libeffects/hapticgenerator/EffectHapticGenerator.h
@@ -51,6 +51,7 @@
     // A map from track id to haptic intensity.
     std::map<int, os::HapticScale> id2Intensity;
     os::HapticScale maxHapticIntensity; // max intensity will be used to scale haptic data.
+    float maxHapticAmplitude; // max amplitude will be used to limit haptic data absolute values.
 
     float resonantFrequency;
     float bpfQ;
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 65a163f..76a0ba7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -336,11 +336,11 @@
 }
 
 // getDefaultVibratorInfo_l must be called with AudioFlinger lock held.
-const media::AudioVibratorInfo* AudioFlinger::getDefaultVibratorInfo_l() {
+std::optional<media::AudioVibratorInfo> AudioFlinger::getDefaultVibratorInfo_l() {
     if (mAudioVibratorInfos.empty()) {
-        return nullptr;
+        return {};
     }
-    return &mAudioVibratorInfos.front();
+    return mAudioVibratorInfos.front();
 }
 
 AudioFlinger::~AudioFlinger()
@@ -2455,6 +2455,10 @@
         ThreadBase *thread = (ThreadBase *)mRecordThreads.valueAt(i).get();
         thread->systemReady();
     }
+    for (size_t i = 0; i < mMmapThreads.size(); i++) {
+        ThreadBase *thread = (ThreadBase *)mMmapThreads.valueAt(i).get();
+        thread->systemReady();
+    }
     return NO_ERROR;
 }
 
@@ -4178,6 +4182,7 @@
         case TransactionCode::LIST_AUDIO_PATCHES:
         case TransactionCode::SET_AUDIO_PORT_CONFIG:
         case TransactionCode::SET_RECORD_SILENCED:
+        case TransactionCode::AUDIO_POLICY_READY:
             ALOGW("%s: transaction %d received from PID %d",
                   __func__, code, IPCThreadState::self()->getCallingPid());
             // return status only for non void methods
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index fff61f8..e3402f9 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -269,6 +269,9 @@
 
     /* Indicate JAVA services are ready (scheduling, power management ...) */
     virtual status_t systemReady();
+    virtual status_t audioPolicyReady() { mAudioPolicyReady.store(true); return NO_ERROR; }
+            bool isAudioPolicyReady() const { return mAudioPolicyReady.load(); }
+
 
     virtual status_t getMicrophones(std::vector<media::MicrophoneInfo> *microphones);
 
@@ -309,7 +312,7 @@
     void updateDownStreamPatches_l(const struct audio_patch *patch,
                                    const std::set<audio_io_handle_t> streams);
 
-    const media::AudioVibratorInfo* getDefaultVibratorInfo_l();
+    std::optional<media::AudioVibratorInfo> getDefaultVibratorInfo_l();
 
 private:
     // FIXME The 400 is temporarily too high until a leak of writers in media.log is fixed.
@@ -986,6 +989,7 @@
     DeviceEffectManager mDeviceEffectManager;
 
     bool       mSystemReady;
+    std::atomic_bool mAudioPolicyReady{};
 
     mediautils::UidInfo mUidInfo;
 
diff --git a/services/audioflinger/DeviceEffectManager.h b/services/audioflinger/DeviceEffectManager.h
index a05f5fe..b260700 100644
--- a/services/audioflinger/DeviceEffectManager.h
+++ b/services/audioflinger/DeviceEffectManager.h
@@ -190,6 +190,10 @@
 
     wp<EffectChain> chain() const override { return nullptr; }
 
+    bool isAudioPolicyReady() const override {
+        return mManager.audioFlinger().isAudioPolicyReady();
+    }
+
     int newEffectId() { return mManager.audioFlinger().nextUniqueId(AUDIO_UNIQUE_ID_USE_EFFECT); }
 
     status_t addEffectToHal(audio_port_handle_t deviceId,
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index b267d88..c12f03b 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -242,6 +242,12 @@
 
     {
         Mutex::Autolock _l(mLock);
+
+        if ((isInternal_l() && !mPolicyRegistered)
+                || !getCallback()->isAudioPolicyReady()) {
+            return NO_ERROR;
+        }
+
         // register effect when first handle is attached and unregister when last handle is removed
         if (mPolicyRegistered != mHandles.size() > 0) {
             doRegister = true;
@@ -1600,7 +1606,7 @@
     return status;
 }
 
-status_t AudioFlinger::EffectModule::setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo)
+status_t AudioFlinger::EffectModule::setVibratorInfo(const media::AudioVibratorInfo& vibratorInfo)
 {
     if (mStatus != NO_ERROR) {
         return mStatus;
@@ -1610,15 +1616,17 @@
         return INVALID_OPERATION;
     }
 
+    const size_t paramCount = 3;
     std::vector<uint8_t> request(
-            sizeof(effect_param_t) + sizeof(int32_t) + 2 * sizeof(float));
+            sizeof(effect_param_t) + sizeof(int32_t) + paramCount * sizeof(float));
     effect_param_t *param = (effect_param_t*) request.data();
     param->psize = sizeof(int32_t);
-    param->vsize = 2 * sizeof(float);
+    param->vsize = paramCount * sizeof(float);
     *(int32_t*)param->data = HG_PARAM_VIBRATOR_INFO;
     float* vibratorInfoPtr = reinterpret_cast<float*>(param->data + sizeof(int32_t));
-    vibratorInfoPtr[0] = vibratorInfo->resonantFrequency;
-    vibratorInfoPtr[1] = vibratorInfo->qFactor;
+    vibratorInfoPtr[0] = vibratorInfo.resonantFrequency;
+    vibratorInfoPtr[1] = vibratorInfo.qFactor;
+    vibratorInfoPtr[2] = vibratorInfo.maxAmplitude;
     std::vector<uint8_t> response;
     status_t status = command(EFFECT_CMD_SET_PARAM, request, sizeof(int32_t), &response);
     if (status == NO_ERROR) {
@@ -2048,11 +2056,11 @@
       mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX),
       mEffectCallback(new EffectCallback(wp<EffectChain>(this), thread))
 {
-    mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
     sp<ThreadBase> p = thread.promote();
     if (p == nullptr) {
         return;
     }
+    mStrategy = p->getStrategyForStream(AUDIO_STREAM_MUSIC);
     mMaxTailBuffers = ((kProcessTailDurationMs * p->sampleRate()) / 1000) /
                                     p->frameCount();
 }
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index a727e04..b6f9758 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -64,6 +64,8 @@
     virtual void resetVolume() = 0;
 
     virtual wp<EffectChain> chain() const = 0;
+
+    virtual bool isAudioPolicyReady() const = 0;
 };
 
 // EffectBase(EffectModule) and EffectChain classes both have their own mutex to protect
@@ -164,6 +166,16 @@
 
     void             dump(int fd, const Vector<String16>& args);
 
+protected:
+    bool             isInternal_l() const {
+                         for (auto handle : mHandles) {
+                            if (handle->client() != nullptr) {
+                                return false;
+                            }
+                         }
+                         return true;
+                     }
+
 private:
     friend class AudioFlinger;      // for mHandles
     bool             mPinned = false;
@@ -259,7 +271,7 @@
     bool             isHapticGenerator() const;
 
     status_t         setHapticIntensity(int id, int intensity);
-    status_t         setVibratorInfo(const media::AudioVibratorInfo* vibratorInfo);
+    status_t         setVibratorInfo(const media::AudioVibratorInfo& vibratorInfo);
 
     void             dump(int fd, const Vector<String16>& args);
 
@@ -342,6 +354,8 @@
     android::binder::Status disconnect() override;
     android::binder::Status getCblk(media::SharedFileRegion* _aidl_return) override;
 
+    sp<Client> client() const { return mClient; }
+
 private:
     void disconnect(bool unpinIfLast);
 
@@ -566,6 +580,10 @@
 
         wp<EffectChain> chain() const override { return mChain; }
 
+        bool isAudioPolicyReady() const override {
+            return mAudioFlinger.isAudioPolicyReady();
+        }
+
         wp<ThreadBase> thread() const { return mThread.load(); }
 
         void setThread(const wp<ThreadBase>& thread) {
@@ -716,6 +734,10 @@
 
         wp<EffectChain> chain() const override { return nullptr; }
 
+        bool isAudioPolicyReady() const override {
+            return mManagerCallback->isAudioPolicyReady();
+        }
+
         int newEffectId();
 
     private:
diff --git a/services/audioflinger/FastMixer.cpp b/services/audioflinger/FastMixer.cpp
index 88d4eaf..fc34d95 100644
--- a/services/audioflinger/FastMixer.cpp
+++ b/services/audioflinger/FastMixer.cpp
@@ -204,6 +204,8 @@
                 (void *)(uintptr_t)fastTrack->mHapticPlaybackEnabled);
         mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_INTENSITY,
                 (void *)(uintptr_t)fastTrack->mHapticIntensity);
+        mMixer->setParameter(index, AudioMixer::TRACK, AudioMixer::HAPTIC_MAX_AMPLITUDE,
+                (void *)(&(fastTrack->mHapticMaxAmplitude)));
 
         mMixer->enable(index);
         break;
diff --git a/services/audioflinger/FastMixerState.h b/services/audioflinger/FastMixerState.h
index 857d3de..ce3cc14 100644
--- a/services/audioflinger/FastMixerState.h
+++ b/services/audioflinger/FastMixerState.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_AUDIO_FAST_MIXER_STATE_H
 #define ANDROID_AUDIO_FAST_MIXER_STATE_H
 
+#include <math.h>
+
 #include <audio_utils/minifloat.h>
 #include <system/audio.h>
 #include <media/AudioMixer.h>
@@ -51,6 +53,7 @@
     int                     mGeneration;     // increment when any field is assigned
     bool                    mHapticPlaybackEnabled = false; // haptic playback is enabled or not
     os::HapticScale         mHapticIntensity = os::HapticScale::MUTE; // intensity of haptic data
+    float                   mHapticMaxAmplitude = NAN; // max amplitude allowed for haptic data
 };
 
 // Represents a single state of the fast mixer
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index 0929055..e9e98ca 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -19,6 +19,8 @@
     #error This header file should only be included from AudioFlinger.h
 #endif
 
+#include <math.h>
+
 // Checks and monitors OP_PLAY_AUDIO
 class OpPlayAudioMonitor : public RefBase {
 public:
@@ -161,6 +163,8 @@
             }
             /** Return at what intensity to play haptics, used in mixer. */
             os::HapticScale getHapticIntensity() const { return mHapticIntensity; }
+            /** Return the maximum amplitude allowed for haptics data, used in mixer. */
+            float getHapticMaxAmplitude() const { return mHapticMaxAmplitude; }
             /** Set intensity of haptic playback, should be set after querying vibrator service. */
             void    setHapticIntensity(os::HapticScale hapticIntensity) {
                 if (os::isValidHapticScale(hapticIntensity)) {
@@ -168,6 +172,12 @@
                     setHapticPlaybackEnabled(mHapticIntensity != os::HapticScale::MUTE);
                 }
             }
+            /** Set maximum amplitude allowed for haptic data, should be set after querying
+             *  vibrator service.
+             */
+            void    setHapticMaxAmplitude(float maxAmplitude) {
+                mHapticMaxAmplitude = maxAmplitude;
+            }
             sp<os::ExternalVibration> getExternalVibration() const { return mExternalVibration; }
 
             void    setTeePatches(TeePatches teePatches);
@@ -282,6 +292,8 @@
     bool                mHapticPlaybackEnabled = false; // indicates haptic playback enabled or not
     // intensity to play haptic data
     os::HapticScale mHapticIntensity = os::HapticScale::MUTE;
+    // max amplitude allowed for haptic data
+    float mHapticMaxAmplitude = NAN;
     class AudioVibrationController : public os::BnExternalVibrationController {
     public:
         explicit AudioVibrationController(Track* track) : mTrack(track) {}
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index b9cdab8..08d2d93 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1477,11 +1477,11 @@
         if (effect->isHapticGenerator()) {
             // TODO(b/184194057): Use the vibrator information from the vibrator that will be used
             // for the HapticGenerator.
-            const media::AudioVibratorInfo* defaultVibratorInfo =
-                    mAudioFlinger->getDefaultVibratorInfo_l();
-            if (defaultVibratorInfo != nullptr) {
+            const std::optional<media::AudioVibratorInfo> defaultVibratorInfo =
+                    std::move(mAudioFlinger->getDefaultVibratorInfo_l());
+            if (defaultVibratorInfo) {
                 // Only set the vibrator info when it is a valid one.
-                effect->setVibratorInfo(defaultVibratorInfo);
+                effect->setVibratorInfo(*defaultVibratorInfo);
             }
         }
         // create effect handle and connect it to effect module
@@ -1888,6 +1888,14 @@
     item->selfrecord();
 }
 
+product_strategy_t AudioFlinger::ThreadBase::getStrategyForStream(audio_stream_type_t stream) const
+{
+    if (!mAudioFlinger->isAudioPolicyReady()) {
+        return PRODUCT_STRATEGY_NONE;
+    }
+    return AudioSystem::getStrategyForStream(stream);
+}
+
 // ----------------------------------------------------------------------------
 //      Playback
 // ----------------------------------------------------------------------------
@@ -2397,11 +2405,11 @@
         // all tracks in same audio session must share the same routing strategy otherwise
         // conflicts will happen when tracks are moved from one output to another by audio policy
         // manager
-        product_strategy_t strategy = AudioSystem::getStrategyForStream(streamType);
+        product_strategy_t strategy = getStrategyForStream(streamType);
         for (size_t i = 0; i < mTracks.size(); ++i) {
             sp<Track> t = mTracks[i];
             if (t != 0 && t->isExternalTrack()) {
-                product_strategy_t actual = AudioSystem::getStrategyForStream(t->streamType());
+                product_strategy_t actual = getStrategyForStream(t->streamType());
                 if (sessionId == t->sessionId() && strategy != actual) {
                     ALOGE("createTrack_l() mismatched strategy; expected %u but found %u",
                             strategy, actual);
@@ -2445,7 +2453,7 @@
         if (chain != 0) {
             ALOGV("createTrack_l() setting main buffer %p", chain->inBuffer());
             track->setMainBuffer(chain->inBuffer());
-            chain->setStrategy(AudioSystem::getStrategyForStream(track->streamType()));
+            chain->setStrategy(getStrategyForStream(track->streamType()));
             chain->incTrackCnt();
         }
 
@@ -2613,8 +2621,19 @@
             mLock.unlock();
             const int intensity = AudioFlinger::onExternalVibrationStart(
                     track->getExternalVibration());
+            std::optional<media::AudioVibratorInfo> vibratorInfo;
+            {
+                // TODO(b/184194780): Use the vibrator information from the vibrator that will be
+                // used to play this track.
+                Mutex::Autolock _l(mAudioFlinger->mLock);
+                vibratorInfo = std::move(mAudioFlinger->getDefaultVibratorInfo_l());
+            }
             mLock.lock();
             track->setHapticIntensity(static_cast<os::HapticScale>(intensity));
+            if (vibratorInfo) {
+                track->setHapticMaxAmplitude(vibratorInfo->maxAmplitude);
+            }
+
             // Haptic playback should be enabled by vibrator service.
             if (track->getHapticPlaybackEnabled()) {
                 // Disable haptic playback of all active track to ensure only
@@ -3051,15 +3070,15 @@
     // session AUDIO_SESSION_OUTPUT_MIX is placed in same strategy as MUSIC stream so that
     // it is moved to correct output by audio policy manager when A2DP is connected or disconnected
     if (sessionId == AUDIO_SESSION_OUTPUT_MIX) {
-        return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
+        return getStrategyForStream(AUDIO_STREAM_MUSIC);
     }
     for (size_t i = 0; i < mTracks.size(); i++) {
         sp<Track> track = mTracks[i];
         if (sessionId == track->sessionId() && !track->isInvalid()) {
-            return AudioSystem::getStrategyForStream(track->streamType());
+            return getStrategyForStream(track->streamType());
         }
     }
-    return AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
+    return getStrategyForStream(AUDIO_STREAM_MUSIC);
 }
 
 
@@ -4566,6 +4585,7 @@
         fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
         fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
         fastTrack->mHapticIntensity = os::HapticScale::NONE;
+        fastTrack->mHapticMaxAmplitude = NAN;
         fastTrack->mGeneration++;
         state->mFastTracksGen++;
         state->mTrackMask = 1;
@@ -5103,6 +5123,7 @@
                     fastTrack->mFormat = track->mFormat;
                     fastTrack->mHapticPlaybackEnabled = track->getHapticPlaybackEnabled();
                     fastTrack->mHapticIntensity = track->getHapticIntensity();
+                    fastTrack->mHapticMaxAmplitude = track->getHapticMaxAmplitude();
                     fastTrack->mGeneration++;
                     state->mTrackMask |= 1 << j;
                     didModify = true;
@@ -5425,6 +5446,10 @@
                 trackId,
                 AudioMixer::TRACK,
                 AudioMixer::HAPTIC_INTENSITY, (void *)(uintptr_t)track->getHapticIntensity());
+            mAudioMixer->setParameter(
+                trackId,
+                AudioMixer::TRACK,
+                AudioMixer::HAPTIC_MAX_AMPLITUDE, (void *)(&(track->mHapticMaxAmplitude)));
 
             // reset retry count
             track->mRetryCount = kMaxTrackRetries;
@@ -9278,7 +9303,7 @@
     mActiveTracks.add(track);
     sp<EffectChain> chain = getEffectChain_l(mSessionId);
     if (chain != 0) {
-        chain->setStrategy(AudioSystem::getStrategyForStream(streamType()));
+        chain->setStrategy(getStrategyForStream(streamType()));
         chain->incTrackCnt();
         chain->incActiveTrackCnt();
     }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 16082a9..b9dbc7f 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -574,6 +574,8 @@
                                 return INVALID_OPERATION;
                             }
 
+                product_strategy_t getStrategyForStream(audio_stream_type_t stream) const;
+
     virtual     void        dumpInternals_l(int fd __unused, const Vector<String16>& args __unused)
                             { }
     virtual     void        dumpTracks_l(int fd __unused, const Vector<String16>& args __unused) { }
diff --git a/services/audiopolicy/service/AudioPolicyService.cpp b/services/audiopolicy/service/AudioPolicyService.cpp
index 4d0e1f1..3c757b3 100644
--- a/services/audiopolicy/service/AudioPolicyService.cpp
+++ b/services/audiopolicy/service/AudioPolicyService.cpp
@@ -127,6 +127,7 @@
         loadAudioPolicyManager();
         mAudioPolicyManager = mCreateAudioPolicyManager(mAudioPolicyClient);
     }
+
     // load audio processing modules
     sp<AudioPolicyEffects> audioPolicyEffects = new AudioPolicyEffects();
     sp<UidPolicy> uidPolicy = new UidPolicy(this);
@@ -139,6 +140,8 @@
     }
     uidPolicy->registerSelf();
     sensorPrivacyPolicy->registerSelf();
+
+    AudioSystem::audioPolicyReady();
 }
 
 void AudioPolicyService::unloadAudioPolicyManager()
diff --git a/services/mediametrics/Android.bp b/services/mediametrics/Android.bp
index 5989181..0351d2d 100644
--- a/services/mediametrics/Android.bp
+++ b/services/mediametrics/Android.bp
@@ -148,7 +148,8 @@
         "statsd_mediaparser.cpp",
         "statsd_nuplayer.cpp",
         "statsd_recorder.cpp",
-        "StringUtils.cpp"
+        "StringUtils.cpp",
+        "ValidateId.cpp",
     ],
 
     proto: {
diff --git a/services/mediametrics/AudioAnalytics.cpp b/services/mediametrics/AudioAnalytics.cpp
index 11ec993..a0dcb55 100644
--- a/services/mediametrics/AudioAnalytics.cpp
+++ b/services/mediametrics/AudioAnalytics.cpp
@@ -28,6 +28,7 @@
 #include "AudioTypes.h"           // string to int conversions
 #include "MediaMetricsService.h"  // package info
 #include "StringUtils.h"
+#include "ValidateId.h"
 
 #define PROP_AUDIO_ANALYTICS_CLOUD_ENABLED "persist.audio.analytics.cloud.enabled"
 
@@ -562,7 +563,7 @@
         const auto flagsForStats = types::lookup<types::INPUT_FLAG, short_enum_type_t>(flags);
         const auto sourceForStats = types::lookup<types::SOURCE_TYPE, short_enum_type_t>(source);
         // Android S
-        const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId);
+        const auto logSessionIdForStats = ValidateId::get()->validateId(logSessionId);
 
         LOG(LOG_LEVEL) << "key:" << key
               << " id:" << id
@@ -717,7 +718,7 @@
                  types::lookup<types::TRACK_TRAITS, short_enum_type_t>(traits);
         const auto usageForStats = types::lookup<types::USAGE, short_enum_type_t>(usage);
         // Android S
-        const auto logSessionIdForStats = stringutils::sanitizeLogSessionId(logSessionId);
+        const auto logSessionIdForStats = ValidateId::get()->validateId(logSessionId);
 
         LOG(LOG_LEVEL) << "key:" << key
               << " id:" << id
diff --git a/services/mediametrics/LruSet.h b/services/mediametrics/LruSet.h
new file mode 100644
index 0000000..1f0ab60
--- /dev/null
+++ b/services/mediametrics/LruSet.h
@@ -0,0 +1,123 @@
+/*
+ * 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 <list>
+#include <sstream>
+#include <unordered_map>
+
+namespace android::mediametrics {
+
+/**
+ * LruSet keeps a set of the last "Size" elements added or accessed.
+ *
+ * (Lru stands for least-recently-used eviction policy).
+ *
+ * Runs in O(1) time for add, remove, and check.  Internally implemented
+ * with an unordered_map and a list.  In order to remove elements,
+ * a list iterator is stored in the unordered_map
+ * (noting that std::list::erase() contractually
+ * does not affect iterators other than the one erased).
+ */
+
+template <typename T>
+class LruSet {
+    const size_t mMaxSize;
+    std::list<T> mAccessOrder;                 // front is the most recent, back is the oldest.
+    // item T with its access order iterator.
+    std::unordered_map<T, typename std::list<T>::iterator> mMap;
+
+public:
+    /**
+     * Constructs a LruSet which checks whether the element was
+     * accessed or added recently.
+     *
+     * The parameter maxSize is used to cap growth of LruSet;
+     * eviction is based on least recently used LRU.
+     * If maxSize is zero, the LruSet contains no elements
+     * and check() always returns false.
+     *
+     * \param maxSize the maximum number of elements that are tracked.
+     */
+    explicit LruSet(size_t maxSize) : mMaxSize(maxSize) {}
+
+    /**
+     * Returns the number of entries in the LruSet.
+     *
+     * This is a number between 0 and maxSize.
+     */
+    size_t size() const {
+        return mMap.size();
+    }
+
+    /** Clears the container contents. */
+    void clear() {
+        mMap.clear();
+        mAccessOrder.clear();
+    }
+
+    /** Returns a string dump of the last n entries. */
+    std::string dump(size_t n) const {
+        std::stringstream ss;
+        auto it = mAccessOrder.cbegin();
+        for (size_t i = 0; i < n && it != mAccessOrder.cend(); ++i) {
+            ss << *it++ << "\n";
+        }
+        return ss.str();
+    }
+
+    /** Adds a new item to the set. */
+    void add(const T& t) {
+        if (mMaxSize == 0) return;
+        auto it = mMap.find(t);
+        if (it != mMap.end()) { // already exists.
+            mAccessOrder.erase(it->second);  // move-to-front on the chronologically ordered list.
+        } else if (mAccessOrder.size() >= mMaxSize) {
+            const T last = mAccessOrder.back();
+            mAccessOrder.pop_back();
+            mMap.erase(last);
+        }
+        mAccessOrder.push_front(t);
+        mMap[t] = mAccessOrder.begin();
+    }
+
+    /**
+     * Removes an item from the set.
+     *
+     * \param t item to be removed.
+     * \return false if the item doesn't exist.
+     */
+    bool remove(const T& t) {
+        auto it = mMap.find(t);
+        if (it == mMap.end()) return false;
+        mAccessOrder.erase(it->second);
+        mMap.erase(it);
+        return true;
+    }
+
+    /** Returns true if t is present (and moves the access order of t to the front). */
+    bool check(const T& t) { // not const, as it adjusts the least-recently-used order.
+        auto it = mMap.find(t);
+        if (it == mMap.end()) return false;
+        mAccessOrder.erase(it->second);
+        mAccessOrder.push_front(it->first);
+        it->second = mAccessOrder.begin();
+        return true;
+    }
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/MediaMetricsService.cpp b/services/mediametrics/MediaMetricsService.cpp
index 1d64878..35e0ae4 100644
--- a/services/mediametrics/MediaMetricsService.cpp
+++ b/services/mediametrics/MediaMetricsService.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "MediaMetricsService.h"
+#include "ValidateId.h"
 #include "iface_statsd.h"
 
 #include <pwd.h> //getpwuid
@@ -204,6 +205,15 @@
     // now attach either the item or its dup to a const shared pointer
     std::shared_ptr<const mediametrics::Item> sitem(release ? item : item->dup());
 
+    // register log session ids with singleton.
+    if (startsWith(item->getKey(), "metrics.manager")) {
+        std::string logSessionId;
+        if (item->get("logSessionId", &logSessionId)
+                && mediametrics::stringutils::isLogSessionId(logSessionId.c_str())) {
+            mediametrics::ValidateId::get()->registerId(logSessionId);
+        }
+    }
+
     (void)mAudioAnalytics.submit(sitem, isTrusted);
 
     (void)dump2Statsd(sitem, mStatsdLog);  // failure should be logged in function.
@@ -309,6 +319,9 @@
                 result << "-- some lines may be truncated --\n";
             }
 
+            result << "LogSessionId:\n"
+                   << mediametrics::ValidateId::get()->dump();
+
             // Dump the statsd atoms we sent out.
             result << "Statsd atoms:\n"
                    << mStatsdLog->dumpToString("  " /* prefix */,
diff --git a/services/mediametrics/ValidateId.cpp b/services/mediametrics/ValidateId.cpp
new file mode 100644
index 0000000..0cc8593
--- /dev/null
+++ b/services/mediametrics/ValidateId.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaMetricsService"  // not ValidateId
+#include <utils/Log.h>
+
+#include "ValidateId.h"
+
+namespace android::mediametrics {
+
+std::string ValidateId::dump() const
+{
+    std::stringstream ss;
+    ss << "Entries:" << mIdSet.size() << "  InvalidIds:" << mInvalidIds << "\n";
+    ss << mIdSet.dump(10);
+    return ss.str();
+}
+
+void ValidateId::registerId(const std::string& id)
+{
+    if (id.empty()) return;
+    if (!mediametrics::stringutils::isLogSessionId(id.c_str())) {
+        ALOGW("%s: rejecting malformed id %s", __func__, id.c_str());
+        return;
+    }
+    ALOGV("%s: registering %s", __func__, id.c_str());
+    mIdSet.add(id);
+}
+
+const std::string& ValidateId::validateId(const std::string& id)
+{
+    static const std::string empty{};
+    if (id.empty()) return empty;
+
+    // reject because the id is malformed
+    if (!mediametrics::stringutils::isLogSessionId(id.c_str())) {
+        ALOGW("%s: rejecting malformed id %s", __func__, id.c_str());
+        ++mInvalidIds;
+        return empty;
+    }
+
+    // reject because the id is unregistered
+    if (!mIdSet.check(id)) {
+        ALOGW("%s: rejecting unregistered id %s", __func__, id.c_str());
+        ++mInvalidIds;
+        return empty;
+    }
+    return id;
+}
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/ValidateId.h b/services/mediametrics/ValidateId.h
new file mode 100644
index 0000000..166b39a
--- /dev/null
+++ b/services/mediametrics/ValidateId.h
@@ -0,0 +1,80 @@
+/*
+ * 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 "LruSet.h"
+#include "StringUtils.h"
+#include "Wrap.h"
+
+namespace android::mediametrics {
+
+/*
+ * ValidateId is used to check whether the log session id is properly formed
+ * and has been registered (i.e. from the Java MediaMetricsManagerService).
+ *
+ * The default memory window to track registered ids is set to SINGLETON_LRU_SET_SIZE.
+ *
+ * This class is not thread-safe, but the singleton returned by get() uses LockWrap<>
+ * to ensure thread-safety.
+ */
+class ValidateId {
+    mediametrics::LruSet<std::string> mIdSet;
+    size_t mInvalidIds = 0;  // count invalid ids encountered.
+public:
+    /** Creates a ValidateId object with size memory window. */
+    explicit ValidateId(size_t size) : mIdSet{size} {}
+
+    /** Returns a string dump of recent contents and stats. */
+    std::string dump() const;
+
+    /**
+     * Registers the id string.
+     *
+     * If id string is malformed (not 16 Base64Url chars), it is ignored.
+     * Once registered, calling validateId() will return id (instead of the empty string).
+     * ValidateId may "forget" the id after not encountering it within the past N ids,
+     * where N is the size set in the constructor.
+     *
+     * param id string (from MediaMetricsManagerService).
+     */
+    void registerId(const std::string& id);
+
+    /**
+     * Returns the empty string if id string is malformed (not 16 Base64Url chars)
+     * or if id string has not been seen (in the recent size ids);
+     * otherwise it returns the same id parameter.
+     *
+     * \param id string (to be sent to statsd).
+     */
+    const std::string& validateId(const std::string& id);
+
+    /** Singleton set size */
+    static inline constexpr size_t SINGLETON_LRU_SET_SIZE = 2000;
+
+    using LockedValidateId = mediametrics::LockWrap<ValidateId>;
+    /**
+     * Returns a singleton locked ValidateId object that is thread-safe using LockWrap<>.
+     *
+     * The Singleton ValidateId object is created with size LRU_SET_SIZE (during first call).
+     */
+    static inline LockedValidateId& get() {
+        static LockedValidateId privateSet{SINGLETON_LRU_SET_SIZE};
+        return privateSet;
+    }
+};
+
+} // namespace android::mediametrics
diff --git a/services/mediametrics/statsd_audiorecord.cpp b/services/mediametrics/statsd_audiorecord.cpp
index 41efcaa..c53b6f3 100644
--- a/services/mediametrics/statsd_audiorecord.cpp
+++ b/services/mediametrics/statsd_audiorecord.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
@@ -143,8 +143,7 @@
     // log_session_id (string)
     std::string logSessionId;
     (void)item->getString("android.media.audiorecord.logSessionId", &logSessionId);
-    const auto log_session_id =
-            mediametrics::stringutils::sanitizeLogSessionId(logSessionId);
+    const auto log_session_id = mediametrics::ValidateId::get()->validateId(logSessionId);
 
     android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
     int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIORECORD_REPORTED,
diff --git a/services/mediametrics/statsd_audiotrack.cpp b/services/mediametrics/statsd_audiotrack.cpp
index 59627ae..707effd 100644
--- a/services/mediametrics/statsd_audiotrack.cpp
+++ b/services/mediametrics/statsd_audiotrack.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
@@ -137,8 +137,7 @@
     // log_session_id (string)
     std::string logSessionId;
     (void)item->getString("android.media.audiotrack.logSessionId", &logSessionId);
-    const auto log_session_id =
-            mediametrics::stringutils::sanitizeLogSessionId(logSessionId);
+    const auto log_session_id = mediametrics::ValidateId::get()->validateId(logSessionId);
 
     android::util::BytesField bf_serialized( serialized.c_str(), serialized.size());
     int result = android::util::stats_write(android::util::MEDIAMETRICS_AUDIOTRACK_REPORTED,
diff --git a/services/mediametrics/statsd_codec.cpp b/services/mediametrics/statsd_codec.cpp
index 46cbdc8..8581437 100644
--- a/services/mediametrics/statsd_codec.cpp
+++ b/services/mediametrics/statsd_codec.cpp
@@ -34,7 +34,7 @@
 
 #include "cleaner.h"
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
@@ -228,7 +228,7 @@
 
     std::string sessionId;
     if (item->getString("android.media.mediacodec.log-session-id", &sessionId)) {
-        sessionId = mediametrics::stringutils::sanitizeLogSessionId(sessionId);
+        sessionId = mediametrics::ValidateId::get()->validateId(sessionId);
         metrics_proto.set_log_session_id(sessionId);
     }
     AStatsEvent_writeString(event, codec.c_str());
diff --git a/services/mediametrics/statsd_extractor.cpp b/services/mediametrics/statsd_extractor.cpp
index bcf2e0a..a8bfeaa 100644
--- a/services/mediametrics/statsd_extractor.cpp
+++ b/services/mediametrics/statsd_extractor.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
@@ -86,7 +86,7 @@
 
     std::string log_session_id;
     if (item->getString("android.media.mediaextractor.logSessionId", &log_session_id)) {
-        log_session_id = mediametrics::stringutils::sanitizeLogSessionId(log_session_id);
+        log_session_id = mediametrics::ValidateId::get()->validateId(log_session_id);
         metrics_proto.set_log_session_id(log_session_id);
     }
 
diff --git a/services/mediametrics/statsd_mediaparser.cpp b/services/mediametrics/statsd_mediaparser.cpp
index 921b320..67ca874b 100644
--- a/services/mediametrics/statsd_mediaparser.cpp
+++ b/services/mediametrics/statsd_mediaparser.cpp
@@ -31,7 +31,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/enums/stats/mediametrics/mediametrics.pb.h"
 #include "iface_statsd.h"
 
@@ -81,7 +81,7 @@
 
     std::string logSessionId;
     item->getString("android.media.mediaparser.logSessionId", &logSessionId);
-    logSessionId = mediametrics::stringutils::sanitizeLogSessionId(logSessionId);
+    logSessionId = mediametrics::ValidateId::get()->validateId(logSessionId);
 
     int result = android::util::stats_write(android::util::MEDIAMETRICS_MEDIAPARSER_REPORTED,
                                timestamp_nanos,
diff --git a/services/mediametrics/statsd_recorder.cpp b/services/mediametrics/statsd_recorder.cpp
index b29ad73..5f54a68 100644
--- a/services/mediametrics/statsd_recorder.cpp
+++ b/services/mediametrics/statsd_recorder.cpp
@@ -32,7 +32,7 @@
 #include <statslog.h>
 
 #include "MediaMetricsService.h"
-#include "StringUtils.h"
+#include "ValidateId.h"
 #include "frameworks/proto_logging/stats/message/mediametrics_message.pb.h"
 #include "iface_statsd.h"
 
@@ -59,7 +59,7 @@
     // string kRecorderLogSessionId = "android.media.mediarecorder.log-session-id";
     std::string log_session_id;
     if (item->getString("android.media.mediarecorder.log-session-id", &log_session_id)) {
-        log_session_id = mediametrics::stringutils::sanitizeLogSessionId(log_session_id);
+        log_session_id = mediametrics::ValidateId::get()->validateId(log_session_id);
         metrics_proto.set_log_session_id(log_session_id);
     }
     // string kRecorderAudioMime = "android.media.mediarecorder.audio.mime";
diff --git a/services/mediametrics/tests/mediametrics_tests.cpp b/services/mediametrics/tests/mediametrics_tests.cpp
index 2336d6f..69ec947 100644
--- a/services/mediametrics/tests/mediametrics_tests.cpp
+++ b/services/mediametrics/tests/mediametrics_tests.cpp
@@ -28,6 +28,7 @@
 
 #include "AudioTypes.h"
 #include "StringUtils.h"
+#include "ValidateId.h"
 
 using namespace android;
 
@@ -1127,3 +1128,100 @@
    validId2[3] = '!';
    ASSERT_EQ("", mediametrics::stringutils::sanitizeLogSessionId(validId2));
 }
+
+TEST(mediametrics_tests, LruSet) {
+    constexpr size_t LRU_SET_SIZE = 2;
+    mediametrics::LruSet<std::string> lruSet(LRU_SET_SIZE);
+
+    // test adding a couple strings.
+    lruSet.add("abc");
+    ASSERT_EQ(1u, lruSet.size());
+    ASSERT_TRUE(lruSet.check("abc"));
+    lruSet.add("def");
+    ASSERT_EQ(2u, lruSet.size());
+
+    // now adding the third string causes eviction of the oldest.
+    lruSet.add("ghi");
+    ASSERT_FALSE(lruSet.check("abc"));
+    ASSERT_TRUE(lruSet.check("ghi"));
+    ASSERT_TRUE(lruSet.check("def"));  // "def" is most recent.
+    ASSERT_EQ(2u, lruSet.size());      // "abc" is correctly discarded.
+
+    // adding another string will evict the oldest.
+    lruSet.add("foo");
+    ASSERT_FALSE(lruSet.check("ghi")); // note: "ghi" discarded when "foo" added.
+    ASSERT_TRUE(lruSet.check("foo"));
+    ASSERT_TRUE(lruSet.check("def"));
+
+    // manual removing of a string works, too.
+    ASSERT_TRUE(lruSet.remove("def"));
+    ASSERT_FALSE(lruSet.check("def")); // we manually removed "def".
+    ASSERT_TRUE(lruSet.check("foo"));  // "foo" is still there.
+    ASSERT_EQ(1u, lruSet.size());
+
+    // you can't remove a string that has not been added.
+    ASSERT_FALSE(lruSet.remove("bar")); // Note: "bar" doesn't exist, so remove returns false.
+    ASSERT_EQ(1u, lruSet.size());
+
+    lruSet.add("foo");   // adding "foo" (which already exists) doesn't change size.
+    ASSERT_EQ(1u, lruSet.size());
+    lruSet.add("bar");   // add "bar"
+    ASSERT_EQ(2u, lruSet.size());
+    lruSet.add("glorp"); // add "glorp" evicts "foo".
+    ASSERT_EQ(2u, lruSet.size());
+    ASSERT_TRUE(lruSet.check("bar"));
+    ASSERT_TRUE(lruSet.check("glorp"));
+    ASSERT_FALSE(lruSet.check("foo"));
+}
+
+TEST(mediametrics_tests, LruSet0) {
+    constexpr size_t LRU_SET_SIZE = 0;
+    mediametrics::LruSet<std::string> lruSet(LRU_SET_SIZE);
+
+    lruSet.add("a");
+    ASSERT_EQ(0u, lruSet.size());
+    ASSERT_FALSE(lruSet.check("a"));
+    ASSERT_FALSE(lruSet.remove("a")); // never added.
+    ASSERT_EQ(0u, lruSet.size());
+}
+
+// Returns a 16 Base64Url string representing the decimal representation of value
+// (with leading 0s) e.g. 0000000000000000, 0000000000000001, 0000000000000002, ...
+static std::string generateId(size_t value)
+{
+    char id[16 + 1]; // to be filled with 16 Base64Url chars (and zero termination)
+    char *sptr = id + 16; // start at the end.
+    *sptr-- = 0; // zero terminate.
+    // output the digits from least significant to most significant.
+    while (value) {
+        *sptr-- = value % 10;
+        value /= 10;
+    }
+    // add leading 0's
+    while (sptr > id) {
+        *sptr-- = '0';
+    }
+    return std::string(id);
+}
+
+TEST(mediametrics_tests, ValidateId) {
+    constexpr size_t LRU_SET_SIZE = 3;
+    constexpr size_t IDS = 10;
+    static_assert(IDS > LRU_SET_SIZE);  // IDS must be greater than LRU_SET_SIZE.
+    mediametrics::ValidateId validateId(LRU_SET_SIZE);
+
+
+    // register IDs as integer strings counting from 0.
+    for (size_t i = 0; i < IDS; ++i) {
+        validateId.registerId(generateId(i));
+    }
+
+    // only the last LRU_SET_SIZE exist.
+    for (size_t i = 0; i < IDS - LRU_SET_SIZE; ++i) {
+        ASSERT_EQ("", validateId.validateId(generateId(i)));
+    }
+    for (size_t i = IDS - LRU_SET_SIZE; i < IDS; ++i) {
+        const std::string id = generateId(i);
+        ASSERT_EQ(id, validateId.validateId(id));
+    }
+}