Add libaudiousecasevalidation library

This library will verify the audio attributes usage for a track
that is connected to the specified stream.

It will only update the audio attributes usage for AUDIO_USAGE_MEDIA or
AUDIO_USAGE_UNKNOWN, to AUDIO_USAGE_GAME if output flags AUDIO_OUTPUT_FLAG_FAST
or AUDIO_OUTPUT_FLAG_MMAP_NOIRQ have been set for specified stream.

Bug: 257922898
Test: atest libaudiousecasevalidation-test

Change-Id: Ic2d89fa05467538abbc97d707478df1d70901fdb
diff --git a/media/libaudiousecasevalidation/Android.bp b/media/libaudiousecasevalidation/Android.bp
new file mode 100644
index 0000000..3ee7e32
--- /dev/null
+++ b/media/libaudiousecasevalidation/Android.bp
@@ -0,0 +1,49 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_av_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_av_license"],
+}
+
+cc_library {
+    name: "libaudiousecasevalidation",
+    host_supported: true,
+    srcs: [
+        "UsecaseLookup.cpp",
+        "UsecaseValidator.cpp",
+    ],
+    header_libs: [
+        "liberror_headers",
+    ],
+    shared_libs: [
+        "framework-permission-aidl-cpp",
+        "libaudioutils",
+        "libbase",
+        "liblog",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
+
+cc_test_host {
+    name: "libaudiousecasevalidation-test",
+    srcs: [
+        "tests/UsecaseValidator-test.cpp",
+    ],
+    header_libs: [
+        "liberror_headers",
+    ],
+    shared_libs: [
+        "framework-permission-aidl-cpp",
+        "libaudiousecasevalidation",
+        "libutils",
+    ],
+}
diff --git a/media/libaudiousecasevalidation/UsecaseLookup.cpp b/media/libaudiousecasevalidation/UsecaseLookup.cpp
new file mode 100644
index 0000000..9def180
--- /dev/null
+++ b/media/libaudiousecasevalidation/UsecaseLookup.cpp
@@ -0,0 +1,79 @@
+#define LOG_TAG "UsecaseLookup"
+// #define LOG_NDEBUG 0
+
+#include "media/UsecaseLookup.h"
+
+#include <utils/Log.h>
+
+namespace android {
+namespace media {
+
+/**
+ * Add streamId and outputFlags to stream list.
+ */
+void UsecaseLookup::addStream(STREAMID streamId, bool outputFlagGame) {
+    ALOGV("%s streamId: %d outputFlagGame: %d", __func__, streamId, outputFlagGame);
+
+    mutex_lock lock(m_mutex);
+    m_streams[streamId] = outputFlagGame;
+}
+
+/**
+ * Remove streamId from stream list.
+ */
+void UsecaseLookup::removeStream(STREAMID streamId) {
+    ALOGV("%s streamId: %d ", __func__, streamId);
+
+    mutex_lock lock(m_mutex);
+    m_streams.erase(streamId);
+
+    // Shouldn't happen but it might.
+    for (auto it = m_tracks.begin(); it != m_tracks.end();) {
+        if (it->second == streamId) {
+            it = m_tracks.erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+/**
+ * Add streamId and portId to track list.
+ */
+void UsecaseLookup::addTrack(STREAMID streamId, PORTID portId) {
+    ALOGV("%s streamId: %d portId: %d", __func__, streamId, portId);
+
+    mutex_lock lock(m_mutex);
+
+    if (m_tracks.find(portId) == m_tracks.end()) {
+        m_tracks[portId] = streamId;
+    }
+}
+
+/**
+ * Remove streamId and portId from track list.
+ */
+void UsecaseLookup::removeTrack(STREAMID streamId, PORTID portId) {
+    ALOGV("%s streamId: %d portId: %d", __func__, streamId, portId);
+
+    mutex_lock lock(m_mutex);
+    auto it = m_tracks.find(portId);
+
+    if (it != m_tracks.end() && it->second == streamId) {
+        m_tracks.erase(portId);
+    }
+}
+
+/**
+ * Check if stream list contains streamId with Game outputFlag.
+ */
+bool UsecaseLookup::isGameStream(STREAMID streamId) {
+    ALOGV("%s streamId: %d ", __func__, streamId);
+    mutex_lock lock(m_mutex);
+    auto it = m_streams.find(streamId);
+
+    return (it != m_streams.end()) ? it->second : false;
+}
+
+}  // namespace media
+}  // namespace android
diff --git a/media/libaudiousecasevalidation/UsecaseValidator.cpp b/media/libaudiousecasevalidation/UsecaseValidator.cpp
new file mode 100644
index 0000000..3a8a9c1
--- /dev/null
+++ b/media/libaudiousecasevalidation/UsecaseValidator.cpp
@@ -0,0 +1,128 @@
+#define LOG_TAG "UsecaseValidator"
+// #define LOG_NDEBUG 0
+
+#include <inttypes.h>
+
+#include <utils/Log.h>
+
+#include "media/UsecaseValidator.h"
+#include "media/UsecaseLookup.h"
+
+namespace android {
+namespace media {
+namespace {
+
+class UsecaseValidatorImpl : public UsecaseValidator {
+ public:
+    UsecaseValidatorImpl() {}
+
+    /**
+     * Register a new mixer/stream.
+     * Called when the stream is opened at the HAL and communicates
+     * immutable stream attributes like flags, sampling rate, format.
+     */
+    status_t registerStream(audio_io_handle_t streamId,
+                            const audio_config_base_t& audioConfig __attribute__((unused)),
+                            const audio_output_flags_t outputFlags) override {
+        ALOGV("%s output: %d flags: %#x", __func__, streamId, outputFlags);
+
+        // Check if FAST or MMAP output flag has been set.
+        bool outputFlagGame = outputFlags & (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ);
+        m_lookup.addStream(streamId, outputFlagGame);
+        return OK;
+    };
+
+    /**
+     * Unregister a stream/mixer.
+     * Called when the stream is closed.
+     */
+    status_t unregisterStream(audio_io_handle_t streamId) override {
+        ALOGV("%s output: %d", __func__, streamId);
+
+        m_lookup.removeStream(streamId);
+        return OK;
+    };
+
+    /**
+     * Indicates that some playback activity started on the stream.
+     * Called each time an audio track starts or resumes.
+     */
+    error::Result<audio_attributes_t> startClient(audio_io_handle_t streamId,
+            audio_port_handle_t portId, const content::AttributionSourceState& attributionSource,
+            const audio_attributes_t& attributes,
+            const AttributesChangedCallback *callback __attribute__((unused))) override {
+        ALOGV("%s output: %d portId: %d usage: %d pid: %d package: %s",
+                __func__, streamId, portId, attributes.usage, attributionSource.pid,
+                attributionSource.packageName.value_or("").c_str());
+
+        m_lookup.addTrack(streamId, portId);
+
+        return verifyAudioAttributes(streamId, attributionSource, attributes);
+    };
+
+    /**
+     * Indicates that some playback activity stopped on the stream.
+     * Called each time an audio track stops or pauses.
+     */
+    status_t stopClient(audio_io_handle_t streamId, audio_port_handle_t portId) override {
+        ALOGV("%s output: %d portId: %d", __func__, streamId, portId);
+
+        m_lookup.removeTrack(streamId, portId);
+        return OK;
+    };
+
+    /**
+     * Called to verify and update audio attributes for a track that is connected
+     * to the specified stream.
+     */
+    error::Result<audio_attributes_t> verifyAudioAttributes(audio_io_handle_t streamId,
+            const content::AttributionSourceState& attributionSource,
+            const audio_attributes_t& attributes) override {
+        ALOGV("%s output: %d usage: %d pid: %d package: %s",
+                __func__, streamId, attributes.usage, attributionSource.pid,
+                attributionSource.packageName.value_or("").c_str());
+
+        audio_attributes_t attrRet = attributes;
+
+        // Check if attribute usage media or unknown has been set.
+        bool isUsageValid = this->isUsageValid(attributes);
+
+        if (isUsageValid && m_lookup.isGameStream(streamId)) {
+            ALOGI("%s update usage: %d to AUDIO_USAGE_GAME for output: %d pid: %d package: %s",
+                    __func__, attributes.usage, streamId, attributionSource.pid,
+                    attributionSource.packageName.value_or("").c_str());
+            // Set attribute usage Game.
+            attrRet.usage = AUDIO_USAGE_GAME;
+        }
+
+        return {attrRet};
+    };
+
+ protected:
+    /**
+     * Check if attribute usage valid.
+     */
+    bool isUsageValid(const audio_attributes_t& attr) {
+        ALOGV("isUsageValid attr.usage: %d", attr.usage);
+        switch (attr.usage) {
+            case AUDIO_USAGE_MEDIA:
+            case AUDIO_USAGE_UNKNOWN:
+                return true;
+            default:
+                break;
+        }
+        return false;
+    }
+
+ protected:
+    UsecaseLookup m_lookup;
+};
+
+}  // namespace
+
+std::unique_ptr<UsecaseValidator> createUsecaseValidator() {
+    return std::make_unique<UsecaseValidatorImpl>();
+}
+
+}  // namespace media
+}  // namespace android
diff --git a/media/libaudiousecasevalidation/include/media/UsecaseLookup.h b/media/libaudiousecasevalidation/include/media/UsecaseLookup.h
new file mode 100644
index 0000000..9d1c987
--- /dev/null
+++ b/media/libaudiousecasevalidation/include/media/UsecaseLookup.h
@@ -0,0 +1,68 @@
+#ifndef MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASELOOKUP_H_
+#define MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASELOOKUP_H_
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+
+namespace android {
+namespace media {
+
+typedef int STREAMID;
+typedef int PORTID;
+
+// List of streamId and outputFlag state.
+typedef std::map<STREAMID, bool> STREAMLIST;
+// List of portId and streamId.
+typedef std::map<PORTID, STREAMID> TRACKLIST;
+typedef std::lock_guard<std::mutex> mutex_lock;
+
+class UsecaseLookup {
+ public:
+    UsecaseLookup() { }
+    virtual ~UsecaseLookup() { }
+
+    // Required for testing.
+    void clear() {
+        m_streams.clear();
+        m_tracks.clear();
+    }
+
+    /**
+     * Add streamId and outputFlag to stream list.
+     */
+    void addStream(STREAMID streamId, bool outputFlagGame = false);
+
+    /**
+     * Remove streamId from stream list.
+     */
+    void removeStream(STREAMID streamId);
+
+    /**
+     * Add streamId and portId to track list.
+     */
+    void addTrack(STREAMID streamId, PORTID portId);
+
+    /**
+     * Remove streamId and portId from track list.
+     */
+    void removeTrack(STREAMID streamId, PORTID portId);
+
+    /**
+     * Check if stream list contains streamId with Game output flag.
+     */
+    bool isGameStream(STREAMID streamId);
+
+ protected:
+    STREAMLIST m_streams;
+    TRACKLIST m_tracks;
+    std::mutex m_mutex;
+};
+
+}  // namespace media
+}  // namespace android
+
+#endif  // MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASELOOKUP_H_
diff --git a/media/libaudiousecasevalidation/include/media/UsecaseValidator.h b/media/libaudiousecasevalidation/include/media/UsecaseValidator.h
new file mode 100644
index 0000000..3f98423
--- /dev/null
+++ b/media/libaudiousecasevalidation/include/media/UsecaseValidator.h
@@ -0,0 +1,82 @@
+#ifndef MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASEVALIDATOR_H_
+#define MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASEVALIDATOR_H_
+
+#pragma once
+
+#include <error/Result.h>
+#include <system/audio.h>
+#include <android/content/AttributionSourceState.h>
+
+#include <limits>
+#include <memory>
+
+namespace android {
+namespace media {
+
+/**
+ * Main entry-point for this library.
+ */
+class UsecaseValidator {
+ public:
+    virtual ~UsecaseValidator() = default;
+
+    /**
+     * A callback called by the module when the audio attributes for
+     * an active portId changes.
+     */
+    class AttributesChangedCallback {
+     public:
+        virtual ~AttributesChangedCallback() = default;
+        virtual void onAttributesChanged(audio_port_handle_t portId,
+                                         const audio_attributes_t& attributes) = 0;
+    };
+
+    /**
+     * Register a new mixer/stream.
+     * Called when the stream is opened at the HAL  and communicates
+     * immutable stream attributes like flags, sampling rate, format.
+     */
+    virtual status_t registerStream(audio_io_handle_t streamId,
+                                    const audio_config_base_t& audioConfig,
+                                    const audio_output_flags_t outputFlags) = 0;
+
+    /**
+     * Unregister a stream/mixer.
+     * Called when the stream is closed.
+     */
+    virtual status_t unregisterStream(audio_io_handle_t streamId) = 0;
+
+    /**
+     * Indicates that some playback activity started on the stream.
+     * Called each time an audio track starts or resumes.
+     */
+    virtual error::Result<audio_attributes_t> startClient(audio_io_handle_t streamId,
+            audio_port_handle_t portId,
+            const content::AttributionSourceState& attributionSource,
+            const audio_attributes_t& attributes,
+            const AttributesChangedCallback *callback) = 0;
+
+    /**
+     * Indicates that some playback activity stopped on the stream.
+     * Called each time an audio track stops or pauses.
+     */
+    virtual status_t stopClient(audio_io_handle_t streamId, audio_port_handle_t portId) = 0;
+
+    /**
+     * Called to verify and update audio attributes for a track that is connected
+     * to the specified stream.
+     */
+    virtual error::Result<audio_attributes_t> verifyAudioAttributes(audio_io_handle_t streamId,
+            const content::AttributionSourceState& attributionSource,
+            const audio_attributes_t& attributes) = 0;
+};
+
+/**
+ * Creates an instance featuring a default implementation of the UsecaseValidator interface.
+ */
+std::unique_ptr<UsecaseValidator> createUsecaseValidator();
+
+}  // namespace media
+}  // namespace android
+
+#endif  // MEDIA_LIBAUDIOUSECASEVALIDATION_INCLUDE_MEDIA_USECASEVALIDATOR_H_
diff --git a/media/libaudiousecasevalidation/tests/UsecaseValidator-test.cpp b/media/libaudiousecasevalidation/tests/UsecaseValidator-test.cpp
new file mode 100644
index 0000000..f014068
--- /dev/null
+++ b/media/libaudiousecasevalidation/tests/UsecaseValidator-test.cpp
@@ -0,0 +1,215 @@
+#include "tests/UsecaseValidator-test.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace media {
+
+/**
+ * Helper test functions.
+ */
+
+/**
+ * Register a mock stream.
+ */
+audio_io_handle_t UsecaseValidatorTest::testRegisterStream(bool outputFlagGame) {
+    static int streamId = 0;
+    status_t result;
+    static audio_config_base_t audioConfig = AUDIO_CONFIG_BASE_INITIALIZER;
+    audio_output_flags_t outputFlags = outputFlagGame ? GAME_OUTPUT_FLAGS : MEDIA_OUTPUT_FLAGS;
+
+    result = m_validator->registerStream(++streamId, audioConfig, outputFlags);
+
+    return result == OK ? streamId : 0;
+}
+
+/**
+ * Create a mock portId.
+ */
+audio_port_handle_t UsecaseValidatorTest::testCreatePortId(audio_io_handle_t streamId) {
+    static int portId = 0;
+
+    return (streamId << 8) | (++portId);
+}
+
+/**
+ * Add a mock portId to a stream and verify.
+ */
+error::Result<audio_attributes_t> UsecaseValidatorTest::testStartClient(audio_io_handle_t streamId,
+        audio_port_handle_t portId,
+        audio_usage_t usage) {
+    content::AttributionSourceState attributionSource;
+    audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+    attributes.usage = usage;
+
+    return m_validator->startClient(streamId, portId, attributionSource, attributes, NULL);
+}
+
+/**
+ * Verify a mock stream.
+ */
+error::Result<audio_attributes_t> UsecaseValidatorTest::testVerifyAudioAttributes(
+        audio_io_handle_t streamId,
+        audio_usage_t usage) {
+    content::AttributionSourceState attributionSource;
+    audio_attributes_t attributes = AUDIO_ATTRIBUTES_INITIALIZER;
+    attributes.usage = usage;
+
+    return m_validator->verifyAudioAttributes(streamId, attributionSource, attributes);
+}
+
+/**
+ * Test functions.
+ */
+
+/**
+ * Test adding and removing streams.
+ */
+TEST_F(UsecaseLookupTest, testAddAndRemoveStream) {
+    addStream(1, false);
+    addStream(2, true);
+
+    EXPECT_NE(m_streams.find(1), m_streams.end());
+    EXPECT_NE(m_streams.find(2), m_streams.end());
+    EXPECT_EQ(m_streams.find(3), m_streams.end());
+
+    EXPECT_FALSE(isGameStream(1));
+    EXPECT_TRUE(isGameStream(2));
+    EXPECT_FALSE(isGameStream(3));
+
+    removeStream(2);
+
+    EXPECT_FALSE(isGameStream(2));
+}
+
+/**
+ * Verify attributes usage for stream.
+ */
+TEST_F(UsecaseValidatorTest, testAttributesUsage) {
+    audio_io_handle_t gameStreamId, mediaStreamId;
+
+    // Register game and media stream.
+    gameStreamId = testRegisterStream(true);
+    mediaStreamId = testRegisterStream(false);
+    EXPECT_NE(gameStreamId, 0);
+    EXPECT_NE(mediaStreamId, 0);
+    EXPECT_NE(gameStreamId, mediaStreamId);
+
+    // Verify attributes on game stream.
+    auto attr = testVerifyAudioAttributes(gameStreamId, AUDIO_USAGE_GAME);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_GAME);
+
+    // Verify attributes on media stream.
+    attr = testVerifyAudioAttributes(mediaStreamId, AUDIO_USAGE_MEDIA);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_MEDIA);
+
+    EXPECT_EQ(m_validator->unregisterStream(gameStreamId), 0);
+    EXPECT_EQ(m_validator->unregisterStream(mediaStreamId), 0);
+}
+
+/**
+ * Test hanging client.
+ */
+TEST_F(UsecaseValidatorTest, testHangingClient) {
+    audio_io_handle_t gameStreamId, mediaStreamId;
+    audio_port_handle_t gamePortId, mediaPortId;
+
+    // Register game and media stream.
+    gameStreamId = testRegisterStream(true);
+    EXPECT_NE(gameStreamId, 0);
+    mediaStreamId = testRegisterStream(false);
+    EXPECT_NE(mediaStreamId, 0);
+
+    // Assign portId.
+    gamePortId = testCreatePortId(gameStreamId);
+    EXPECT_NE(gamePortId, 0);
+    mediaPortId = testCreatePortId(mediaStreamId);
+    EXPECT_NE(mediaPortId, 0);
+
+    // Start client on game stream.
+    testStartClient(gameStreamId, gamePortId, AUDIO_USAGE_GAME);
+
+    // Start client on media stream.
+    testStartClient(mediaStreamId, mediaPortId, AUDIO_USAGE_MEDIA);
+
+    // Unregister media stream before stopClient.
+    EXPECT_EQ(m_validator->unregisterStream(gameStreamId), 0);
+    EXPECT_EQ(m_validator->unregisterStream(mediaStreamId), 0);
+}
+
+/**
+ * Verify attributes usage does not change.
+ */
+TEST_F(UsecaseValidatorTest, testAttributesUsageUnchanged) {
+    audio_io_handle_t gameStreamId, mediaStreamId;
+    audio_port_handle_t gamePortId, mediaPortId, unknownPortId, voiceCommPortId;
+
+    // Register game and media stream.
+    gameStreamId = testRegisterStream(true);
+    EXPECT_NE(gameStreamId, 0);
+    mediaStreamId = testRegisterStream(false);
+    EXPECT_NE(mediaStreamId, 0);
+
+    // Assign portId.
+    gamePortId = testCreatePortId(gameStreamId);
+    EXPECT_NE(gamePortId, 0);
+    mediaPortId = testCreatePortId(mediaStreamId);
+    EXPECT_NE(mediaPortId, 0);
+    unknownPortId = testCreatePortId(mediaStreamId);
+    EXPECT_NE(unknownPortId, 0);
+    voiceCommPortId = testCreatePortId(gameStreamId);
+    EXPECT_NE(voiceCommPortId, 0);
+
+    // Verify attributes on game stream.
+    auto attr = testStartClient(gameStreamId, gamePortId, AUDIO_USAGE_GAME);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_GAME);
+
+    attr = testStartClient(gameStreamId, voiceCommPortId, AUDIO_USAGE_VOICE_COMMUNICATION);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_VOICE_COMMUNICATION);
+
+    // Verify attributes on media stream.
+    attr = testStartClient(mediaStreamId, mediaPortId, AUDIO_USAGE_MEDIA);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_MEDIA);
+
+    attr = testStartClient(mediaStreamId, unknownPortId, AUDIO_USAGE_UNKNOWN);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_UNKNOWN);
+
+    // Stop client on game and media stream.
+    EXPECT_EQ(m_validator->stopClient(gameStreamId, gamePortId), 0);
+    EXPECT_EQ(m_validator->stopClient(mediaStreamId, mediaPortId), 0);
+
+    // Unregister game and media stream.
+    EXPECT_EQ(m_validator->unregisterStream(gameStreamId), 0);
+    EXPECT_EQ(m_validator->unregisterStream(mediaStreamId), 0);
+}
+
+/**
+ * Verify attributes usage changes.
+ */
+TEST_F(UsecaseValidatorTest, testAttributesUsageChanged) {
+    audio_io_handle_t gameStreamId;
+    audio_port_handle_t mediaPortId, unknownPortId;
+
+    // Register game and media stream.
+    gameStreamId = testRegisterStream(true);
+    EXPECT_NE(gameStreamId, 0);
+
+    // Assign portId.
+    mediaPortId = testCreatePortId(gameStreamId);
+    EXPECT_NE(mediaPortId, 0);
+    unknownPortId = testCreatePortId(gameStreamId);
+    EXPECT_NE(unknownPortId, 0);
+
+    // Verify attributes on game stream.
+    auto attr = testStartClient(gameStreamId, mediaPortId, AUDIO_USAGE_MEDIA);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_GAME);
+
+    attr = testStartClient(gameStreamId, unknownPortId, AUDIO_USAGE_UNKNOWN);
+    EXPECT_EQ(attr.value().usage, AUDIO_USAGE_GAME);
+
+    // Unregister game stream.
+    EXPECT_EQ(m_validator->unregisterStream(gameStreamId), 0);
+}
+
+}  // namespace media
+}  // namespace android
diff --git a/media/libaudiousecasevalidation/tests/UsecaseValidator-test.h b/media/libaudiousecasevalidation/tests/UsecaseValidator-test.h
new file mode 100644
index 0000000..89296ac
--- /dev/null
+++ b/media/libaudiousecasevalidation/tests/UsecaseValidator-test.h
@@ -0,0 +1,67 @@
+#ifndef MEDIA_LIBAUDIOUSECASEVALIDATION_TESTS_USECASEVALIDATOR_TEST_H_
+#define MEDIA_LIBAUDIOUSECASEVALIDATION_TESTS_USECASEVALIDATOR_TEST_H_
+
+#include <gtest/gtest.h>
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <set>
+
+#include "media/UsecaseLookup.h"
+#include "media/UsecaseValidator.h"
+
+namespace android {
+namespace media {
+
+#define MEDIA_OUTPUT_FLAGS (audio_output_flags_t)(0xFFFFF &\
+                                ~(AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ))
+
+#define GAME_OUTPUT_FLAGS (audio_output_flags_t)\
+                                (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)
+
+class TestCallback : public UsecaseValidator::AttributesChangedCallback {
+ public:
+    TestCallback() {
+        m_iCallCnt = 0;
+    }
+    virtual ~TestCallback() { }
+    virtual void onAttributesChanged(audio_port_handle_t /*portId*/,
+                                     const audio_attributes_t& /*attributes*/) {
+        ++m_iCallCnt;
+    }
+
+ public:
+    int m_iCallCnt;
+};
+
+class UsecaseLookupTest : public UsecaseLookup, public ::testing::Test {
+ public:
+    UsecaseLookupTest() { }
+    virtual ~UsecaseLookupTest() = default;
+};
+
+class UsecaseValidatorTest : public ::testing::Test {
+ public:
+    UsecaseValidatorTest() {
+        m_validator = createUsecaseValidator();
+    }
+
+    virtual ~UsecaseValidatorTest() = default;
+
+ protected:
+    audio_io_handle_t testRegisterStream(bool outputFlagGame);
+    audio_port_handle_t testCreatePortId(audio_io_handle_t streamId);
+    error::Result<audio_attributes_t> testStartClient(audio_io_handle_t streamId,
+                                                      audio_port_handle_t portId,
+                                                      audio_usage_t usage);
+    error::Result<audio_attributes_t> testVerifyAudioAttributes(audio_io_handle_t streamId,
+                                                                audio_usage_t usage);
+
+    std::unique_ptr<UsecaseValidator> m_validator;
+};
+
+}  // namespace media
+}  // namespace android
+
+#endif  // MEDIA_LIBAUDIOUSECASEVALIDATION_TESTS_USECASEVALIDATOR_TEST_H_