Add VTS for the audio primary device

Its base class was tested but not the primary
device getters and setters.

Test: run the corresponding vts test
Test: vts-tradefed r vts-hal-hidl -m VtsHalAudioV2_0Target

Bug: 34170075
Change-Id: Ic451761c7b8b29fd302a62aba83be6ce7be35f64
Signed-off-by: Kevin Rocard <krocard@google.com>
diff --git a/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
index d896788..a3f0d27 100644
--- a/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
+++ b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -32,6 +32,7 @@
 
 #include <android/hardware/audio/2.0/IDevice.h>
 #include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <android/hardware/audio/2.0/IPrimaryDevice.h>
 #include <android/hardware/audio/2.0/types.h>
 #include <android/hardware/audio/common/2.0/types.h>
 
@@ -50,6 +51,8 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::audio::V2_0::DeviceAddress;
 using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::IPrimaryDevice;
+using TtyMode = ::android::hardware::audio::V2_0::IPrimaryDevice::TtyMode;
 using ::android::hardware::audio::V2_0::IDevicesFactory;
 using ::android::hardware::audio::V2_0::IStream;
 using ::android::hardware::audio::V2_0::IStreamIn;
@@ -63,6 +66,7 @@
 using ::android::hardware::audio::common::V2_0::AudioHandleConsts;
 using ::android::hardware::audio::common::V2_0::AudioInputFlag;
 using ::android::hardware::audio::common::V2_0::AudioIoHandle;
+using ::android::hardware::audio::common::V2_0::AudioMode;
 using ::android::hardware::audio::common::V2_0::AudioOffloadInfo;
 using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
 using ::android::hardware::audio::common::V2_0::AudioSource;
@@ -154,19 +158,23 @@
         ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base
 
         if (device == nullptr) {
-            environment->registerTearDown([]{ device.clear(); });
             IDevicesFactory::Result result;
+            sp<IDevice> baseDevice;
             ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY,
-                                                 returnIn(result, device)));
+                                                 returnIn(result, baseDevice)));
+            ASSERT_TRUE(baseDevice != nullptr);
+
+            environment->registerTearDown([]{ device.clear(); });
+            device = IPrimaryDevice::castFrom(baseDevice);
+            ASSERT_TRUE(device != nullptr);
         }
-        ASSERT_TRUE(device != nullptr);
     }
 
 protected:
     // Cache the device opening to speed up each test by ~0.5s
-    static sp<IDevice> device;
+    static sp<IPrimaryDevice> device;
 };
-sp<IDevice> AudioPrimaryHidlTest::device;
+sp<IPrimaryDevice> AudioPrimaryHidlTest::device;
 
 TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) {
     doc::test("Test the openDevice (called in SetUp)");
@@ -234,42 +242,64 @@
 ////////////////////////// {set,get}{Master,Mic}Mute /////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-class BoolAccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
+template <class Property>
+class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
 protected:
+
+    /** Test a property getter and setter. */
     template <class Getter, class Setter>
-    void testBoolAccessors(const string& propertyName, const vector<bool>& valuesToTest,
-                           Setter setter, Getter getter) {
-        for (bool setState : valuesToTest) {
-            SCOPED_TRACE("Test " + propertyName + " state: " + to_string(setState));
-            ASSERT_OK((device.get()->*setter)(setState));
-            bool getState;
-            ASSERT_OK((device.get()->*getter)(returnIn(res, getState)));
+    void testAccessors(const string& propertyName, const vector<Property>& valuesToTest,
+                       Setter setter, Getter getter) {
+
+        Property initialValue; // Save initial value to restore it at the end of the test
+        ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue)));
+        ASSERT_OK(res);
+
+        for (Property setValue : valuesToTest) {
+            SCOPED_TRACE("Test " + propertyName + " getter and setter for " + testing::PrintToString(setValue));
+            ASSERT_OK((device.get()->*setter)(setValue));
+            Property getValue;
+            // Make sure the getter returns the same value just set
+            ASSERT_OK((device.get()->*getter)(returnIn(res, getValue)));
             ASSERT_OK(res);
-            ASSERT_EQ(setState, getState);
+            EXPECT_EQ(setValue, getValue);
         }
+
+        ASSERT_OK((device.get()->*setter)(initialValue)); // restore initial value
+    }
+
+    /** Test the getter and setter of an optional feature. */
+    template <class Getter, class Setter>
+    void testOptionalAccessors(const string& propertyName, const vector<Property>& valuesToTest,
+                               Setter setter, Getter getter) {
+        doc::test("Test the optional " + propertyName + " getters and setter");
+        {
+            SCOPED_TRACE("Test feature support by calling the getter");
+            Property initialValue;
+            ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue)));
+            if (res == Result::NOT_SUPPORTED) {
+                doc::partialTest(propertyName + " getter is not supported");
+                return;
+            }
+            ASSERT_OK(res); // If it is supported it must succeed
+        }
+        // The feature is supported, test it
+        testAccessors(propertyName, valuesToTest, setter, getter);
     }
 };
 
