Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9ff55f50 authored by Mikhail Naganov's avatar Mikhail Naganov
Browse files

Update and clean up audiorouting_tests

The important change is in TestPerformanceMode to not require
the mix port serving a FAST track to have a FAST flag. This is
because a FastMixer potentially can be created on any port
which satisfies certain buffer size conditions.

Remove the code which reads the XML APM file. The test does not
use it (and shouldn't as the XML file may not exist with the AIDL
HAL).

Add tracing of test invocations to correspond logcat with
test start/completion.

Also, replace hand-written dumping function with invocations
of `toString` functions from AIDL parcelables.

Bug: 302036943
Test: atest AudioTrackTest
Change-Id: Ia81cf429af92b8ac9582abc7cf270b1ccb96019c
parent dcd31c41
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -114,7 +114,6 @@ cc_defaults {
        "libnblog",
        "libprocessgroup",
        "libshmemcompat",
        "libxml2",
        "mediametricsservice-aidl-cpp",
        "packagemanager_aidl-cpp",
        "shared-file-region-aidl-cpp",
+8 −190
Original line number Diff line number Diff line
@@ -26,25 +26,10 @@
#define WAIT_PERIOD_MS 10  // from AudioTrack.cpp
#define MAX_WAIT_TIME_MS 5000

template <class T>
constexpr void (*xmlDeleter)(T* t);
template <>
constexpr auto xmlDeleter<xmlDoc> = xmlFreeDoc;
template <>
constexpr auto xmlDeleter<xmlChar> = [](xmlChar* s) { xmlFree(s); };

/** @return a unique_ptr with the correct deleter for the libxml2 object. */
template <class T>
constexpr auto make_xmlUnique(T* t) {
    // Wrap deleter in lambda to enable empty base optimization
    auto deleter = [](T* t) { xmlDeleter<T>(t); };
    return std::unique_ptr<T, decltype(deleter)>{t, deleter};
}

void OnAudioDeviceUpdateNotifier::onAudioDeviceUpdate(audio_io_handle_t audioIo,
                                                      audio_port_handle_t deviceId) {
    std::unique_lock<std::mutex> lock{mMutex};
    ALOGD("%s  audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
    ALOGI("%s: audioIo=%d deviceId=%d", __func__, audioIo, deviceId);
    mAudioIo = audioIo;
    mDeviceId = deviceId;
    mCondition.notify_all();
@@ -727,184 +712,17 @@ bool checkPatchCapture(audio_io_handle_t audioIo, audio_port_handle_t deviceId)
}

std::string dumpPortConfig(const audio_port_config& port) {
    std::ostringstream result;
    std::string deviceInfo;
    if (port.type == AUDIO_PORT_TYPE_DEVICE) {
        if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
            InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
        } else {
            OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
        }
        deviceInfo += std::string(", address = ") + port.ext.device.address;
    }
    result << "audio_port_handle_t = " << port.id << ", "
           << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
           << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
           << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
           << "config_mask = 0x" << std::hex << port.config_mask << std::dec << ", ";
    if (port.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) {
        result << "sample rate = " << port.sample_rate << ", ";
    }
    if (port.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) {
        result << "channel mask = " << port.channel_mask << ", ";
    }
    if (port.config_mask & AUDIO_PORT_CONFIG_FORMAT) {
        result << "format = " << port.format << ", ";
    }
    result << "input flags = " << port.flags.input << ", ";
    result << "output flags = " << port.flags.output << ", ";
    result << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
           << "\n";
    return result.str();
    auto aidlPortConfig = legacy2aidl_audio_port_config_AudioPortConfigFw(port);
    return aidlPortConfig.ok() ? aidlPortConfig.value().toString()
                               : "Error while converting audio port config to AIDL";
}

std::string dumpPatch(const audio_patch& patch) {
    std::ostringstream result;
    result << "----------------- Dumping Patch ------------ \n";
    result << "Patch Handle: " << patch.id << ", sources: " << patch.num_sources
           << ", sink: " << patch.num_sinks << "\n";
    audio_port_v7 port;
    for (uint32_t i = 0; i < patch.num_sources; i++) {
        result << "----------------- Dumping Source Port Config @ index " << i
               << " ------------ \n";
        result << dumpPortConfig(patch.sources[i]);
        result << "----------------- Dumping Source Port for id " << patch.sources[i].id
               << " ------------ \n";
        getPortById(patch.sources[i].id, port);
        result << dumpPort(port);
    }
    for (uint32_t i = 0; i < patch.num_sinks; i++) {
        result << "----------------- Dumping Sink Port Config @ index " << i << " ------------ \n";
        result << dumpPortConfig(patch.sinks[i]);
        result << "----------------- Dumping Sink Port for id " << patch.sinks[i].id
               << " ------------ \n";
        getPortById(patch.sinks[i].id, port);
        result << dumpPort(port);
    }
    return result.str();
    auto aidlPatch = legacy2aidl_audio_patch_AudioPatchFw(patch);
    return aidlPatch.ok() ? aidlPatch.value().toString() : "Error while converting patch to AIDL";
}

std::string dumpPort(const audio_port_v7& port) {
    std::ostringstream result;
    std::string deviceInfo;
    if (port.type == AUDIO_PORT_TYPE_DEVICE) {
        if (port.ext.device.type & AUDIO_DEVICE_BIT_IN) {
            InputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
        } else {
            OutputDeviceConverter::maskToString(port.ext.device.type, deviceInfo);
        }
        deviceInfo += std::string(", address = ") + port.ext.device.address;
    }
    result << "audio_port_handle_t = " << port.id << ", "
           << "Role = " << (port.role == AUDIO_PORT_ROLE_SOURCE ? "source" : "sink") << ", "
           << "Type = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix") << ", "
           << "deviceInfo = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? deviceInfo : "") << ", "
           << "Name = " << port.name << ", "
           << "num profiles = " << port.num_audio_profiles << ", "
           << "mix io handle = " << (port.type == AUDIO_PORT_TYPE_DEVICE ? 0 : port.ext.mix.handle)
           << ", ";
    for (int i = 0; i < port.num_audio_profiles; i++) {
        result << "AudioProfile = " << i << " {";
        result << "format = " << port.audio_profiles[i].format << ", ";
        result << "samplerates = ";
        for (int j = 0; j < port.audio_profiles[i].num_sample_rates; j++) {
            result << port.audio_profiles[i].sample_rates[j] << ", ";
        }
        result << "channelmasks = ";
        for (int j = 0; j < port.audio_profiles[i].num_channel_masks; j++) {
            result << "0x" << std::hex << port.audio_profiles[i].channel_masks[j] << std::dec
                   << ", ";
        }
        result << "} ";
    }
    result << dumpPortConfig(port.active_config);
    return result.str();
}

