blob: e10a7167f8d099ed0e2759f2d1ced2e6cbb590d2 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <memory>
#include <set>
#include <sys/wait.h>
#include <unistd.h>
#include <gtest/gtest.h>
#define LOG_TAG "APM_Test"
#include <log/log.h>
#include <media/PatchBuilder.h>
#include "AudioPolicyTestClient.h"
#include "AudioPolicyTestManager.h"
using namespace android;
TEST(AudioPolicyManagerTestInit, EngineFailure) {
AudioPolicyTestClient client;
AudioPolicyTestManager manager(&client);
manager.getConfig().setDefault();
manager.getConfig().setEngineLibraryNameSuffix("non-existent");
ASSERT_EQ(NO_INIT, manager.initialize());
ASSERT_EQ(NO_INIT, manager.initCheck());
}
TEST(AudioPolicyManagerTestInit, ClientFailure) {
AudioPolicyTestClient client;
AudioPolicyTestManager manager(&client);
manager.getConfig().setDefault();
// Since the default client fails to open anything,
// APM should indicate that the initialization didn't succeed.
ASSERT_EQ(NO_INIT, manager.initialize());
ASSERT_EQ(NO_INIT, manager.initCheck());
}
class AudioPolicyManagerTestClient : public AudioPolicyTestClient {
public:
// AudioPolicyClientInterface implementation
audio_module_handle_t loadHwModule(const char* /*name*/) override {
return mNextModuleHandle++;
}
status_t openOutput(audio_module_handle_t module,
audio_io_handle_t* output,
audio_config_t* /*config*/,
audio_devices_t* /*devices*/,
const String8& /*address*/,
uint32_t* /*latencyMs*/,
audio_output_flags_t /*flags*/) override {
if (module >= mNextModuleHandle) {
ALOGE("%s: Module handle %d has not been allocated yet (next is %d)",
__func__, module, mNextModuleHandle);
return BAD_VALUE;
}
*output = mNextIoHandle++;
return NO_ERROR;
}
status_t openInput(audio_module_handle_t module,
audio_io_handle_t* input,
audio_config_t* /*config*/,
audio_devices_t* /*device*/,
const String8& /*address*/,
audio_source_t /*source*/,
audio_input_flags_t /*flags*/) override {
if (module >= mNextModuleHandle) {
ALOGE("%s: Module handle %d has not been allocated yet (next is %d)",
__func__, module, mNextModuleHandle);
return BAD_VALUE;
}
*input = mNextIoHandle++;
return NO_ERROR;
}
status_t createAudioPatch(const struct audio_patch* /*patch*/,
audio_patch_handle_t* handle,
int /*delayMs*/) override {
*handle = mNextPatchHandle++;
mActivePatches.insert(*handle);
return NO_ERROR;
}
status_t releaseAudioPatch(audio_patch_handle_t handle,
int /*delayMs*/) override {
if (mActivePatches.erase(handle) != 1) {
if (handle >= mNextPatchHandle) {
ALOGE("%s: Patch handle %d has not been allocated yet (next is %d)",
__func__, handle, mNextPatchHandle);
} else {
ALOGE("%s: Attempt to release patch %d twice", __func__, handle);
}
return BAD_VALUE;
}
return NO_ERROR;
}
// Helper methods for tests
size_t getActivePatchesCount() const { return mActivePatches.size(); }
private:
audio_module_handle_t mNextModuleHandle = AUDIO_MODULE_HANDLE_NONE + 1;
audio_io_handle_t mNextIoHandle = AUDIO_IO_HANDLE_NONE + 1;
audio_patch_handle_t mNextPatchHandle = AUDIO_PATCH_HANDLE_NONE + 1;
std::set<audio_patch_handle_t> mActivePatches;
};
class PatchCountCheck {
public:
explicit PatchCountCheck(AudioPolicyManagerTestClient *client)
: mClient{client},
mInitialCount{mClient->getActivePatchesCount()} {}
int deltaFromSnapshot() const {
size_t currentCount = mClient->getActivePatchesCount();
if (mInitialCount <= currentCount) {
return currentCount - mInitialCount;
} else {
return -(static_cast<int>(mInitialCount - currentCount));
}
}
private:
const AudioPolicyManagerTestClient *mClient;
const size_t mInitialCount;
};
class AudioPolicyManagerTest : public testing::Test {
protected:
void SetUp() override;
void TearDown() override;
virtual void SetUpConfig(AudioPolicyConfig *config) { (void)config; }
void dumpToLog();
void getOutputForAttr(
audio_port_handle_t *selectedDeviceId,
audio_format_t format,
int channelMask,
int sampleRate,
audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE,
audio_port_handle_t *portId = nullptr);
PatchCountCheck snapshotPatchCount() { return PatchCountCheck(mClient.get()); }
std::unique_ptr<AudioPolicyManagerTestClient> mClient;
std::unique_ptr<AudioPolicyTestManager> mManager;
};
void AudioPolicyManagerTest::SetUp() {
mClient.reset(new AudioPolicyManagerTestClient);
mManager.reset(new AudioPolicyTestManager(mClient.get()));
mManager->getConfig().setDefault();
SetUpConfig(&mManager->getConfig()); // Subclasses may want to customize the config.
ASSERT_EQ(NO_ERROR, mManager->initialize());
ASSERT_EQ(NO_ERROR, mManager->initCheck());
}
void AudioPolicyManagerTest::TearDown() {
mManager.reset();
mClient.reset();
}
void AudioPolicyManagerTest::dumpToLog() {
int pipefd[2];
ASSERT_NE(-1, pipe(pipefd));
pid_t cpid = fork();
ASSERT_NE(-1, cpid);
if (cpid == 0) {
// Child process reads from the pipe and logs.
close(pipefd[1]);
std::string line;
char buf;
while (read(pipefd[0], &buf, sizeof(buf)) > 0) {
if (buf != '\n') {
line += buf;
} else {
ALOGI("%s", line.c_str());
line = "";
}
}
if (!line.empty()) ALOGI("%s", line.c_str());
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else {
// Parent does the dump and checks the status code.
close(pipefd[0]);
ASSERT_EQ(NO_ERROR, mManager->dump(pipefd[1]));
close(pipefd[1]);
wait(NULL); // Wait for the child to exit.
}
}
void AudioPolicyManagerTest::getOutputForAttr(
audio_port_handle_t *selectedDeviceId,
audio_format_t format,
int channelMask,
int sampleRate,
audio_output_flags_t flags,
audio_port_handle_t *portId) {
audio_attributes_t attr = {};
audio_io_handle_t output = AUDIO_PORT_HANDLE_NONE;
audio_stream_type_t stream = AUDIO_STREAM_DEFAULT;
audio_config_t config = AUDIO_CONFIG_INITIALIZER;
config.sample_rate = sampleRate;
config.channel_mask = channelMask;
config.format = format;
*selectedDeviceId = AUDIO_PORT_HANDLE_NONE;
audio_port_handle_t localPortId;
if (!portId) portId = &localPortId;
*portId = AUDIO_PORT_HANDLE_NONE;
ASSERT_EQ(OK, mManager->getOutputForAttr(
&attr, &output, AUDIO_SESSION_NONE, &stream, 0 /*uid*/, &config, &flags,
selectedDeviceId, portId, {}));
ASSERT_NE(AUDIO_PORT_HANDLE_NONE, *portId);
}
TEST_F(AudioPolicyManagerTest, InitSuccess) {
// SetUp must finish with no assertions.
}
TEST_F(AudioPolicyManagerTest, Dump) {
dumpToLog();
}
TEST_F(AudioPolicyManagerTest, CreateAudioPatchFailure) {
audio_patch patch{};
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
const PatchCountCheck patchCount = snapshotPatchCount();
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(nullptr, &handle, 0));
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, nullptr, 0));
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0));
patch.num_sources = AUDIO_PATCH_PORTS_MAX + 1;
patch.num_sinks = 1;
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0));
patch.num_sources = 1;
patch.num_sinks = AUDIO_PATCH_PORTS_MAX + 1;
ASSERT_EQ(BAD_VALUE, mManager->createAudioPatch(&patch, &handle, 0));
patch.num_sources = 2;
patch.num_sinks = 1;
ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0));
patch = {};
patch.num_sources = 1;
patch.sources[0].role = AUDIO_PORT_ROLE_SINK;
patch.num_sinks = 1;
patch.sinks[0].role = AUDIO_PORT_ROLE_SINK;
ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0));
patch = {};
patch.num_sources = 1;
patch.sources[0].role = AUDIO_PORT_ROLE_SOURCE;
patch.num_sinks = 1;
patch.sinks[0].role = AUDIO_PORT_ROLE_SOURCE;
ASSERT_EQ(INVALID_OPERATION, mManager->createAudioPatch(&patch, &handle, 0));
// Verify that the handle is left unchanged.
ASSERT_EQ(AUDIO_PATCH_HANDLE_NONE, handle);
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTest, CreateAudioPatchFromMix) {
audio_patch_handle_t handle = AUDIO_PATCH_HANDLE_NONE;
uid_t uid = 42;
const PatchCountCheck patchCount = snapshotPatchCount();
ASSERT_FALSE(mManager->getConfig().getAvailableInputDevices().isEmpty());
PatchBuilder patchBuilder;
patchBuilder.addSource(mManager->getConfig().getAvailableInputDevices()[0]).
addSink(mManager->getConfig().getDefaultOutputDevice());
ASSERT_EQ(NO_ERROR, mManager->createAudioPatch(patchBuilder.patch(), &handle, uid));
ASSERT_NE(AUDIO_PATCH_HANDLE_NONE, handle);
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
// TODO: Add patch creation tests that involve already existing patch
class AudioPolicyManagerTestMsd : public AudioPolicyManagerTest {
protected:
void SetUpConfig(AudioPolicyConfig *config) override;
void TearDown() override;
sp<DeviceDescriptor> mMsdOutputDevice;
sp<DeviceDescriptor> mMsdInputDevice;
};
void AudioPolicyManagerTestMsd::SetUpConfig(AudioPolicyConfig *config) {
// TODO: Consider using Serializer to load part of the config from a string.
mMsdOutputDevice = new DeviceDescriptor(AUDIO_DEVICE_OUT_BUS);
sp<AudioProfile> pcmOutputProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
sp<AudioProfile> ac3OutputProfile = new AudioProfile(
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000);
mMsdOutputDevice->addAudioProfile(pcmOutputProfile);
mMsdOutputDevice->addAudioProfile(ac3OutputProfile);
mMsdInputDevice = new DeviceDescriptor(AUDIO_DEVICE_IN_BUS);
// Match output profile from AudioPolicyConfig::setDefault.
sp<AudioProfile> pcmInputProfile = new AudioProfile(
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_IN_STEREO, 44100);
mMsdInputDevice->addAudioProfile(pcmInputProfile);
config->addAvailableDevice(mMsdOutputDevice);
config->addAvailableDevice(mMsdInputDevice);
sp<HwModule> msdModule = new HwModule(AUDIO_HARDWARE_MODULE_ID_MSD, 2 /*halVersionMajor*/);
HwModuleCollection modules = config->getHwModules();
modules.add(msdModule);
config->setHwModules(modules);
mMsdOutputDevice->attach(msdModule);
mMsdInputDevice->attach(msdModule);
sp<OutputProfile> msdOutputProfile = new OutputProfile(String8("msd input"));
msdOutputProfile->addAudioProfile(pcmOutputProfile);
msdOutputProfile->addSupportedDevice(mMsdOutputDevice);
msdModule->addOutputProfile(msdOutputProfile);
sp<OutputProfile> msdCompressedOutputProfile =
new OutputProfile(String8("msd compressed input"));
msdCompressedOutputProfile->addAudioProfile(ac3OutputProfile);
msdCompressedOutputProfile->setFlags(
AUDIO_OUTPUT_FLAG_DIRECT | AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
AUDIO_OUTPUT_FLAG_NON_BLOCKING);
msdCompressedOutputProfile->addSupportedDevice(mMsdOutputDevice);
msdModule->addOutputProfile(msdCompressedOutputProfile);
sp<InputProfile> msdInputProfile = new InputProfile(String8("msd output"));
msdInputProfile->addAudioProfile(pcmInputProfile);
msdInputProfile->addSupportedDevice(mMsdInputDevice);
msdModule->addInputProfile(msdInputProfile);
// Add a profile with another encoding to the default device to test routing
// of streams that are not supported by MSD.
sp<AudioProfile> dtsOutputProfile = new AudioProfile(
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000);
config->getDefaultOutputDevice()->addAudioProfile(dtsOutputProfile);
sp<OutputProfile> primaryEncodedOutputProfile = new OutputProfile(String8("encoded"));
primaryEncodedOutputProfile->addAudioProfile(dtsOutputProfile);
primaryEncodedOutputProfile->setFlags(AUDIO_OUTPUT_FLAG_DIRECT);
primaryEncodedOutputProfile->addSupportedDevice(config->getDefaultOutputDevice());
config->getHwModules().getModuleFromName(AUDIO_HARDWARE_MODULE_ID_PRIMARY)->
addOutputProfile(primaryEncodedOutputProfile);
}
void AudioPolicyManagerTestMsd::TearDown() {
mMsdOutputDevice.clear();
mMsdInputDevice.clear();
AudioPolicyManagerTest::TearDown();
}
TEST_F(AudioPolicyManagerTestMsd, InitSuccess) {
ASSERT_TRUE(mMsdOutputDevice);
ASSERT_TRUE(mMsdInputDevice);
}
TEST_F(AudioPolicyManagerTestMsd, Dump) {
dumpToLog();
}
TEST_F(AudioPolicyManagerTestMsd, PatchCreationOnSetForceUse) {
const PatchCountCheck patchCount = snapshotPatchCount();
mManager->setForceUse(AUDIO_POLICY_FORCE_FOR_ENCODED_SURROUND,
AUDIO_POLICY_FORCE_ENCODED_SURROUND_ALWAYS);
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrPcmRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrEncodedPlusPcmRoutesToMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_PCM_16_BIT, AUDIO_CHANNEL_OUT_STEREO, 48000);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrUnsupportedFormatBypassesMsd) {
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
TEST_F(AudioPolicyManagerTestMsd, GetOutputForAttrFormatSwitching) {
// Switch between formats that are supported and not supported by MSD.
{
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId, portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
&portId);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
ASSERT_EQ(1, patchCount.deltaFromSnapshot());
}
{
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId, portId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_DTS, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT,
&portId);
ASSERT_NE(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(-1, patchCount.deltaFromSnapshot());
mManager->releaseOutput(portId);
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
{
const PatchCountCheck patchCount = snapshotPatchCount();
audio_port_handle_t selectedDeviceId;
getOutputForAttr(&selectedDeviceId,
AUDIO_FORMAT_AC3, AUDIO_CHANNEL_OUT_5POINT1, 48000, AUDIO_OUTPUT_FLAG_DIRECT);
ASSERT_EQ(selectedDeviceId, mMsdOutputDevice->getId());
ASSERT_EQ(0, patchCount.deltaFromSnapshot());
}
}