/* * Copyright (C) 2018 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 "AudioPrimaryHidlHalTest.h" #if MAJOR_VERSION >= 7 #include PATH(APM_XSD_H_FILENAME) #include using android::xsdc_enum_range; #endif TEST_P(AudioHidlTest, OpenPrimaryDeviceUsingGetDevice) { doc::test("Calling openDevice(\"primary\") should return the primary device."); if (getDeviceName() != DeviceManager::kPrimaryDevice) { GTEST_SKIP() << "No primary device on this factory"; // returns } EXPECT_TRUE(DeviceManager::getInstance().resetPrimary(getFactoryName())); // Must use IDevicesFactory directly because DeviceManager always uses // the latest interfaces version and corresponding methods for opening // them. However, in minor package uprevs IPrimaryDevice does not inherit // IDevice from the same package and thus IDevice can not be upcasted // (see the interfaces in V7.1). auto factory = DevicesFactoryManager::getInstance().get(getFactoryName()); ASSERT_TRUE(factory != nullptr); sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IDevice> baseDevice; Result result; auto ret = factory->openDevice(DeviceManager::kPrimaryDevice, returnIn(result, baseDevice)); ASSERT_TRUE(ret.isOk()) << ret.description(); ASSERT_EQ(Result::OK, result); ASSERT_TRUE(baseDevice != nullptr); { Return> primaryDevice = ::android::hardware::audio::CORE_TYPES_CPP_VERSION::IPrimaryDevice:: castFrom(baseDevice); EXPECT_TRUE(primaryDevice.isOk()); EXPECT_TRUE(sp<::android::hardware::audio::CORE_TYPES_CPP_VERSION::IPrimaryDevice>( primaryDevice) != nullptr); } #if MAJOR_VERSION < 6 baseDevice.clear(); DeviceManager::waitForInstanceDestruction(); #else auto closeRet = baseDevice->close(); EXPECT_TRUE(closeRet.isOk()); #endif } ////////////////////////////////////////////////////////////////////////////// /////////////////////////// get(Active)Microphones /////////////////////////// ////////////////////////////////////////////////////////////////////////////// TEST_P(AudioHidlDeviceTest, GetMicrophonesTest) { doc::test("Make sure getMicrophones always succeeds"); hidl_vec microphones; ASSERT_OK(getDevice()->getMicrophones(returnIn(res, microphones))); if (res == Result::NOT_SUPPORTED) { GTEST_SKIP() << "getMicrophones is not supported"; // returns } ASSERT_OK(res); #if MAJOR_VERSION <= 6 // In V7, 'getActiveMicrophones' is tested by the 'MicrophoneInfoInputStream' // test which uses the actual configuration of the device. if (microphones.size() > 0) { // When there is microphone on the phone, try to open an input stream // and query for the active microphones. doc::test( "Make sure getMicrophones always succeeds" "and getActiveMicrophones always succeeds when recording from these microphones."); AudioConfig config{}; config.channelMask = mkEnumBitfield(AudioChannelMask::IN_MONO); config.sampleRateHz = 8000; config.format = AudioFormat::PCM_16_BIT; auto flags = hidl_bitfield(AudioInputFlag::NONE); const SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}}; for (auto microphone : microphones) { if (microphone.deviceAddress.device != AudioDevice::IN_BUILTIN_MIC) { continue; } sp stream; StreamHelper helper(stream); AudioConfig suggestedConfig{}; ASSERT_NO_FATAL_FAILURE(helper.open( [&](AudioIoHandle handle, AudioConfig config, auto cb) { return getDevice()->openInputStream(handle, microphone.deviceAddress, config, flags, initMetadata, cb); }, config, &res, &suggestedConfig)); StreamReader reader(stream.get(), stream->getBufferSize()); ASSERT_TRUE(reader.start()); reader.pause(); // This ensures that at least one read has happened. EXPECT_FALSE(reader.hasError()); hidl_vec activeMicrophones; ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones))); ASSERT_OK(res); EXPECT_NE(0U, activeMicrophones.size()); } } #endif // MAJOR_VERSION <= 6 } TEST_P(AudioHidlDeviceTest, SetConnectedState) { doc::test("Check that the HAL can be notified of device connection and deconnection"); #if MAJOR_VERSION <= 6 using AD = AudioDevice; for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) { SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType)); #elif MAJOR_VERSION >= 7 using AD = xsd::AudioDevice; for (auto deviceType : {AD::AUDIO_DEVICE_OUT_HDMI, AD::AUDIO_DEVICE_OUT_WIRED_HEADPHONE, AD::AUDIO_DEVICE_IN_USB_HEADSET}) { SCOPED_TRACE("device=" + toString(deviceType)); #endif for (bool state : {true, false}) { SCOPED_TRACE("state=" + ::testing::PrintToString(state)); DeviceAddress address = {}; #if MAJOR_VERSION <= 6 address.device = deviceType; #elif MAJOR_VERSION >= 7 address.deviceType = toString(deviceType); if (deviceType == AD::AUDIO_DEVICE_IN_USB_HEADSET) { address.address.alsa({0, 0}); } #endif auto ret = getDevice()->setConnectedState(address, state); ASSERT_TRUE(ret.isOk()); if (ret == Result::NOT_SUPPORTED) { doc::partialTest("setConnectedState is not supported"); break; // other deviceType might be supported } ASSERT_OK(ret); } } // Because there is no way of knowing if the devices were connected before // calling setConnectedState, there is no way to restore the HAL to its // initial state. To workaround this, destroy the HAL at the end of this test. ASSERT_TRUE(resetDevice()); } static void testGetDevices(IStream* stream, AudioDevice expectedDevice) { hidl_vec devices; Result res; ASSERT_OK(stream->getDevices(returnIn(res, devices))); if (res == Result::NOT_SUPPORTED) { return doc::partialTest("GetDevices is not supported"); } // The stream was constructed with one device, thus getDevices must only return one ASSERT_EQ(1U, devices.size()); #if MAJOR_VERSION <= 6 AudioDevice device = devices[0].device; #elif MAJOR_VERSION >= 7 auto device = devices[0].deviceType; #endif ASSERT_TRUE(device == expectedDevice) << "Expected: " << ::testing::PrintToString(expectedDevice) << "\n Actual: " << ::testing::PrintToString(device); } TEST_IO_STREAM(GetDevices, "Check that the stream device == the one it was opened with", areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported") #if MAJOR_VERSION <= 6 : testGetDevices(stream.get(), address.device)) #elif MAJOR_VERSION >= 7 : testGetDevices(stream.get(), address.deviceType)) #endif static void testSetDevices(IStream* stream, const DeviceAddress& address) { DeviceAddress otherAddress = address; #if MAJOR_VERSION <= 6 otherAddress.device = (address.device & AudioDevice::BIT_IN) == 0 ? AudioDevice::OUT_SPEAKER : AudioDevice::IN_BUILTIN_MIC; #elif MAJOR_VERSION >= 7 otherAddress.deviceType = xsd::isOutputDevice(address.deviceType) ? toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER) : toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC); #endif EXPECT_RESULT(okOrNotSupported, stream->setDevices({otherAddress})); ASSERT_RESULT(okOrNotSupported, stream->setDevices({address})); // Go back to the original value } TEST_IO_STREAM(SetDevices, "Check that the stream can be rerouted to SPEAKER or BUILTIN_MIC", areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported") : testSetDevices(stream.get(), address)) static void checkGetHwAVSync(::android::hardware::audio::CPP_VERSION::IDevice* device) { Result res; AudioHwSync sync; ASSERT_OK(device->getHwAvSync(returnIn(res, sync))); if (res == Result::NOT_SUPPORTED) { return doc::partialTest("getHwAvSync is not supported"); } ASSERT_OK(res); } TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(getDevice().get())); TEST_P(InputStreamTest, updateSinkMetadata) { doc::test("The HAL should not crash on metadata change"); #if MAJOR_VERSION <= 6 hidl_enum_range range; // Test all possible track configuration for (auto source : range) { for (float volume : {0.0, 0.5, 1.0}) { const SinkMetadata metadata = {{{.source = source, .gain = volume}}}; ASSERT_OK(stream->updateSinkMetadata(metadata)) << "source=" << toString(source) << ", volume=" << volume; } } // Do not test concurrent capture as this is not officially supported // Set no metadata as if all stream track had stopped ASSERT_OK(stream->updateSinkMetadata({})); // Restore initial ASSERT_OK(stream->updateSinkMetadata(initMetadata)); #elif MAJOR_VERSION >= 7 xsdc_enum_range range; // Test all possible track configuration for (auto source : range) { for (float volume : {0.0, 0.5, 1.0}) { const SinkMetadata metadata = { {{.source = toString(source), .gain = volume, .tags = {}, .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}}; ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(metadata)) << "source=" << toString(source) << ", volume=" << volume; } } // Do not test concurrent capture as this is not officially supported // Set no metadata as if all stream track had stopped ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata({})); // Restore initial ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(initMetadata)); #endif } TEST_P(OutputStreamTest, SelectPresentation) { doc::test("Verify that presentation selection does not crash"); ASSERT_RESULT(okOrNotSupported, stream->selectPresentation(0, 0)); } TEST_P(OutputStreamTest, updateSourceMetadata) { doc::test("The HAL should not crash on metadata change"); #if MAJOR_VERSION <= 6 hidl_enum_range usageRange; hidl_enum_range contentRange; // Test all possible track configuration for (auto usage : usageRange) { for (auto content : contentRange) { for (float volume : {0.0, 0.5, 1.0}) { const SourceMetadata metadata = {{{usage, content, volume}}}; ASSERT_OK(stream->updateSourceMetadata(metadata)) << "usage=" << toString(usage) << ", content=" << toString(content) << ", volume=" << volume; } } } // Set many track of different configuration // clang-format off ASSERT_OK(stream->updateSourceMetadata( {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 0.1}, {AudioUsage::VOICE_COMMUNICATION, AudioContentType::SPEECH, 1.0}, {AudioUsage::ALARM, AudioContentType::SONIFICATION, 0.0}, {AudioUsage::ASSISTANT, AudioContentType::UNKNOWN, 0.3}}} )); // clang-format on // Set no metadata as if all stream track had stopped ASSERT_OK(stream->updateSourceMetadata({})); // Restore initial ASSERT_OK(stream->updateSourceMetadata(initMetadata)); #elif MAJOR_VERSION >= 7 xsdc_enum_range usageRange; xsdc_enum_range contentRange; // Test all possible track configuration for (auto usage : usageRange) { for (auto content : contentRange) { for (float volume : {0.0, 0.5, 1.0}) { const SourceMetadata metadata = { {{toString(usage), toString(content), volume, toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), {} /* tags */}}}; ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(metadata)) << "usage=" << toString(usage) << ", content=" << toString(content) << ", volume=" << volume; } } } // Set many track of different configuration // clang-format off ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata( {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA), toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC), 0.1, // gain toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), {}}, // tags {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION), toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH), 1.0, toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO), {}}, {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM), toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION), 0.0, toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), {}}, {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT), toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN), 0.3, toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO), {}}}} )); // clang-format on // Set no metadata as if all stream track had stopped ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata({})); // Restore initial ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(initMetadata)); #endif } TEST_P(AudioPrimaryHidlTest, setMode) { doc::test("Make sure setMode always succeeds if mode is valid and fails otherwise"); // Test Invalid values #if MAJOR_VERSION >= 6 int maxMode = int(AudioMode::CALL_SCREEN); #else int maxMode = int(AudioMode::IN_COMMUNICATION); #endif for (int mode : {-2, -1, maxMode + 1}) { EXPECT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode))) << "mode=" << mode; } // AudioMode::CALL_SCREEN as support is optional #if MAJOR_VERSION >= 6 EXPECT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setMode(AudioMode::CALL_SCREEN)); #endif // Test valid values for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE, AudioMode::NORMAL}) { EXPECT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode); } // Make sure to leave the test in normal mode getDevice()->setMode(AudioMode::NORMAL); } TEST_P(AudioPrimaryHidlTest, setBtHfpSampleRate) { doc::test( "Make sure setBtHfpSampleRate either succeeds or " "indicates that it is not supported at all, or that the provided value is invalid"); for (auto samplingRate : {8000, 16000, 22050, 24000}) { ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setBtHfpSampleRate(samplingRate)); } } TEST_P(AudioPrimaryHidlTest, setBtHfpVolume) { doc::test( "Make sure setBtHfpVolume is either not supported or " "only succeed if volume is in [0,1]"); auto ret = getDevice()->setBtHfpVolume(0.0); ASSERT_TRUE(ret.isOk()); if (ret == Result::NOT_SUPPORTED) { doc::partialTest("setBtHfpVolume is not supported"); return; } testUnitaryGain([this](float volume) { return getDevice()->setBtHfpVolume(volume); }); } TEST_P(AudioPrimaryHidlTest, setBtScoHeadsetDebugName) { doc::test( "Make sure setBtScoHeadsetDebugName either succeeds or " "indicates that it is not supported"); ASSERT_RESULT(okOrNotSupported, getDevice()->setBtScoHeadsetDebugName("test")); } TEST_P(AudioPrimaryHidlTest, updateRotation) { doc::test("Check that the hal can receive the current rotation"); for (Rotation rotation : {Rotation::DEG_0, Rotation::DEG_90, Rotation::DEG_180, Rotation::DEG_270, Rotation::DEG_0}) { ASSERT_RESULT(okOrNotSupported, getDevice()->updateRotation(rotation)); } } TEST_P(BoolAccessorPrimaryHidlTest, setGetBtHfpEnabled) { doc::test("Query and set the BT HFP state"); testAccessors("BtHfpEnabled", Initial{false, OPTIONAL}, {true}, &IPrimaryDevice::setBtHfpEnabled, &IPrimaryDevice::getBtHfpEnabled); }