std::string getXmlAttribute(const xmlNode* cur, const char* attribute) {
    auto charPtr = make_xmlUnique(xmlGetProp(cur, reinterpret_cast<const xmlChar*>(attribute)));
    if (charPtr == NULL) {
        return "";
    }
    std::string value(reinterpret_cast<const char*>(charPtr.get()));
    return value;
}

status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
                                              std::vector<MixPort>& mixPorts,
                                              std::vector<Route>& routes) {
    std::string path = audio_find_readable_configuration_file("audio_policy_configuration.xml");
    if (path.length() == 0) return UNKNOWN_ERROR;
    auto doc = make_xmlUnique(xmlParseFile(path.c_str()));
    if (doc == nullptr) return UNKNOWN_ERROR;
    xmlNode* root = xmlDocGetRootElement(doc.get());
    if (root == nullptr) return UNKNOWN_ERROR;
    if (xmlXIncludeProcess(doc.get()) < 0) return UNKNOWN_ERROR;
    mixPorts.clear();
    if (!xmlStrcmp(root->name, reinterpret_cast<const xmlChar*>("audioPolicyConfiguration"))) {
        std::string raw{getXmlAttribute(root, "version")};
        for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
            if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("modules"))) {
                xmlNode* root = child;
                for (auto* child = root->xmlChildrenNode; child != nullptr; child = child->next) {
                    if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>("module"))) {
                        xmlNode* root = child;
                        for (auto* child = root->xmlChildrenNode; child != nullptr;
                             child = child->next) {
                            if (!xmlStrcmp(child->name,
                                           reinterpret_cast<const xmlChar*>("mixPorts"))) {
                                xmlNode* root = child;
                                for (auto* child = root->xmlChildrenNode; child != nullptr;
                                     child = child->next) {
                                    if (!xmlStrcmp(child->name,
                                                   reinterpret_cast<const xmlChar*>("mixPort"))) {
                                        MixPort mixPort;
                                        xmlNode* root = child;
                                        mixPort.name = getXmlAttribute(root, "name");
                                        mixPort.role = getXmlAttribute(root, "role");
                                        mixPort.flags = getXmlAttribute(root, "flags");
                                        if (mixPort.role == "source") mixPorts.push_back(mixPort);
                                    }
                                }
                            } else if (!xmlStrcmp(child->name, reinterpret_cast<const xmlChar*>(
                                                                       "attachedDevices"))) {
                                xmlNode* root = child;
                                for (auto* child = root->xmlChildrenNode; child != nullptr;
                                     child = child->next) {
                                    if (!xmlStrcmp(child->name,
                                                   reinterpret_cast<const xmlChar*>("item"))) {
                                        auto xmlValue = make_xmlUnique(xmlNodeListGetString(
                                                child->doc, child->xmlChildrenNode, 1));
                                        if (xmlValue == nullptr) {
                                            raw = "";
                                        } else {
                                            raw = reinterpret_cast<const char*>(xmlValue.get());
                                        }
                                        std::string& value = raw;
                                        attachedDevices.push_back(std::move(value));
                                    }
                                }
                            } else if (!xmlStrcmp(child->name,
                                                  reinterpret_cast<const xmlChar*>("routes"))) {
                                xmlNode* root = child;
                                for (auto* child = root->xmlChildrenNode; child != nullptr;
                                     child = child->next) {
                                    if (!xmlStrcmp(child->name,
                                                   reinterpret_cast<const xmlChar*>("route"))) {
                                        Route route;
                                        xmlNode* root = child;
                                        route.name = getXmlAttribute(root, "name");
                                        route.sources = getXmlAttribute(root, "sources");
                                        route.sink = getXmlAttribute(root, "sink");
                                        routes.push_back(route);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return OK;
    auto aidlPort = legacy2aidl_audio_port_v7_AudioPortFw(port);
    return aidlPort.ok() ? aidlPort.value().toString() : "Error while converting port to AIDL";
}
+0 −5
Original line number Diff line number Diff line
@@ -28,8 +28,6 @@
#include <thread>

#include <binder/MemoryDealer.h>
#include <libxml/parser.h>
#include <libxml/xinclude.h>
#include <media/AidlConversion.h>
#include <media/AudioRecord.h>
#include <media/AudioTrack.h>
@@ -48,9 +46,6 @@ struct Route {
    std::string sink;
};

status_t parse_audio_policy_configuration_xml(std::vector<std::string>& attachedDevices,
                                              std::vector<MixPort>& mixPorts,
                                              std::vector<Route>& routes);
status_t listAudioPorts(std::vector<audio_port_v7>& portsVec);
status_t listAudioPatches(std::vector<struct audio_patch>& patchesVec);
status_t getPortByAttributes(audio_port_role_t role, audio_port_type_t type,
+36 −7
Original line number Diff line number Diff line
@@ -15,10 +15,13 @@
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "AudioRoutingTest"

#include <string.h>

#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <gtest/gtest.h>
#include <string.h>

#include "audio_test_utils.h"

@@ -63,12 +66,16 @@ TEST(AudioTrackTest, TestPerformanceMode) {
        EXPECT_NE(0, ap->getAudioTrackHandle()->getFlags() & output_flags[i]);
        audio_patch patch;
        EXPECT_EQ(OK, getPatchForOutputMix(cb->mAudioIo, patch));
        if (output_flags[i] != AUDIO_OUTPUT_FLAG_FAST) {
            // A "normal" output can still have a FastMixer, depending on the buffer size.
            // Thus, a fast track can be created on a mix port which does not have the FAST flag.
            for (auto j = 0; j < patch.num_sources; j++) {
                if (patch.sources[j].type == AUDIO_PORT_TYPE_MIX &&
                    patch.sources[j].ext.mix.handle == cb->mAudioIo) {
                if ((patch.sources[j].flags.output & output_flags[i]) == 0) {
                    ADD_FAILURE() << "expected output flag " << output_flags[i] << " is absent";
                    std::cerr << dumpPortConfig(patch.sources[j]);
                    SCOPED_TRACE(dumpPortConfig(patch.sources[j]));
                    EXPECT_NE(0, patch.sources[j].flags.output & output_flags[i])
                            << "expected output flag "
                            << audio_output_flag_to_string(output_flags[i]) << " is absent";
                }
            }
        }
@@ -259,3 +266,25 @@ TEST_F(AudioRoutingTest, ConcurrentDynamicRoutingTest) {
    captureA->stop();
    playback->stop();
}

class TestExecutionTracer : public ::testing::EmptyTestEventListener {
  public:
    void OnTestStart(const ::testing::TestInfo& test_info) override {
        TraceTestState("Started", test_info);
    }
    void OnTestEnd(const ::testing::TestInfo& test_info) override {
        TraceTestState("Completed", test_info);
    }

  private:
    static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info) {
        ALOGI("%s %s::%s", state.c_str(), test_info.test_suite_name(), test_info.name());
    }
};

int main(int argc, char** argv) {
    android::ProcessState::self()->startThreadPool();
    ::testing::InitGoogleTest(&argc, argv);
    ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
    return RUN_ALL_TESTS();
}