+using BoolAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<bool>;
+
 TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
     doc::test("Check that the mic can be muted and unmuted");
-    testBoolAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute);
+    testAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute);
     // TODO: check that the mic is really muted (all sample are 0)
 }
 
 TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
     doc::test("If master mute is supported, try to mute and unmute the master output");
-    {
-        SCOPED_TRACE("Check for master mute support");
-        auto ret = device->setMasterMute(false);
-        ASSERT_TRUE(ret.isOk());
-        if (ret == Result::NOT_SUPPORTED) {
-            doc::partialTest("Master mute is not supported");
-            return;
-        }
-    }
-    // NOTE: this code has never been tested on a platform supporting MasterMute
-    testBoolAccessors("master mute", {true, false, true},
-                      &IDevice::setMasterMute, &IDevice::getMasterMute);
+    testOptionalAccessors("master mute", {true, false, true},
+                          &IDevice::setMasterMute, &IDevice::getMasterMute);
     // TODO: check that the master volume is really muted
 }
 
@@ -690,6 +720,68 @@
 }
 
 //////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// PrimaryDevice ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, setVoiceVolume) {
+    doc::test("Make sure setVoiceVolume only succeed if volume is in [0,1]");
+    for (float volume : {0.0, 0.01, 0.5, 0.09, 1.0}) {
+        SCOPED_TRACE("volume=" + to_string(volume));
+        ASSERT_OK(device->setVoiceVolume(volume));
+    }
+    for (float volume : (float[]){-INFINITY,-1.0, -0.0,
+                                  1.0 + std::numeric_limits<float>::epsilon(), 2.0, INFINITY,
+                                  NAN}) {
+        SCOPED_TRACE("volume=" + to_string(volume));
+        // FIXME: NAN should never be accepted
+        // FIXME: Missing api doc. What should the impl do if the volume is outside [0,1] ?
+        ASSERT_INVALID_ARGUMENTS(device->setVoiceVolume(volume));
+    }
+}
+
+TEST_F(AudioPrimaryHidlTest, setMode) {
+    doc::test("Make sure setMode always succeeds if mode is valid");
+    for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION,
+                           AudioMode::RINGTONE, AudioMode::CURRENT,
+                           AudioMode::NORMAL /* Make sure to leave the test in normal mode */ }) {
+        SCOPED_TRACE("mode=" + toString(mode));
+        ASSERT_OK(device->setMode(mode));
+    }
+
+    // FIXME: Missing api doc. What should the impl do if the mode is invalid ?
+    ASSERT_INVALID_ARGUMENTS(device->setMode(AudioMode::INVALID));
+}
+
+
+TEST_F(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) {
+    doc::test("Query and set the BT SCO NR&EC state");
+    testOptionalAccessors("BtScoNrecEnabled", {true, false, true},
+                         &IPrimaryDevice::setBtScoNrecEnabled,
+                         &IPrimaryDevice::getBtScoNrecEnabled);
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) {
+    doc::test("Query and set the SCO whideband state");
+    testOptionalAccessors("BtScoWideband", {true, false, true},
+                         &IPrimaryDevice::setBtScoWidebandEnabled,
+                         &IPrimaryDevice::getBtScoWidebandEnabled);
+}
+
+using TtyModeAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<TtyMode>;
+TEST_F(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) {
+    doc::test("Query and set the TTY mode state");
+    testOptionalAccessors("TTY mode", {TtyMode::OFF, TtyMode::HCO, TtyMode::VCO, TtyMode::FULL},
+                          &IPrimaryDevice::setTtyMode, &IPrimaryDevice::getTtyMode);
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, setGetHac) {
+    doc::test("Query and set the HAC state");
+    testAccessors("HAC", {true, false, true},
+                         &IPrimaryDevice::setHacEnabled,
+                         &IPrimaryDevice::getHacEnabled);
+}
+
+//////////////////////////////////////////////////////////////////////////////
 //////////////////// Clean caches on global tear down ////////////////////////
 //////////////////////////////////////////////////////////////////////////////