AudioFlinger: Add datapath subproject

Test: ALLOW_LOCAL_TIDY_TRUE=1 mm -j .
Bug: 287521708
Merged-In: I1fbe85ff5c5fc58a3e49fa137b4247b33f919ecb
Change-Id: I1fbe85ff5c5fc58a3e49fa137b4247b33f919ecb
(cherry picked from commit 76913a08cba261ea59ca436ee99ff1ea3f2de6e8)
diff --git a/services/audioflinger/datapath/Android.bp b/services/audioflinger/datapath/Android.bp
new file mode 100644
index 0000000..58f0422
--- /dev/null
+++ b/services/audioflinger/datapath/Android.bp
@@ -0,0 +1,64 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_av_services_audioflinger_license"],
+}
+
+audioflinger_datapath_tidy_errors = audioflinger_base_tidy_errors + [
+    "modernize-avoid-c-arrays",
+    "modernize-deprecated-headers",
+    "modernize-pass-by-value",
+    "modernize-use-auto",
+    "modernize-use-nodiscard",
+
+    // TODO(b/275642749) Reenable these warnings
+    "-misc-non-private-member-variables-in-classes",
+]
+
+// Eventually use common tidy defaults
+cc_defaults {
+    name: "audioflinger_datapath_flags_defaults",
+    // https://clang.llvm.org/docs/UsersManual.html#command-line-options
+    // https://clang.llvm.org/docs/DiagnosticsReference.html
+    cflags: audioflinger_base_cflags,
+    // https://clang.llvm.org/extra/clang-tidy/
+    tidy: true,
+    tidy_checks: audioflinger_datapath_tidy_errors,
+    tidy_checks_as_errors: audioflinger_datapath_tidy_errors,
+    tidy_flags: [
+      "-format-style=file",
+    ],
+}
+
+cc_library {
+    name: "libaudioflinger_datapath",
+
+    defaults: [
+        "audioflinger_datapath_flags_defaults",
+        "latest_android_media_audio_common_types_cpp_shared",
+    ],
+
+    srcs: [
+        "AudioHwDevice.cpp",
+        "AudioStreamOut.cpp",
+        "SpdifStreamOut.cpp",
+    ],
+
+    header_libs: [
+        "libaudiohal_headers",
+        "liberror_headers",
+    ],
+
+    shared_libs: [
+        "audioclient-types-aidl-cpp",
+        "av-types-aidl-cpp",
+        "libaudiospdif",
+        "libaudioutils",
+        "libbase",
+        "liblog",
+        "libutils", // refbase
+    ],
+}
diff --git a/services/audioflinger/datapath/AudioHwDevice.cpp b/services/audioflinger/datapath/AudioHwDevice.cpp
new file mode 100644
index 0000000..9ff316c
--- /dev/null
+++ b/services/audioflinger/datapath/AudioHwDevice.cpp
@@ -0,0 +1,122 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioHwDevice"
+//#define LOG_NDEBUG 0
+
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include <audio_utils/spdif/SPDIFEncoder.h>
+
+#include "AudioHwDevice.h"
+#include "AudioStreamOut.h"
+#include "SpdifStreamOut.h"
+
+namespace android {
+
+using media::audio::common::AudioMMapPolicyInfo;
+using media::audio::common::AudioMMapPolicyType;
+
+// ----------------------------------------------------------------------------
+
+status_t AudioHwDevice::openOutputStream(
+        AudioStreamOut **ppStreamOut,
+        audio_io_handle_t handle,
+        audio_devices_t deviceType,
+        audio_output_flags_t flags,
+        struct audio_config *config,
+        const char *address)
+{
+
+    struct audio_config originalConfig = *config;
+    auto outputStream = new AudioStreamOut(this, flags);
+
+    // Try to open the HAL first using the current format.
+    ALOGV("openOutputStream(), try "
+            " sampleRate %d, Format %#x, "
+            "channelMask %#x",
+            config->sample_rate,
+            config->format,
+            config->channel_mask);
+    status_t status = outputStream->open(handle, deviceType, config, address);
+
+    if (status != NO_ERROR) {
+        delete outputStream;
+        outputStream = nullptr;
+
+        // FIXME Look at any modification to the config.
+        // The HAL might modify the config to suggest a wrapped format.
+        // Log this so we can see what the HALs are doing.
+        ALOGI("openOutputStream(), HAL returned"
+            " sampleRate %d, Format %#x, "
+            "channelMask %#x, status %d",
+            config->sample_rate,
+            config->format,
+            config->channel_mask,
+            status);
+
+        // If the data is encoded then try again using wrapped PCM.
+        const bool wrapperNeeded = !audio_has_proportional_frames(originalConfig.format)
+                && ((flags & AUDIO_OUTPUT_FLAG_DIRECT) != 0)
+                && ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) == 0);
+
+        if (wrapperNeeded) {
+            if (SPDIFEncoder::isFormatSupported(originalConfig.format)) {
+                outputStream = new SpdifStreamOut(this, flags, originalConfig.format);
+                status = outputStream->open(handle, deviceType, &originalConfig, address);
+                if (status != NO_ERROR) {
+                    ALOGE("ERROR - openOutputStream(), SPDIF open returned %d",
+                        status);
+                    delete outputStream;
+                    outputStream = nullptr;
+                }
+            } else {
+                ALOGE("ERROR - openOutputStream(), SPDIFEncoder does not support format 0x%08x",
+                    originalConfig.format);
+            }
+        }
+    }
+
+    *ppStreamOut = outputStream;
+    return status;
+}
+
+bool AudioHwDevice::supportsAudioPatches() const {
+    bool result;
+    return mHwDevice->supportsAudioPatches(&result) == OK ? result : false;
+}
+
+status_t AudioHwDevice::getAudioPort(struct audio_port_v7 *port) const {
+    return mHwDevice->getAudioPort(port);
+}
+
+status_t AudioHwDevice::getMmapPolicyInfos(
+            AudioMMapPolicyType policyType, std::vector<AudioMMapPolicyInfo> *policyInfos) const {
+    return mHwDevice->getMmapPolicyInfos(policyType, policyInfos);
+}
+
+int32_t AudioHwDevice::getAAudioMixerBurstCount() const {
+    return mHwDevice->getAAudioMixerBurstCount();
+}
+
+int32_t AudioHwDevice::getAAudioHardwareBurstMinUsec() const {
+    return mHwDevice->getAAudioHardwareBurstMinUsec();
+}
+
+
+}; // namespace android
diff --git a/services/audioflinger/datapath/AudioHwDevice.h b/services/audioflinger/datapath/AudioHwDevice.h
new file mode 100644
index 0000000..f9cb80e
--- /dev/null
+++ b/services/audioflinger/datapath/AudioHwDevice.h
@@ -0,0 +1,113 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_HW_DEVICE_H
+#define ANDROID_AUDIO_HW_DEVICE_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <android/media/audio/common/AudioMMapPolicyInfo.h>
+#include <android/media/audio/common/AudioMMapPolicyType.h>
+#include <media/audiohal/DeviceHalInterface.h>
+#include <utils/Errors.h>
+#include <system/audio.h>
+
+namespace android {
+
+class AudioStreamOut;
+
+class AudioHwDevice {
+public:
+    enum Flags {
+        AHWD_CAN_SET_MASTER_VOLUME  = 0x1,
+        AHWD_CAN_SET_MASTER_MUTE    = 0x2,
+        // Means that this isn't a terminal module, and software patches
+        // are used to transport audio data further.
+        AHWD_IS_INSERT              = 0x4,
+        // This Module supports BT Latency mode control
+        AHWD_SUPPORTS_BT_LATENCY_MODES = 0x8,
+    };
+
+    AudioHwDevice(audio_module_handle_t handle,
+                  const char *moduleName,
+                  const sp<DeviceHalInterface>& hwDevice,
+                  Flags flags)
+        : mHandle(handle)
+        , mModuleName(strdup(moduleName))
+        , mHwDevice(hwDevice)
+        , mFlags(flags) { }
+    virtual ~AudioHwDevice() { free((void *)mModuleName); }
+
+    [[nodiscard]] bool canSetMasterVolume() const {
+        return (0 != (mFlags & AHWD_CAN_SET_MASTER_VOLUME));
+    }
+
+    [[nodiscard]] bool canSetMasterMute() const {
+        return (0 != (mFlags & AHWD_CAN_SET_MASTER_MUTE));
+    }
+
+    [[nodiscard]] bool isInsert() const {
+        return (0 != (mFlags & AHWD_IS_INSERT));
+    }
+
+    [[nodiscard]] bool supportsBluetoothVariableLatency() const {
+        return (0 != (mFlags & AHWD_SUPPORTS_BT_LATENCY_MODES));
+    }
+
+   [[nodiscard]] audio_module_handle_t handle() const { return mHandle; }
+   [[nodiscard]] const char *moduleName() const { return mModuleName; }
+   [[nodiscard]] sp<DeviceHalInterface> hwDevice() const { return mHwDevice; }
+
+    /** This method creates and opens the audio hardware output stream.
+     * The "address" parameter qualifies the "devices" audio device type if needed.
+     * The format format depends on the device type:
+     * - Bluetooth devices use the MAC address of the device in the form "00:11:22:AA:BB:CC"
+     * - USB devices use the ALSA card and device numbers in the form  "card=X;device=Y"
+     * - Other devices may use a number or any other string.
+     */
+    status_t openOutputStream(
+            AudioStreamOut **ppStreamOut,
+            audio_io_handle_t handle,
+            audio_devices_t deviceType,
+            audio_output_flags_t flags,
+            struct audio_config *config,
+            const char *address);
+
+    [[nodiscard]] bool supportsAudioPatches() const;
+
+    [[nodiscard]] status_t getAudioPort(struct audio_port_v7 *port) const;
+
+    [[nodiscard]] status_t getMmapPolicyInfos(
+            media::audio::common::AudioMMapPolicyType policyType,
+            std::vector<media::audio::common::AudioMMapPolicyInfo> *policyInfos) const;
+
+    [[nodiscard]] int32_t getAAudioMixerBurstCount() const;
+
+    [[nodiscard]] int32_t getAAudioHardwareBurstMinUsec() const;
+
+private:
+    const audio_module_handle_t mHandle;
+    const char * const          mModuleName;
+    sp<DeviceHalInterface>      mHwDevice;
+    const Flags                 mFlags;
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_HW_DEVICE_H
diff --git a/services/audioflinger/datapath/AudioStreamOut.cpp b/services/audioflinger/datapath/AudioStreamOut.cpp
new file mode 100644
index 0000000..6fa82e5
--- /dev/null
+++ b/services/audioflinger/datapath/AudioStreamOut.cpp
@@ -0,0 +1,212 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+
+#include <media/audiohal/DeviceHalInterface.h>
+#include <media/audiohal/StreamHalInterface.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include "AudioHwDevice.h"
+#include "AudioStreamOut.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+AudioStreamOut::AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags)
+        : audioHwDev(dev)
+        , flags(flags)
+{
+}
+
+// This must be defined here together with the HAL includes above and
+// not solely in the header.
+AudioStreamOut::~AudioStreamOut() = default;
+
+sp<DeviceHalInterface> AudioStreamOut::hwDev() const
+{
+    return audioHwDev->hwDevice();
+}
+
+status_t AudioStreamOut::getRenderPosition(uint64_t *frames)
+{
+    if (stream == nullptr) {
+        return NO_INIT;
+    }
+
+    uint32_t halPosition = 0;
+    const status_t status = stream->getRenderPosition(&halPosition);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    // Maintain a 64-bit render position using the 32-bit result from the HAL.
+    // This delta calculation relies on the arithmetic overflow behavior
+    // of integers. For example (100 - 0xFFFFFFF0) = 116.
+    const auto truncatedPosition = (uint32_t)mRenderPosition;
+    int32_t deltaHalPosition; // initialization not needed, overwitten by __builtin_sub_overflow()
+    (void) __builtin_sub_overflow(halPosition, truncatedPosition, &deltaHalPosition);
+
+    if (deltaHalPosition > 0) {
+        mRenderPosition += deltaHalPosition;
+    } else if (mExpectRetrograde) {
+        mExpectRetrograde = false;
+        mRenderPosition -= static_cast<uint64_t>(-deltaHalPosition);
+    }
+    // Scale from HAL sample rate to application rate.
+    *frames = mRenderPosition / mRateMultiplier;
+
+    return status;
+}
+
+// return bottom 32-bits of the render position
+status_t AudioStreamOut::getRenderPosition(uint32_t *frames)
+{
+    uint64_t position64 = 0;
+    const status_t status = getRenderPosition(&position64);
+    if (status == NO_ERROR) {
+        *frames = (uint32_t)position64;
+    }
+    return status;
+}
+
+status_t AudioStreamOut::getPresentationPosition(uint64_t *frames, struct timespec *timestamp)
+{
+    if (stream == nullptr) {
+        return NO_INIT;
+    }
+
+    uint64_t halPosition = 0;
+    const status_t status = stream->getPresentationPosition(&halPosition, timestamp);
+    if (status != NO_ERROR) {
+        return status;
+    }
+
+    // Adjust for standby using HAL rate frames.
+    // Only apply this correction if the HAL is getting PCM frames.
+    if (mHalFormatHasProportionalFrames) {
+        const uint64_t adjustedPosition = (halPosition <= mFramesWrittenAtStandby) ?
+                0 : (halPosition - mFramesWrittenAtStandby);
+        // Scale from HAL sample rate to application rate.
+        *frames = adjustedPosition / mRateMultiplier;
+    } else {
+        // For offloaded MP3 and other compressed formats.
+        *frames = halPosition;
+    }
+
+    return status;
+}
+
+status_t AudioStreamOut::open(
+        audio_io_handle_t handle,
+        audio_devices_t deviceType,
+        struct audio_config *config,
+        const char *address)
+{
+    sp<StreamOutHalInterface> outStream;
+
+    const audio_output_flags_t customFlags = (config->format == AUDIO_FORMAT_IEC61937)
+                ? (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO)
+                : flags;
+
+    int status = hwDev()->openOutputStream(
+            handle,
+            deviceType,
+            customFlags,
+            config,
+            address,
+            &outStream);
+    ALOGV("AudioStreamOut::open(), HAL returned "
+            " stream %p, sampleRate %d, Format %#x, "
+            "channelMask %#x, status %d",
+            outStream.get(),
+            config->sample_rate,
+            config->format,
+            config->channel_mask,
+            status);
+
+    // Some HALs may not recognize AUDIO_FORMAT_IEC61937. But if we declare
+    // it as PCM then it will probably work.
+    if (status != NO_ERROR && config->format == AUDIO_FORMAT_IEC61937) {
+        struct audio_config customConfig = *config;
+        customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+
+        status = hwDev()->openOutputStream(
+                handle,
+                deviceType,
+                customFlags,
+                &customConfig,
+                address,
+                &outStream);
+        ALOGV("AudioStreamOut::open(), treat IEC61937 as PCM, status = %d", status);
+    }
+
+    if (status == NO_ERROR) {
+        stream = outStream;
+        mHalFormatHasProportionalFrames = audio_has_proportional_frames(config->format);
+        status = stream->getFrameSize(&mHalFrameSize);
+        LOG_ALWAYS_FATAL_IF(status != OK, "Error retrieving frame size from HAL: %d", status);
+        LOG_ALWAYS_FATAL_IF(mHalFrameSize <= 0, "Error frame size was %zu but must be greater than"
+                " zero", mHalFrameSize);
+
+    }
+
+    return status;
+}
+
+audio_config_base_t AudioStreamOut::getAudioProperties() const
+{
+    audio_config_base_t result = AUDIO_CONFIG_BASE_INITIALIZER;
+    if (stream->getAudioProperties(&result) != OK) {
+        result.sample_rate = 0;
+        result.channel_mask = AUDIO_CHANNEL_INVALID;
+        result.format = AUDIO_FORMAT_INVALID;
+    }
+    return result;
+}
+
+int AudioStreamOut::flush()
+{
+    mRenderPosition = 0;
+    mExpectRetrograde = false;
+    mFramesWritten = 0;
+    mFramesWrittenAtStandby = 0;
+    const status_t result = stream->flush();
+    return result != INVALID_OPERATION ? result : NO_ERROR;
+}
+
+int AudioStreamOut::standby()
+{
+    mRenderPosition = 0;
+    mExpectRetrograde = false;
+    mFramesWrittenAtStandby = mFramesWritten;
+    return stream->standby();
+}
+
+ssize_t AudioStreamOut::write(const void *buffer, size_t numBytes)
+{
+    size_t bytesWritten;
+    const status_t result = stream->write(buffer, numBytes, &bytesWritten);
+    if (result == OK && bytesWritten > 0 && mHalFrameSize > 0) {
+        mFramesWritten += bytesWritten / mHalFrameSize;
+    }
+    return result == OK ? bytesWritten : result;
+}
+
+} // namespace android
diff --git a/services/audioflinger/datapath/AudioStreamOut.h b/services/audioflinger/datapath/AudioStreamOut.h
new file mode 100644
index 0000000..ce00f8c
--- /dev/null
+++ b/services/audioflinger/datapath/AudioStreamOut.h
@@ -0,0 +1,115 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_AUDIO_STREAM_OUT_H
+#define ANDROID_AUDIO_STREAM_OUT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+namespace android {
+
+class AudioHwDevice;
+class DeviceHalInterface;
+class StreamOutHalInterface;
+
+/**
+ * Managed access to a HAL output stream.
+ */
+class AudioStreamOut {
+public:
+// AudioStreamOut is immutable, so its fields are const.
+// For emphasis, we could also make all pointers to them be "const *",
+// but that would clutter the code unnecessarily.
+    AudioHwDevice * const audioHwDev;
+    sp<StreamOutHalInterface> stream;
+    const audio_output_flags_t flags;
+
+    [[nodiscard]] sp<DeviceHalInterface> hwDev() const;
+
+    AudioStreamOut(AudioHwDevice *dev, audio_output_flags_t flags);
+
+    virtual status_t open(
+            audio_io_handle_t handle,
+            audio_devices_t deviceType,
+            struct audio_config *config,
+            const char *address);
+
+    virtual ~AudioStreamOut();
+
+    // Get the bottom 32-bits of the 64-bit render position.
+    status_t getRenderPosition(uint32_t *frames);
+
+    virtual status_t getRenderPosition(uint64_t *frames);
+
+    virtual status_t getPresentationPosition(uint64_t *frames, struct timespec *timestamp);
+
+    /**
+    * Write audio buffer to driver. Returns number of bytes written, or a
+    * negative status_t. If at least one frame was written successfully prior to the error,
+    * it is suggested that the driver return that successful (short) byte count
+    * and then return an error in the subsequent call.
+    *
+    * If set_callback() has previously been called to enable non-blocking mode
+    * the write() is not allowed to block. It must write only the number of
+    * bytes that currently fit in the driver/hardware buffer and then return
+    * this byte count. If this is less than the requested write size the
+    * callback function must be called when more space is available in the
+    * driver/hardware buffer.
+    */
+    virtual ssize_t write(const void *buffer, size_t bytes);
+
+    /**
+     * @return frame size from the perspective of the application and the AudioFlinger.
+     */
+    [[nodiscard]] virtual size_t getFrameSize() const { return mHalFrameSize; }
+
+    /**
+     * @return audio stream configuration: channel mask, format, sample rate:
+     *   - channel mask from the perspective of the application and the AudioFlinger,
+     *     The HAL is in stereo mode when playing multi-channel compressed audio over HDMI;
+     *   - format from the perspective of the application and the AudioFlinger;
+     *   - sample rate from the perspective of the application and the AudioFlinger,
+     *     The HAL may be running at a higher sample rate if, for example, playing wrapped EAC3.
+     */
+    [[nodiscard]] virtual audio_config_base_t getAudioProperties() const;
+
+    virtual status_t flush();
+    virtual status_t standby();
+
+    // Avoid suppressing retrograde motion in mRenderPosition for gapless offload/direct when
+    // transitioning between tracks.
+    // The HAL resets the frame position without flush/stop being called, but calls back prior to
+    // this event. So, on the next occurrence of retrograde motion, we permit backwards movement of
+    // mRenderPosition.
+    virtual void presentationComplete() { mExpectRetrograde = true; }
+
+protected:
+    uint64_t             mFramesWritten = 0; // reset by flush
+    uint64_t             mFramesWrittenAtStandby = 0;
+    uint64_t             mRenderPosition = 0; // reset by flush, standby, or presentation complete
+    int                  mRateMultiplier = 1;
+    bool                 mHalFormatHasProportionalFrames = false;
+    size_t               mHalFrameSize = 0;
+    bool                 mExpectRetrograde = false; // see presentationComplete
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_STREAM_OUT_H
diff --git a/services/audioflinger/datapath/SpdifStreamOut.cpp b/services/audioflinger/datapath/SpdifStreamOut.cpp
new file mode 100644
index 0000000..43e9c0c
--- /dev/null
+++ b/services/audioflinger/datapath/SpdifStreamOut.cpp
@@ -0,0 +1,125 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "AudioFlinger"
+//#define LOG_NDEBUG 0
+#include <system/audio.h>
+#include <utils/Log.h>
+
+#include <audio_utils/spdif/SPDIFEncoder.h>
+
+#include "AudioHwDevice.h"
+#include "SpdifStreamOut.h"
+
+namespace android {
+
+/**
+ * If the AudioFlinger is processing encoded data and the HAL expects
+ * PCM then we need to wrap the data in an SPDIF wrapper.
+ */
+SpdifStreamOut::SpdifStreamOut(AudioHwDevice *dev,
+            audio_output_flags_t flags,
+            audio_format_t format)
+        // Tell the HAL that the data will be compressed audio wrapped in a data burst.
+        : AudioStreamOut(dev, (audio_output_flags_t) (flags | AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO))
+        , mSpdifEncoder(this, format)
+{
+}
+
+status_t SpdifStreamOut::open(
+                              audio_io_handle_t handle,
+                              audio_devices_t devices,
+                              struct audio_config *config,
+                              const char *address)
+{
+    struct audio_config customConfig = *config;
+
+    mApplicationFormat = config->format;
+    mApplicationSampleRate = config->sample_rate;
+    mApplicationChannelMask = config->channel_mask;
+
+    // Some data bursts run at a higher sample rate.
+    // TODO Move this into the audio_utils as a static method.
+    switch(config->format) {
+        case AUDIO_FORMAT_E_AC3:
+        case AUDIO_FORMAT_E_AC3_JOC:
+            mRateMultiplier = 4;
+            break;
+        case AUDIO_FORMAT_AC3:
+        case AUDIO_FORMAT_DTS:
+        case AUDIO_FORMAT_DTS_HD:
+            mRateMultiplier = 1;
+            break;
+        default:
+            ALOGE("ERROR SpdifStreamOut::open() unrecognized format 0x%08X\n",
+                config->format);
+            return BAD_VALUE;
+    }
+    customConfig.sample_rate = config->sample_rate * mRateMultiplier;
+
+    customConfig.format = AUDIO_FORMAT_PCM_16_BIT;
+    customConfig.channel_mask = AUDIO_CHANNEL_OUT_STEREO;
+
+    // Always print this because otherwise it could be very confusing if the
+    // HAL and AudioFlinger are using different formats.
+    // Print before open() because HAL may modify customConfig.
+    ALOGI("SpdifStreamOut::open() AudioFlinger requested"
+            " sampleRate %d, format %#x, channelMask %#x",
+            config->sample_rate,
+            config->format,
+            config->channel_mask);
+    ALOGI("SpdifStreamOut::open() HAL configured for"
+            " sampleRate %d, format %#x, channelMask %#x",
+            customConfig.sample_rate,
+            customConfig.format,
+            customConfig.channel_mask);
+
+    const status_t status = AudioStreamOut::open(
+            handle,
+            devices,
+            &customConfig,
+            address);
+
+    ALOGI("SpdifStreamOut::open() status = %d", status);
+
+    return status;
+}
+
+int SpdifStreamOut::flush()
+{
+    mSpdifEncoder.reset();
+    return AudioStreamOut::flush();
+}
+
+int SpdifStreamOut::standby()
+{
+    mSpdifEncoder.reset();
+    return AudioStreamOut::standby();
+}
+
+ssize_t SpdifStreamOut::writeDataBurst(const void* buffer, size_t bytes)
+{
+    return AudioStreamOut::write(buffer, bytes);
+}
+
+ssize_t SpdifStreamOut::write(const void* buffer, size_t numBytes)
+{
+    // Write to SPDIF wrapper. It will call back to writeDataBurst().
+    return mSpdifEncoder.write(buffer, numBytes);
+}
+
+} // namespace android
diff --git a/services/audioflinger/datapath/SpdifStreamOut.h b/services/audioflinger/datapath/SpdifStreamOut.h
new file mode 100644
index 0000000..c8dc89f
--- /dev/null
+++ b/services/audioflinger/datapath/SpdifStreamOut.h
@@ -0,0 +1,123 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#ifndef ANDROID_SPDIF_STREAM_OUT_H
+#define ANDROID_SPDIF_STREAM_OUT_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <system/audio.h>
+
+#include "AudioStreamOut.h"
+
+#include <audio_utils/spdif/SPDIFEncoder.h>
+
+namespace android {
+
+/**
+ * Stream that is a PCM data burst in the HAL but looks like an encoded stream
+ * to the AudioFlinger. Wraps encoded data in an SPDIF wrapper per IEC61973-3.
+ */
+class SpdifStreamOut : public AudioStreamOut {
+public:
+
+    SpdifStreamOut(AudioHwDevice *dev, audio_output_flags_t flags,
+            audio_format_t format);
+
+    ~SpdifStreamOut() override = default;
+
+    status_t open(
+            audio_io_handle_t handle,
+            audio_devices_t devices,
+            struct audio_config *config,
+            const char *address) override;
+
+    /**
+    * Write audio buffer to driver. Returns number of bytes written, or a
+    * negative status_t. If at least one frame was written successfully prior to the error,
+    * it is suggested that the driver return that successful (short) byte count
+    * and then return an error in the subsequent call.
+    *
+    * If set_callback() has previously been called to enable non-blocking mode
+    * the write() is not allowed to block. It must write only the number of
+    * bytes that currently fit in the driver/hardware buffer and then return
+    * this byte count. If this is less than the requested write size the
+    * callback function must be called when more space is available in the
+    * driver/hardware buffer.
+    */
+    ssize_t write(const void* buffer, size_t bytes) override;
+
+    /**
+     * @return frame size from the perspective of the application and the AudioFlinger.
+     */
+    [[nodiscard]] size_t getFrameSize() const override { return sizeof(int8_t); }
+
+    /**
+     * @return format from the perspective of the application and the AudioFlinger.
+     */
+    [[nodiscard]] virtual audio_format_t getFormat() const { return mApplicationFormat; }
+
+    /**
+     * The HAL may be running at a higher sample rate if, for example, playing wrapped EAC3.
+     * @return sample rate from the perspective of the application and the AudioFlinger.
+     */
+    [[nodiscard]] virtual uint32_t getSampleRate() const { return mApplicationSampleRate; }
+
+    /**
+     * The HAL is in stereo mode when playing multi-channel compressed audio over HDMI.
+     * @return channel mask from the perspective of the application and the AudioFlinger.
+     */
+    [[nodiscard]] virtual audio_channel_mask_t getChannelMask() const {
+        return mApplicationChannelMask;
+    }
+
+    status_t flush() override;
+    status_t standby() override;
+
+private:
+
+    class MySPDIFEncoder : public SPDIFEncoder
+    {
+    public:
+        MySPDIFEncoder(SpdifStreamOut *spdifStreamOut, audio_format_t format)
+          :  SPDIFEncoder(format)
+          , mSpdifStreamOut(spdifStreamOut)
+        {
+        }
+
+        ssize_t writeOutput(const void* buffer, size_t bytes) override
+        {
+            return mSpdifStreamOut->writeDataBurst(buffer, bytes);
+        }
+    protected:
+        SpdifStreamOut * const mSpdifStreamOut;
+    };
+
+    MySPDIFEncoder       mSpdifEncoder;
+    audio_format_t       mApplicationFormat = AUDIO_FORMAT_DEFAULT;
+    uint32_t             mApplicationSampleRate = 0;
+    audio_channel_mask_t mApplicationChannelMask = AUDIO_CHANNEL_NONE;
+
+    ssize_t  writeDataBurst(const void* data, size_t bytes);
+    ssize_t  writeInternal(const void* buffer, size_t bytes);
+
+};
+
+} // namespace android
+
+#endif // ANDROID_SPDIF_STREAM_OUT_H
diff --git a/services/audioflinger/datapath/ThreadMetrics.h b/services/audioflinger/datapath/ThreadMetrics.h
new file mode 100644
index 0000000..5493b3c
--- /dev/null
+++ b/services/audioflinger/datapath/ThreadMetrics.h
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_THREADMETRICS_H
+#define ANDROID_AUDIO_THREADMETRICS_H
+
+#include <mutex>
+
+namespace android {
+
+/**
+ * ThreadMetrics handles the AudioFlinger thread log statistics.
+ *
+ * We aggregate metrics for a particular device for proper analysis.
+ * This includes power, performance, and usage metrics.
+ *
+ * This class is thread-safe with a lock for safety.  There is no risk of deadlock
+ * as this class only executes external one-way calls in Mediametrics and does not
+ * call any other AudioFlinger class.
+ *
+ * Terminology:
+ * An AudioInterval is a contiguous playback segment.
+ * An AudioIntervalGroup is a group of continuous playback segments on the same device.
+ *
+ * We currently deliver metrics based on an AudioIntervalGroup.
+ */
+class ThreadMetrics final {
+public:
+    ThreadMetrics(std::string metricsId, bool isOut)
+        : mMetricsId(std::move(metricsId))
+        , mIsOut(isOut)
+        {}
+
+    ~ThreadMetrics() {
+        logEndInterval(); // close any open interval groups
+        std::lock_guard l(mLock);
+        deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DTOR)
+            .record();
+    }
+
+    // Called under the following circumstances
+    // 1) Upon a createPatch and we are not in standby
+    // 2) We come out of standby
+    void logBeginInterval() {
+        std::lock_guard l(mLock);
+        // The devices we look for change depend on whether the Thread is input or output.
+        const std::string& patchDevices = mIsOut ? mCreatePatchOutDevices : mCreatePatchInDevices;
+        if (mDevices != patchDevices) {
+            deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
+            mDevices = patchDevices; // set after endAudioIntervalGroup
+            resetIntervalGroupMetrics();
+            deliverDeviceMetrics(
+                    AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, mDevices.c_str());
+        }
+        if (mIntervalStartTimeNs == 0) {
+            ++mIntervalCount;
+            mIntervalStartTimeNs = systemTime();
+        }
+    }
+
+    void logConstructor(pid_t pid, const char *threadType, int32_t id) const {
+        mediametrics::LogItem(mMetricsId)
+            .setPid(pid)
+            .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+            .set(AMEDIAMETRICS_PROP_TYPE, threadType)
+            .set(AMEDIAMETRICS_PROP_THREADID, id)
+            .record();
+    }
+
+    void logCreatePatch(const std::string& inDevices, const std::string& outDevices) {
+        std::lock_guard l(mLock);
+        mCreatePatchInDevices = inDevices;
+        mCreatePatchOutDevices = outDevices;
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_CREATEAUDIOPATCH)
+            .set(AMEDIAMETRICS_PROP_INPUTDEVICES, inDevices)
+            .set(AMEDIAMETRICS_PROP_OUTPUTDEVICES, outDevices)
+            .record();
+    }
+
+    // Called when we are removed from the Thread.
+    void logEndInterval() {
+        std::lock_guard l(mLock);
+        if (mIntervalStartTimeNs != 0) {
+            const int64_t elapsedTimeNs = systemTime() - mIntervalStartTimeNs;
+            mIntervalStartTimeNs = 0;
+            mCumulativeTimeNs += elapsedTimeNs;
+            mDeviceTimeNs += elapsedTimeNs;
+        }
+    }
+
+    void logThrottleMs(double throttleMs) const {
+        mediametrics::LogItem(mMetricsId)
+            // ms units always double
+            .set(AMEDIAMETRICS_PROP_THROTTLEMS, (double)throttleMs)
+            .record();
+    }
+
+    void logLatency(double latencyMs) {
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
+            .record();
+        std::lock_guard l(mLock);
+        mDeviceLatencyMs.add(latencyMs);
+    }
+
+    void logUnderrunFrames(size_t frames) {
+        std::lock_guard l(mLock);
+        if (mLastUnderrun == false && frames > 0) {
+            ++mUnderrunCount; // count non-continguous underrun sequences.
+        }
+        mLastUnderrun = (frames > 0);
+        mUnderrunFrames += frames;
+    }
+
+    const std::string& getMetricsId() const {
+        return mMetricsId;
+    }
+
+private:
+    // no lock required - all arguments and constants.
+    void deliverDeviceMetrics(const char *eventName, const char *devices) const {
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_EVENT, eventName)
+            .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
+                   : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
+           .record();
+    }
+
+    void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
+        if (mIntervalCount > 0) {
+            mediametrics::LogItem item(mMetricsId);
+            item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
+                .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
+                .set(AMEDIAMETRICS_PROP_EVENT, eventName)
+                .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount)
+                // we set "last" device to indicate the device the group was
+                // associated with (because a createPatch which is logged in ThreadMetrics
+                // could have changed the device).
+                .set(mIsOut
+                        ? AMEDIAMETRICS_PROP_PREFIX_LAST AMEDIAMETRICS_PROP_OUTPUTDEVICES
+                        : AMEDIAMETRICS_PROP_PREFIX_LAST AMEDIAMETRICS_PROP_INPUTDEVICES,
+                        mDevices.c_str());
+            if (mDeviceLatencyMs.getN() > 0) {
+                item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean());
+            }
+            if (mUnderrunCount > 0) {
+                item.set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t)mUnderrunCount)
+                    .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES, (int64_t)mUnderrunFrames);
+            }
+            item.record();
+        }
+    }
+
+    void resetIntervalGroupMetrics() REQUIRES(mLock) {
+        // mDevices is not reset by clear
+
+        mIntervalCount = 0;
+        mIntervalStartTimeNs = 0;
+        // mCumulativeTimeNs is not reset by clear.
+        mDeviceTimeNs = 0;
+
+        mDeviceLatencyMs.reset();
+
+        mLastUnderrun = false;
+        mUnderrunCount = 0;
+        mUnderrunFrames = 0;
+    }
+
+    const std::string mMetricsId;
+    const bool        mIsOut;  // if true, than a playback track, otherwise used for record.
+
+    mutable           std::mutex mLock;
+
+    // Devices in the interval group.
+    std::string       mDevices GUARDED_BY(mLock); // last input or output devices based on mIsOut.
+    std::string       mCreatePatchInDevices GUARDED_BY(mLock);
+    std::string       mCreatePatchOutDevices GUARDED_BY(mLock);
+
+    // Number of intervals and playing time
+    int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
+    int64_t           mIntervalStartTimeNs GUARDED_BY(mLock) = 0;
+    int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0;
+    int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;
+
+    // latency and startup for each interval.
+    audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
+
+    // underrun count and frames
+    bool              mLastUnderrun GUARDED_BY(mLock) = false; // checks consecutive underruns
+    int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;    // number of consecutive underruns
+    int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;   // total estimated frames underrun
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_THREADMETRICS_H
diff --git a/services/audioflinger/datapath/TrackMetrics.h b/services/audioflinger/datapath/TrackMetrics.h
new file mode 100644
index 0000000..f3425df
--- /dev/null
+++ b/services/audioflinger/datapath/TrackMetrics.h
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_AUDIO_TRACKMETRICS_H
+#define ANDROID_AUDIO_TRACKMETRICS_H
+
+#include <binder/IActivityManager.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <mutex>
+
+namespace android {
+
+/**
+ * TrackMetrics handles the AudioFlinger track metrics.
+ *
+ * We aggregate metrics for a particular device for proper analysis.
+ * This includes power, performance, and usage metrics.
+ *
+ * This class is thread-safe with a lock for safety.  There is no risk of deadlock
+ * as this class only executes external one-way calls in Mediametrics and does not
+ * call any other AudioFlinger class.
+ *
+ * Terminology:
+ * An AudioInterval is a contiguous playback segment.
+ * An AudioIntervalGroup is a group of continuous playback segments on the same device.
+ *
+ * We currently deliver metrics based on an AudioIntervalGroup.
+ */
+class TrackMetrics final {
+
+
+public:
+    TrackMetrics(std::string metricsId, bool isOut, int clientUid)
+        : mMetricsId(std::move(metricsId))
+        , mIsOut(isOut)
+        , mUid(clientUid)
+        {}  // we don't log a constructor item, we wait for more info in logConstructor().
+
+    ~TrackMetrics() {
+        logEndInterval();
+        std::lock_guard l(mLock);
+        deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
+        // we don't log a destructor item here.
+    }
+
+    // Called under the following circumstances
+    // 1) when we are added to the Thread
+    // 2) when we have a createPatch in the Thread.
+    void logBeginInterval(const std::string& devices) {
+        std::lock_guard l(mLock);
+        if (mDevices != devices) {
+            deliverCumulativeMetrics(AMEDIAMETRICS_PROP_EVENT_VALUE_ENDAUDIOINTERVALGROUP);
+            mDevices = devices;
+            resetIntervalGroupMetrics();
+            deliverDeviceMetrics(
+                    AMEDIAMETRICS_PROP_EVENT_VALUE_BEGINAUDIOINTERVALGROUP, devices.c_str());
+        }
+        ++mIntervalCount;
+        const auto& mActivityManager = getActivityManager();
+        if (mActivityManager) {
+            if (mIsOut) {
+                mActivityManager->logFgsApiBegin(AUDIO_API,
+                    mUid,
+                    IPCThreadState::self() -> getCallingPid());
+            } else {
+                mActivityManager->logFgsApiBegin(MICROPHONE_API,
+                    mUid,
+                    IPCThreadState::self() -> getCallingPid());
+            }
+        }
+    }
+
+    void logConstructor(pid_t creatorPid, uid_t creatorUid, int32_t internalTrackId,
+            const std::string& traits = {},
+            audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT) const {
+        // Once this item is logged by the server, the client can add properties.
+        // no lock required, all local or const variables.
+        mediametrics::LogItem item(mMetricsId);
+        item.setPid(creatorPid)
+            .setUid(creatorUid)
+            .set(AMEDIAMETRICS_PROP_ALLOWUID, (int32_t)creatorUid)
+            .set(AMEDIAMETRICS_PROP_EVENT,
+                    AMEDIAMETRICS_PROP_PREFIX_SERVER AMEDIAMETRICS_PROP_EVENT_VALUE_CTOR)
+            .set(AMEDIAMETRICS_PROP_INTERNALTRACKID, internalTrackId)
+            .set(AMEDIAMETRICS_PROP_TRAITS, traits);
+        // log streamType from the service, since client doesn't know chosen streamType.
+        if (streamType != AUDIO_STREAM_DEFAULT) {
+            item.set(AMEDIAMETRICS_PROP_STREAMTYPE, toString(streamType).c_str());
+        }
+        item.record();
+    }
+
+    // Called when we are removed from the Thread.
+    void logEndInterval() {
+        std::lock_guard l(mLock);
+        if (mLastVolumeChangeTimeNs != 0) {
+            logVolume_l(mVolume); // flush out the last volume.
+            mLastVolumeChangeTimeNs = 0;
+        }
+        const auto& mActivityManager = getActivityManager();
+        if (mActivityManager) {
+            if (mIsOut) {
+                mActivityManager->logFgsApiEnd(AUDIO_API,
+                    mUid,
+                    IPCThreadState::self() -> getCallingPid());
+            } else {
+                mActivityManager->logFgsApiEnd(MICROPHONE_API,
+                    mUid,
+                    IPCThreadState::self() -> getCallingPid());
+            }
+        }
+    }
+
+    void logInvalidate() const {
+        // no lock required, all local or const variables.
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_EVENT,
+                 AMEDIAMETRICS_PROP_EVENT_VALUE_INVALIDATE)
+            .record();
+    }
+
+    void logLatencyAndStartup(double latencyMs, double startupMs) {
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_LATENCYMS, latencyMs)
+            .set(AMEDIAMETRICS_PROP_STARTUPMS, startupMs)
+            .record();
+        std::lock_guard l(mLock);
+        mDeviceLatencyMs.add(latencyMs);
+        mDeviceStartupMs.add(startupMs);
+    }
+
+    void updateMinMaxVolume_l(int64_t durationNs, double deviceVolume)
+            REQUIRES(mLock) {
+        if (deviceVolume > mMaxVolume) {
+            mMaxVolume = deviceVolume;
+            mMaxVolumeDurationNs = durationNs;
+        } else if (deviceVolume == mMaxVolume) {
+            mMaxVolumeDurationNs += durationNs;
+        }
+        if (deviceVolume < mMinVolume) {
+            mMinVolume = deviceVolume;
+            mMinVolumeDurationNs = durationNs;
+        } else if (deviceVolume == mMinVolume) {
+            mMinVolumeDurationNs += durationNs;
+        }
+    }
+
+    // may be called multiple times during an interval
+    void logVolume(float volume) {
+        std::lock_guard l(mLock);
+        logVolume_l(volume);
+    }
+
+    // Use absolute numbers returned by AudioTrackShared.
+    void logUnderruns(size_t count, size_t frames) {
+        std::lock_guard l(mLock);
+        mUnderrunCount = count;
+        mUnderrunFrames = frames;
+        // Consider delivering a message here (also be aware of excessive spam).
+    }
+
+private:
+
+    // no lock required - all arguments and constants.
+    void deliverDeviceMetrics(const char *eventName, const char *devices) const {
+        mediametrics::LogItem(mMetricsId)
+            .set(AMEDIAMETRICS_PROP_EVENT, eventName)
+            .set(mIsOut ? AMEDIAMETRICS_PROP_OUTPUTDEVICES
+                   : AMEDIAMETRICS_PROP_INPUTDEVICES, devices)
+           .record();
+    }
+
+    void logVolume_l(float volume) REQUIRES(mLock) {
+        const int64_t timeNs = systemTime();
+        const int64_t durationNs = mLastVolumeChangeTimeNs == 0
+                ? 0 : timeNs - mLastVolumeChangeTimeNs;
+        if (durationNs > 0) {
+            // See West's algorithm for weighted averages
+            // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+            mDeviceVolume += (mVolume - mDeviceVolume) * durationNs
+                      / (durationNs + mDeviceTimeNs);
+            mDeviceTimeNs += durationNs;
+            mCumulativeTimeNs += durationNs;
+        }
+        updateMinMaxVolume_l(durationNs, mVolume); // always update.
+        mVolume = volume;
+        mLastVolumeChangeTimeNs = timeNs;
+    }
+
+    void deliverCumulativeMetrics(const char *eventName) const REQUIRES(mLock) {
+        if (mIntervalCount > 0) {
+            mediametrics::LogItem item(mMetricsId);
+            item.set(AMEDIAMETRICS_PROP_CUMULATIVETIMENS, mCumulativeTimeNs)
+                .set(AMEDIAMETRICS_PROP_DEVICETIMENS, mDeviceTimeNs)
+                .set(AMEDIAMETRICS_PROP_EVENT, eventName)
+                .set(AMEDIAMETRICS_PROP_INTERVALCOUNT, (int32_t)mIntervalCount);
+            if (mIsOut) {
+                item.set(AMEDIAMETRICS_PROP_DEVICEVOLUME, mDeviceVolume)
+                    .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUMEDURATIONNS, mMaxVolumeDurationNs)
+                    .set(AMEDIAMETRICS_PROP_DEVICEMAXVOLUME, mMaxVolume)
+                    .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUMEDURATIONNS, mMinVolumeDurationNs)
+                    .set(AMEDIAMETRICS_PROP_DEVICEMINVOLUME, mMinVolume);
+            }
+            if (mDeviceLatencyMs.getN() > 0) {
+                item.set(AMEDIAMETRICS_PROP_DEVICELATENCYMS, mDeviceLatencyMs.getMean())
+                    .set(AMEDIAMETRICS_PROP_DEVICESTARTUPMS, mDeviceStartupMs.getMean());
+            }
+            if (mUnderrunCount > 0) {
+                item.set(AMEDIAMETRICS_PROP_UNDERRUN,
+                        (int32_t)(mUnderrunCount - mUnderrunCountSinceIntervalGroup))
+                    .set(AMEDIAMETRICS_PROP_UNDERRUNFRAMES,
+                        (int64_t)(mUnderrunFrames - mUnderrunFramesSinceIntervalGroup));
+            }
+            item.record();
+        }
+    }
+
+    void resetIntervalGroupMetrics() REQUIRES(mLock) {
+        // mDevices is not reset by resetIntervalGroupMetrics.
+
+        mIntervalCount = 0;
+        // mCumulativeTimeNs is not reset by resetIntervalGroupMetrics.
+        mDeviceTimeNs = 0;
+
+        mVolume = 0.f;
+        mDeviceVolume = 0.f;
+        mLastVolumeChangeTimeNs = 0;  // last time volume logged, cleared on endInterval
+        mMinVolume = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
+        mMaxVolume = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
+        mMinVolumeDurationNs = 0;
+        mMaxVolumeDurationNs = 0;
+
+        mDeviceLatencyMs.reset();
+        mDeviceStartupMs.reset();
+
+        mUnderrunCountSinceIntervalGroup = mUnderrunCount;
+        mUnderrunFramesSinceIntervalGroup = mUnderrunFrames;
+        // do not reset mUnderrunCount - it keeps continuously running for tracks.
+    }
+
+    // Meyer's singleton is thread-safe.
+    static const sp<IActivityManager>& getActivityManager() {
+        static const auto activityManager = []() -> sp<IActivityManager> {
+            const sp<IServiceManager> sm(defaultServiceManager());
+            if (sm != nullptr) {
+                 return interface_cast<IActivityManager>(sm->checkService(String16("activity")));
+            }
+            return nullptr;
+        }();
+        return activityManager;
+    }
+
+    const std::string mMetricsId;
+    const bool        mIsOut;  // if true, than a playback track, otherwise used for record.
+
+    static constexpr int AUDIO_API = 5;
+    static constexpr int MICROPHONE_API = 6;
+    const int         mUid;
+
+    mutable           std::mutex mLock;
+
+    // Devices in the interval group.
+    std::string       mDevices GUARDED_BY(mLock);
+
+    // Number of intervals and playing time
+    int32_t           mIntervalCount GUARDED_BY(mLock) = 0;
+    int64_t           mCumulativeTimeNs GUARDED_BY(mLock) = 0; // total time.
+    int64_t           mDeviceTimeNs GUARDED_BY(mLock) = 0;     // time on device.
+
+    // Average volume
+    double            mVolume GUARDED_BY(mLock) = 0.f;       // last set volume.
+    double            mDeviceVolume GUARDED_BY(mLock) = 0.f; // running average volume.
+    int64_t           mLastVolumeChangeTimeNs GUARDED_BY(mLock) = 0;
+
+    // Min/Max volume
+    double            mMinVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MIN_VOLUME;
+    double            mMaxVolume GUARDED_BY(mLock) = AMEDIAMETRICS_INITIAL_MAX_VOLUME;
+    int64_t           mMinVolumeDurationNs GUARDED_BY(mLock) = 0;
+    int64_t           mMaxVolumeDurationNs GUARDED_BY(mLock) = 0;
+
+    // latency and startup for each interval.
+    audio_utils::Statistics<double> mDeviceLatencyMs GUARDED_BY(mLock);
+    audio_utils::Statistics<double> mDeviceStartupMs GUARDED_BY(mLock);
+
+    // underrun count and frames
+    int64_t           mUnderrunCount GUARDED_BY(mLock) = 0;
+    int64_t           mUnderrunFrames GUARDED_BY(mLock) = 0;
+    int64_t           mUnderrunCountSinceIntervalGroup GUARDED_BY(mLock) = 0;
+    int64_t           mUnderrunFramesSinceIntervalGroup GUARDED_BY(mLock) = 0;
+};
+
+} // namespace android
+
+#endif // ANDROID_AUDIO_TRACKMETRICS_H