Merge changes from topic "feature_dynamicVolSupport"
* changes:
Adding VTS tests for IAudioControl#registerModuleChangeCallback
Add default implementation for dynamic volume config feature
Adding support for dynamic audio gain config
diff --git a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
index 8dc5ffe..ffd575d 100644
--- a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
+++ b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IAudioControl.aidl
@@ -46,4 +46,6 @@
oneway void onAudioFocusChangeWithMetaData(in android.hardware.audio.common.PlaybackTrackMetadata playbackMetaData, in int zoneId, in android.hardware.automotive.audiocontrol.AudioFocusChange focusChange);
oneway void setAudioDeviceGainsChanged(in android.hardware.automotive.audiocontrol.Reasons[] reasons, in android.hardware.automotive.audiocontrol.AudioGainConfigInfo[] gains);
oneway void registerGainCallback(in android.hardware.automotive.audiocontrol.IAudioGainCallback callback);
+ void setModuleChangeCallback(in android.hardware.automotive.audiocontrol.IModuleChangeCallback callback);
+ void clearModuleChangeCallback();
}
diff --git a/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
new file mode 100644
index 0000000..2bbb936
--- /dev/null
+++ b/automotive/audiocontrol/aidl/aidl_api/android.hardware.automotive.audiocontrol/current/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.automotive.audiocontrol;
+@VintfStability
+interface IModuleChangeCallback {
+ oneway void onAudioPortsChanged(in android.media.audio.common.AudioPort[] audioPorts);
+}
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
index 0ffcd5e..9564efc 100644
--- a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IAudioControl.aidl
@@ -51,6 +51,7 @@
import android.hardware.automotive.audiocontrol.DuckingInfo;
import android.hardware.automotive.audiocontrol.IAudioGainCallback;
import android.hardware.automotive.audiocontrol.IFocusListener;
+import android.hardware.automotive.audiocontrol.IModuleChangeCallback;
import android.hardware.automotive.audiocontrol.MutingInfo;
import android.hardware.automotive.audiocontrol.Reasons;
@@ -183,4 +184,26 @@
* interface.
*/
oneway void registerGainCallback(in IAudioGainCallback callback);
+
+ /**
+ * Sets callback with HAL for notifying changes to hardware module (that is:
+ * {@link android.hardware.audio.core.IModule}) configurations.
+ *
+ * @param callback The {@link android.hardware.automotive.audiocontrol.IModuleChangeCallback}
+ * interface to use use when new updates are available for
+ * {@link android.hardware.audio.core.IModule} configs
+ * @throws EX_UNSUPPORTED_OPERATION if dynamic audio configs are not supported.
+ * @throws EX_ILLEGAL_STATE if a callback already exists
+ * @throws EX_ILLEGAL_ARGUMENT if the passed callback is (@code null}
+ */
+ void setModuleChangeCallback(in IModuleChangeCallback callback);
+
+ /**
+ * Clears module change callback
+ *
+ * If no callback is registered previously, then this call should be a no-op.
+ *
+ * @throws EX_UNSUPPORTED_OPERATION if dynamic audio configs are not supported.
+ */
+ void clearModuleChangeCallback();
}
diff --git a/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
new file mode 100644
index 0000000..4282483
--- /dev/null
+++ b/automotive/audiocontrol/aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.aidl
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.automotive.audiocontrol;
+
+import android.media.audio.common.AudioPort;
+
+/**
+ * Interface definition for asynchronous changes to audio configs defined
+ * for a hardware {@link android.hardware.audio.core.IModule}.
+ */
+@VintfStability
+oneway interface IModuleChangeCallback {
+ /**
+ * Used to indicate that one or more {@link android.media.audio.common.AudioPort}
+ * configs have changed. Implementations MUST return at least one AudioPort.
+ *
+ * Notes for AudioPort:
+ * 1. For V3, the support will be limited to configurable AudioGain stages - per
+ * car audio framework support.
+ * 2. For automotive 'bus' devices, the expected settings are
+ * AudioDevice {
+ * AudioDeviceDescription {type: IN/OUT_DEVICE, connection: CONNECTION_BUS}
+ * AudioDeviceAddress {id: string}}
+ *
+ * Notes for AudioGain:
+ * 1. Car audio framework only supports AudioGainMode::JOINT. Any other mode
+ * selection will be ignored.
+ * See {@link android.media.audio.common.AudioGainMode}
+ * 2. Implementations MUST ensure that the gain stages are identical for buses
+ * that map to the same volume group. Any inconsistencies will result in
+ * inferior volume-change experience to the users.
+ * 3. Implementations MUST ensure that the new gain stages are subset (do not
+ * exceed) of the gain stage definitions provided to audio policy. If they
+ * exceed, it can result in failure when setting value over the range
+ * allowed by the audio policy.
+ *
+ * Other notes:
+ * 1. In case of AudioControl service restart or resume, the implementations MUST
+ * issue an immediate callback following registration.
+ * 2. In case of client restart, the AudioControl service MUST clear all stale
+ * callbacks.
+ *
+ * @param audioPorts list of {@link android.media.audio.common.AudioPort} that
+ * are updated
+ */
+ void onAudioPortsChanged(in AudioPort[] audioPorts);
+}
diff --git a/automotive/audiocontrol/aidl/default/Android.bp b/automotive/audiocontrol/aidl/default/Android.bp
index 6bf4b9d..dde05c3 100644
--- a/automotive/audiocontrol/aidl/default/Android.bp
+++ b/automotive/audiocontrol/aidl/default/Android.bp
@@ -31,7 +31,7 @@
"android.hardware.audio.common@7.0-enums",
"android.hardware.audio.common-V1-ndk",
"android.frameworks.automotive.powerpolicy-V1-ndk",
- "android.hardware.automotive.audiocontrol-V2-ndk",
+ "android.hardware.automotive.audiocontrol-V3-ndk",
"libbase",
"libbinder_ndk",
"libcutils",
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.cpp b/automotive/audiocontrol/aidl/default/AudioControl.cpp
index a121f8b..cf7307d 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.cpp
+++ b/automotive/audiocontrol/aidl/default/AudioControl.cpp
@@ -24,6 +24,7 @@
#include <aidl/android/hardware/automotive/audiocontrol/IFocusListener.h>
#include <android-base/logging.h>
+#include <android-base/parsebool.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
@@ -37,6 +38,8 @@
namespace aidl::android::hardware::automotive::audiocontrol {
using ::android::base::EqualsIgnoreCase;
+using ::android::base::ParseBool;
+using ::android::base::ParseBoolResult;
using ::android::base::ParseInt;
using ::std::shared_ptr;
using ::std::string;
@@ -70,6 +73,95 @@
}
} // namespace
+namespace {
+using ::aidl::android::media::audio::common::AudioChannelLayout;
+using ::aidl::android::media::audio::common::AudioDeviceDescription;
+using ::aidl::android::media::audio::common::AudioDeviceType;
+using ::aidl::android::media::audio::common::AudioFormatDescription;
+using ::aidl::android::media::audio::common::AudioFormatType;
+using ::aidl::android::media::audio::common::AudioGain;
+using ::aidl::android::media::audio::common::AudioGainMode;
+using ::aidl::android::media::audio::common::AudioIoFlags;
+using ::aidl::android::media::audio::common::AudioOutputFlags;
+using ::aidl::android::media::audio::common::AudioPort;
+using ::aidl::android::media::audio::common::AudioPortDeviceExt;
+using ::aidl::android::media::audio::common::AudioPortExt;
+using ::aidl::android::media::audio::common::AudioPortMixExt;
+using ::aidl::android::media::audio::common::AudioProfile;
+using ::aidl::android::media::audio::common::PcmType;
+
+// reuse common code artifacts
+void fillProfile(const std::vector<int32_t>& channelLayouts,
+ const std::vector<int32_t>& sampleRates, AudioProfile* profile) {
+ for (auto layout : channelLayouts) {
+ profile->channelMasks.push_back(
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout));
+ }
+ profile->sampleRates.insert(profile->sampleRates.end(), sampleRates.begin(), sampleRates.end());
+}
+
+AudioProfile createProfile(PcmType pcmType, const std::vector<int32_t>& channelLayouts,
+ const std::vector<int32_t>& sampleRates) {
+ AudioProfile profile;
+ profile.format.type = AudioFormatType::PCM;
+ profile.format.pcm = pcmType;
+ fillProfile(channelLayouts, sampleRates, &profile);
+ return profile;
+}
+
+AudioProfile createProfile(const std::string& encodingType,
+ const std::vector<int32_t>& channelLayouts,
+ const std::vector<int32_t>& sampleRates) {
+ AudioProfile profile;
+ profile.format.encoding = encodingType;
+ fillProfile(channelLayouts, sampleRates, &profile);
+ return profile;
+}
+
+AudioPortExt createDeviceExt(AudioDeviceType devType, int32_t flags,
+ const std::string& connection = "", const std::string& address = "") {
+ AudioPortDeviceExt deviceExt;
+ deviceExt.device.type.type = devType;
+ if (devType == AudioDeviceType::IN_MICROPHONE && connection.empty()) {
+ deviceExt.device.address = "bottom";
+ } else if (devType == AudioDeviceType::IN_MICROPHONE_BACK && connection.empty()) {
+ deviceExt.device.address = "back";
+ } else {
+ deviceExt.device.address = std::move(address);
+ }
+ deviceExt.device.type.connection = std::move(connection);
+ deviceExt.flags = flags;
+ return AudioPortExt::make<AudioPortExt::Tag::device>(deviceExt);
+}
+
+AudioPort createPort(int32_t id, const std::string& name, int32_t flags, bool isInput,
+ const AudioPortExt& ext) {
+ AudioPort port;
+ port.id = id;
+ port.name = name;
+ port.flags = isInput ? AudioIoFlags::make<AudioIoFlags::Tag::input>(flags)
+ : AudioIoFlags::make<AudioIoFlags::Tag::output>(flags);
+ port.ext = ext;
+ return port;
+}
+
+AudioGain createGain(int32_t mode, AudioChannelLayout channelMask, int32_t minValue,
+ int32_t maxValue, int32_t defaultValue, int32_t stepValue,
+ int32_t minRampMs = 100, int32_t maxRampMs = 100, bool useForVolume = true) {
+ AudioGain gain;
+ gain.mode = mode;
+ gain.channelMask = channelMask;
+ gain.minValue = minValue;
+ gain.maxValue = maxValue;
+ gain.defaultValue = defaultValue;
+ gain.stepValue = stepValue;
+ gain.minRampMs = minRampMs;
+ gain.maxRampMs = maxRampMs;
+ gain.useForVolume = useForVolume;
+ return gain;
+}
+} // namespace
+
ndk::ScopedAStatus AudioControl::registerFocusListener(
const shared_ptr<IFocusListener>& in_listener) {
LOG(DEBUG) << "registering focus listener";
@@ -190,6 +282,33 @@
return ndk::ScopedAStatus::ok();
}
+ndk::ScopedAStatus AudioControl::setModuleChangeCallback(
+ const std::shared_ptr<IModuleChangeCallback>& in_callback) {
+ LOG(DEBUG) << ": " << __func__;
+
+ if (in_callback.get() == nullptr) {
+ LOG(ERROR) << __func__ << ": Callback is nullptr";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
+ }
+ if (mModuleChangeCallback != nullptr) {
+ LOG(ERROR) << __func__ << ": Module change callback was already registered";
+ return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+ }
+ std::atomic_store(&mModuleChangeCallback, in_callback);
+ return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus AudioControl::clearModuleChangeCallback() {
+ if (mModuleChangeCallback != nullptr) {
+ shared_ptr<IModuleChangeCallback> nullCallback = nullptr;
+ std::atomic_store(&mModuleChangeCallback, nullCallback);
+ LOG(DEBUG) << ":" << __func__ << ": Unregistered successfully";
+ } else {
+ LOG(DEBUG) << ":" << __func__ << ": No callback registered, no-op";
+ }
+ return ndk::ScopedAStatus::ok();
+}
+
binder_status_t AudioControl::dump(int fd, const char** args, uint32_t numArgs) {
if (numArgs == 0) {
return dumpsys(fd);
@@ -208,6 +327,8 @@
return cmdAbandonFocusWithMetaData(fd, args, numArgs);
} else if (EqualsIgnoreCase(option, "--audioGainCallback")) {
return cmdOnAudioDeviceGainsChanged(fd, args, numArgs);
+ } else if (EqualsIgnoreCase(option, "--audioPortsChangedCallback")) {
+ return cmdOnAudioPortsChanged(fd, args, numArgs);
} else {
dprintf(fd, "Invalid option: %s\n", option.c_str());
return STATUS_BAD_VALUE;
@@ -262,7 +383,21 @@
dprintf(fd,
"Tags are optional. If provided, it must follow the <key>=<value> pattern, where the "
"value is namespaced (for example com.google.strategy=VR).\n");
-
+ dprintf(fd,
+ "--audioPortsChangedCallback <ID_1> <NAME_1> <BUS_ADDRESS_1> <CONNECTION_TYPE_1> "
+ "<AUDIO_GAINS_1> [<ID_N> <NAME_N> <BUS_ADDRESS_N> <CONNECTION_TYPE_N> "
+ "<AUDIO_GAINS_N>]: fires audio ports changed callback. Carries list of modified "
+ "AudioPorts. "
+ "For simplicity, this command accepts limited information for each AudioPort: "
+ "id(int), name(string), port address(string), connection type (string), "
+ "audio gain (csv int)\n");
+ dprintf(fd, "Notes: \n");
+ dprintf(fd,
+ "1. AudioGain csv should match definition at "
+ "android/media/audio/common/AudioPort.aidl\n");
+ dprintf(fd,
+ "2. See android/media/audio/common/AudioDeviceDescription.aidl for valid "
+ "<CONNECTION_TYPE> values.\n");
return STATUS_OK;
}
@@ -514,4 +649,166 @@
toEnumString(reasons).c_str(), toString(agcis).c_str());
return STATUS_OK;
}
+
+binder_status_t AudioControl::parseAudioGains(int fd, const std::string& stringGain,
+ std::vector<AudioGain>& gains) {
+ const int kAudioGainSize = 9;
+ std::stringstream csvGain(stringGain);
+ std::vector<std::string> vecGain;
+ std::string value;
+ while (getline(csvGain, value, ',')) {
+ vecGain.push_back(value);
+ }
+
+ if ((vecGain.size() == 0) || ((vecGain.size() % kAudioGainSize) != 0)) {
+ dprintf(fd, "Erroneous input to generate AudioGain: %s\n", stringGain.c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ // iterate over injected AudioGains
+ for (int index = 0; index < vecGain.size(); index += kAudioGainSize) {
+ int32_t mode;
+ if (!safelyParseInt(vecGain[index], &mode)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n", vecGain[index].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ // car audio framework only supports JOINT mode.
+ // skip injected AudioGains that are not compliant with this.
+ if (mode != static_cast<int>(AudioGainMode::JOINT)) {
+ LOG(WARNING) << ":" << __func__ << ": skipping gain since it is not JOINT mode!";
+ continue;
+ }
+
+ int32_t layout;
+ if (!safelyParseInt(vecGain[index + 1], &layout)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 1].c_str());
+ return STATUS_BAD_VALUE;
+ }
+ AudioChannelLayout channelMask =
+ AudioChannelLayout::make<AudioChannelLayout::layoutMask>(layout);
+
+ int32_t minValue;
+ if (!safelyParseInt(vecGain[index + 2], &minValue)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 2].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ int32_t maxValue;
+ if (!safelyParseInt(vecGain[index + 3], &maxValue)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 3].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ int32_t defaultValue;
+ if (!safelyParseInt(vecGain[index + 4], &defaultValue)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 4].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ int32_t stepValue;
+ if (!safelyParseInt(vecGain[index + 5], &stepValue)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 5].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ int32_t minRampMs;
+ if (!safelyParseInt(vecGain[index + 6], &minRampMs)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 6].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ int32_t maxRampMs;
+ if (!safelyParseInt(vecGain[index + 7], &maxRampMs)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ vecGain[index + 7].c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ ParseBoolResult useForVolume = ParseBool(vecGain[index + 8]);
+ if (useForVolume == ParseBoolResult::kError) {
+ dprintf(fd, "Non-boolean index provided with request: %s\n",
+ vecGain[index + 8].c_str());
+ return STATUS_BAD_VALUE;
+ } else if (useForVolume == ParseBoolResult::kFalse) {
+ // at this level we only care about gain stages that are relevant
+ // for volume control. skip the gain stage if its flagged as false.
+ LOG(WARNING) << ":" << __func__
+ << ": skipping gain since it is not for volume control!";
+ continue;
+ }
+
+ AudioGain gain = createGain(mode, channelMask, minValue, maxValue, defaultValue, stepValue,
+ minRampMs, maxRampMs, true /*useForVolume*/);
+ gains.push_back(gain);
+ }
+ return STATUS_OK;
+}
+
+binder_status_t AudioControl::cmdOnAudioPortsChanged(int fd, const char** args, uint32_t numArgs) {
+ if (!checkCallerHasWritePermissions(fd)) {
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ if ((numArgs < 6) || ((numArgs - 1) % 5 != 0)) {
+ dprintf(fd,
+ "Invalid number of arguments: please provide\n"
+ "--audioPortsChangedCallback <ID_1> <NAME_1> <BUS_ADDRESS_1> <CONNECTION_TYPE_1> "
+ "<AUDIO_GAINS_1> [<ID_N> <NAME_N> <BUS_ADDRESS_N> <CONNECTION_TYPE_N> "
+ "<AUDIO_GAINS_N>]: triggers audio ports changed callback. Carries list of "
+ "modified AudioPorts. "
+ "For simplicity, this command accepts limited information for each AudioPort: "
+ "id(int), name(string), port address(string), connection type (string), "
+ "audio gain (csv int)\n");
+ return STATUS_BAD_VALUE;
+ }
+
+ std::vector<AudioPort> ports;
+ for (uint32_t i = 1; i < numArgs; i += 5) {
+ binder_status_t status;
+ int32_t id;
+ if (!safelyParseInt(std::string(args[i]), &id)) {
+ dprintf(fd, "Non-integer index provided with request: %s\n",
+ std::string(args[i]).c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ std::string name = std::string(args[i + 1]);
+ std::string address = std::string(args[i + 2]);
+ std::string connection = std::string(args[i + 3]);
+
+ std::string stringGains = std::string(args[i + 4]);
+ std::vector<AudioGain> gains;
+ status = parseAudioGains(fd, stringGains, gains);
+ if (status != STATUS_OK) {
+ return status;
+ }
+
+ AudioPort port = createPort(
+ id, name, 0 /*flags*/, false /*isInput*/,
+ createDeviceExt(AudioDeviceType::OUT_DEVICE, 0 /*flags*/, connection, address));
+ port.gains.insert(port.gains.begin(), gains.begin(), gains.end());
+
+ ports.push_back(port);
+ }
+
+ if (mModuleChangeCallback == nullptr) {
+ dprintf(fd,
+ "Unable to trigger audio port callback for ports: %s \n"
+ " - no module change callback registered\n",
+ toString(ports).c_str());
+ return STATUS_BAD_VALUE;
+ }
+
+ // TODO (b/269139706) fix atomic read issue
+ mModuleChangeCallback->onAudioPortsChanged(ports);
+ dprintf(fd, "SUCCESS audio port callback for ports: %s \n", toString(ports).c_str());
+ return STATUS_OK;
+}
} // namespace aidl::android::hardware::automotive::audiocontrol
diff --git a/automotive/audiocontrol/aidl/default/AudioControl.h b/automotive/audiocontrol/aidl/default/AudioControl.h
index 16b80e8..7eca446 100644
--- a/automotive/audiocontrol/aidl/default/AudioControl.h
+++ b/automotive/audiocontrol/aidl/default/AudioControl.h
@@ -21,11 +21,20 @@
#include <aidl/android/hardware/automotive/audiocontrol/BnAudioControl.h>
#include <aidl/android/hardware/automotive/audiocontrol/DuckingInfo.h>
#include <aidl/android/hardware/automotive/audiocontrol/IAudioGainCallback.h>
+#include <aidl/android/hardware/automotive/audiocontrol/IModuleChangeCallback.h>
#include <aidl/android/hardware/automotive/audiocontrol/MutingInfo.h>
#include <aidl/android/hardware/automotive/audiocontrol/Reasons.h>
#include <aidl/android/hardware/audio/common/PlaybackTrackMetadata.h>
+#include <aidl/android/media/audio/common/AudioChannelLayout.h>
+#include <aidl/android/media/audio/common/AudioDeviceType.h>
+#include <aidl/android/media/audio/common/AudioFormatDescription.h>
+#include <aidl/android/media/audio/common/AudioFormatType.h>
+#include <aidl/android/media/audio/common/AudioGainMode.h>
+#include <aidl/android/media/audio/common/AudioIoFlags.h>
+#include <aidl/android/media/audio/common/AudioOutputFlags.h>
+
namespace aidl::android::hardware::automotive::audiocontrol {
namespace audiohalcommon = ::aidl::android::hardware::audio::common;
@@ -51,6 +60,9 @@
const std::vector<AudioGainConfigInfo>& in_gains) override;
ndk::ScopedAStatus registerGainCallback(
const std::shared_ptr<IAudioGainCallback>& in_callback) override;
+ ndk::ScopedAStatus setModuleChangeCallback(
+ const std::shared_ptr<IModuleChangeCallback>& in_callback) override;
+ ndk::ScopedAStatus clearModuleChangeCallback() override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
@@ -67,15 +79,23 @@
*/
std::shared_ptr<IAudioGainCallback> mAudioGainCallback = nullptr;
+ std::shared_ptr<IModuleChangeCallback> mModuleChangeCallback = nullptr;
+
binder_status_t cmdHelp(int fd) const;
binder_status_t cmdRequestFocus(int fd, const char** args, uint32_t numArgs);
binder_status_t cmdAbandonFocus(int fd, const char** args, uint32_t numArgs);
binder_status_t cmdRequestFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
binder_status_t cmdAbandonFocusWithMetaData(int fd, const char** args, uint32_t numArgs);
binder_status_t cmdOnAudioDeviceGainsChanged(int fd, const char** args, uint32_t numArgs);
+ binder_status_t cmdOnAudioPortsChanged(int fd, const char** args, uint32_t numArgs);
binder_status_t parseMetaData(int fd, const std::string& metadataLiteral,
audiohalcommon::PlaybackTrackMetadata& trackMetadata);
+ binder_status_t parseAudioGains(
+ int fd, const std::string& stringGain,
+ std::vector<::aidl::android::media::audio::common::AudioGain>& gains);
+ binder_status_t parseSampleRates(int fd, const std::string& sampleRates,
+ std::vector<int32_t>& vecSampleRates);
binder_status_t dumpsys(int fd);
};
diff --git a/automotive/audiocontrol/aidl/vts/Android.bp b/automotive/audiocontrol/aidl/vts/Android.bp
index edac160..a9122ce 100644
--- a/automotive/audiocontrol/aidl/vts/Android.bp
+++ b/automotive/audiocontrol/aidl/vts/Android.bp
@@ -39,7 +39,7 @@
"libxml2",
],
static_libs: [
- "android.hardware.automotive.audiocontrol-V2-cpp",
+ "android.hardware.automotive.audiocontrol-V3-cpp",
"libgmock",
],
test_suites: [
diff --git a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
index f4f5eef..f4e3b5a 100644
--- a/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
+++ b/automotive/audiocontrol/aidl/vts/VtsHalAudioControlTargetTest.cpp
@@ -21,6 +21,7 @@
#include <android/hardware/automotive/audiocontrol/BnAudioGainCallback.h>
#include <android/hardware/automotive/audiocontrol/BnFocusListener.h>
+#include <android/hardware/automotive/audiocontrol/BnModuleChangeCallback.h>
#include <android/hardware/automotive/audiocontrol/IAudioControl.h>
#include <android/log.h>
#include <binder/IServiceManager.h>
@@ -34,10 +35,14 @@
using android::hardware::automotive::audiocontrol::AudioGainConfigInfo;
using android::hardware::automotive::audiocontrol::BnAudioGainCallback;
using android::hardware::automotive::audiocontrol::BnFocusListener;
+using android::hardware::automotive::audiocontrol::BnModuleChangeCallback;
using android::hardware::automotive::audiocontrol::DuckingInfo;
using android::hardware::automotive::audiocontrol::IAudioControl;
+using android::hardware::automotive::audiocontrol::IModuleChangeCallback;
using android::hardware::automotive::audiocontrol::MutingInfo;
using android::hardware::automotive::audiocontrol::Reasons;
+using ::testing::AnyOf;
+using ::testing::Eq;
#include "android_audio_policy_configuration_V7_0.h"
@@ -55,6 +60,8 @@
ASSERT_NE(audioControl, nullptr);
}
+ void TearDown() override { audioControl = nullptr; }
+
sp<IAudioControl> audioControl;
int32_t capabilities;
};
@@ -226,6 +233,47 @@
ASSERT_TRUE(audioControl->registerGainCallback(gainCallback2).isOk());
}
+/*
+ * Test Module change Callback registration.
+ *
+ * Verifies that:
+ * - setModuleChangeCallback succeeds
+ * - setting a double callback fails with exception
+ * - clearModuleChangeCallback succeeds
+ * - setting with nullptr callback fails with exception
+ * - closing handle does not crash
+ */
+struct ModuleChangeCallbackMock : BnModuleChangeCallback {
+ MOCK_METHOD(Status, onAudioPortsChanged,
+ (const std::vector<android::media::audio::common::AudioPort>& audioPorts));
+};
+
+TEST_P(AudioControlAidl, RegisterModuleChangeCallbackTwiceThrowsException) {
+ ALOGI("Register Module change callback test");
+ // make sure no stale callbacks.
+ audioControl->clearModuleChangeCallback();
+
+ sp<ModuleChangeCallbackMock> moduleChangeCallback = new ModuleChangeCallbackMock();
+ auto status = audioControl->setModuleChangeCallback(moduleChangeCallback);
+ EXPECT_THAT(status.exceptionCode(),
+ AnyOf(Eq(Status::EX_NONE), Eq(Status::EX_UNSUPPORTED_OPERATION)));
+ if (!status.isOk()) return;
+
+ sp<ModuleChangeCallbackMock> moduleChangeCallback2 = new ModuleChangeCallbackMock();
+ // no need to check for unsupported feature
+ EXPECT_EQ(Status::EX_ILLEGAL_STATE,
+ audioControl->setModuleChangeCallback(moduleChangeCallback2).exceptionCode());
+ ASSERT_TRUE(audioControl->clearModuleChangeCallback().isOk());
+ ASSERT_TRUE(audioControl->setModuleChangeCallback(moduleChangeCallback2).isOk());
+}
+
+TEST_P(AudioControlAidl, RegisterModuleChangeNullCallbackThrowsException) {
+ ALOGI("Register Module change callback with nullptr test");
+ auto status = audioControl->setModuleChangeCallback(nullptr);
+ EXPECT_THAT(status.exceptionCode(),
+ AnyOf(Eq(Status::EX_ILLEGAL_ARGUMENT), Eq(Status::EX_UNSUPPORTED_OPERATION)));
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioControlAidl);
INSTANTIATE_TEST_SUITE_P(
Audiocontrol, AudioControlAidl,