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