From 1e1b64767a2adb1b3526d29a6fc2348ad3cfcd2a Mon Sep 17 00:00:00 2001 From: bodamnam Date: Mon, 20 Mar 2023 14:04:49 +0000 Subject: Add UT for AudioManager Fix the crash during the AudioManager UT by the racing codition accessing the AudioManager instance between AudioManger destruction and processEvent in ImsMediaEventHandler. Bug: 272299058 Test: atest ImsMediaNativeTests, Verified the voice call in live network. Change-Id: I9bed59f9bfe2916ccdc2fae2c671c8811bcdcdca --- .../lib/libimsmedia/core/audio/AudioManager.cpp | 116 ++-- .../libimsmedia/core/include/audio/AudioManager.h | 22 +- .../libimsmedia/core/include/text/TextManager.h | 8 - .../core/include/utils/ImsMediaEventHandler.h | 4 +- .../libimsmedia/core/include/video/VideoManager.h | 8 - .../lib/libimsmedia/core/text/TextManager.cpp | 27 +- .../core/utils/ImsMediaEventHandler.cpp | 31 +- .../lib/libimsmedia/core/video/VideoManager.cpp | 27 +- tests/native/Android.bp | 1 + .../libimsmedia/core/audio/AudioManagerTest.cpp | 683 +++++++++++++++++++++ .../include/core/audio/MockAudioManager.h | 39 ++ 11 files changed, 835 insertions(+), 131 deletions(-) create mode 100644 tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp create mode 100644 tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp index 49efef54..e957cc3b 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManager.cpp @@ -23,9 +23,18 @@ using namespace android; AudioManager* AudioManager::sManager = nullptr; -AudioManager::AudioManager() {} +AudioManager::AudioManager() +{ + mRequestHandler.Init("AUDIO_REQUEST_EVENT"); + mResponseHandler.Init("AUDIO_RESPONSE_EVENT"); +} -AudioManager::~AudioManager() {} +AudioManager::~AudioManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + sManager = nullptr; +} AudioManager* AudioManager::getInstance() { @@ -203,6 +212,22 @@ void AudioManager::setMediaQualityThreshold(int sessionId, MediaQualityThreshold } } +void AudioManager::SendInternalEvent( + uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) +{ + auto session = mSessions.find(sessionId); + IMLOGI1("[SendInternalEvent] sessionId[%d]", sessionId); + + if (session != mSessions.end()) + { + (session->second)->SendInternalEvent(event, paramA, paramB); + } + else + { + IMLOGE1("[SendInternalEvent] no session id[%d]", sessionId); + } +} + void AudioManager::sendMessage(const int sessionId, const android::Parcel& parcel) { int nMsg = parcel.readInt32(); @@ -283,35 +308,19 @@ void AudioManager::sendMessage(const int sessionId, const android::Parcel& parce } } -void AudioManager::SendInternalEvent( - uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) -{ - auto session = mSessions.find(sessionId); - IMLOGI1("[SendInternalEvent] sessionId[%d]", sessionId); - - if (session != mSessions.end()) - { - (session->second)->SendInternalEvent(event, paramA, paramB); - } - else - { - IMLOGE1("[SendInternalEvent] no session id[%d]", sessionId); - } -} - -AudioManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("AUDIO_REQUEST_EVENT") -{ -} - -AudioManager::RequestHandler::~RequestHandler() {} - void AudioManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId, paramA, paramB); ImsMediaResult result = RESULT_SUCCESS; + + if (sManager == nullptr) + { + IMLOGE0("[processEvent] not ready"); + return; + } + switch (event) { case kAudioOpenSession: @@ -320,7 +329,7 @@ void AudioManager::RequestHandler::processEvent( if (param != nullptr) { AudioConfig* pConfig = reinterpret_cast(param->mConfig); - result = AudioManager::getInstance()->openSession( + result = sManager->openSession( static_cast(sessionId), param->rtpFd, param->rtcpFd, pConfig); if (result == RESULT_SUCCESS) @@ -349,8 +358,7 @@ void AudioManager::RequestHandler::processEvent( } break; case kAudioCloseSession: - if (AudioManager::getInstance()->closeSession(static_cast(sessionId)) == - RESULT_SUCCESS) + if (sManager->closeSession(static_cast(sessionId)) == RESULT_SUCCESS) { ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioSessionClosed, sessionId, 0, 0); @@ -359,8 +367,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioModifySession: { AudioConfig* config = reinterpret_cast(paramA); - result = - AudioManager::getInstance()->modifySession(static_cast(sessionId), config); + result = sManager->modifySession(static_cast(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioModifySessionResponse, sessionId, result, paramA); } @@ -368,7 +375,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioAddConfig: { AudioConfig* config = reinterpret_cast(paramA); - result = AudioManager::getInstance()->addConfig(static_cast(sessionId), config); + result = sManager->addConfig(static_cast(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioAddConfigResponse, sessionId, result, paramA); } @@ -376,8 +383,7 @@ void AudioManager::RequestHandler::processEvent( case kAudioConfirmConfig: { AudioConfig* config = reinterpret_cast(paramA); - result = - AudioManager::getInstance()->confirmConfig(static_cast(sessionId), config); + result = sManager->confirmConfig(static_cast(sessionId), config); ImsMediaEventHandler::SendEvent( "AUDIO_RESPONSE_EVENT", kAudioConfirmConfigResponse, sessionId, result, paramA); } @@ -387,7 +393,7 @@ void AudioManager::RequestHandler::processEvent( AudioConfig* config = reinterpret_cast(paramA); if (config != nullptr) { - AudioManager::getInstance()->deleteConfig(static_cast(sessionId), config); + sManager->deleteConfig(static_cast(sessionId), config); delete config; } } @@ -397,8 +403,7 @@ void AudioManager::RequestHandler::processEvent( EventParamDtmf* param = reinterpret_cast(paramA); if (param != nullptr) { - AudioManager::getInstance()->sendDtmf( - static_cast(sessionId), param->digit, param->duration); + sManager->sendDtmf(static_cast(sessionId), param->digit, param->duration); delete param; } } @@ -410,8 +415,7 @@ void AudioManager::RequestHandler::processEvent( if (listExtension != nullptr) { - AudioManager::getInstance()->sendRtpHeaderExtension( - static_cast(sessionId), listExtension); + sManager->sendRtpHeaderExtension(static_cast(sessionId), listExtension); delete listExtension; } } @@ -421,34 +425,32 @@ void AudioManager::RequestHandler::processEvent( MediaQualityThreshold* threshold = reinterpret_cast(paramA); if (threshold != nullptr) { - AudioManager::getInstance()->setMediaQualityThreshold( - static_cast(sessionId), threshold); + sManager->setMediaQualityThreshold(static_cast(sessionId), threshold); delete threshold; } } break; case kRequestAudioCmr: case kRequestSendRtcpXrReport: - AudioManager::getInstance()->SendInternalEvent( - event, static_cast(sessionId), paramA, paramB); + sManager->SendInternalEvent(event, static_cast(sessionId), paramA, paramB); break; default: break; } } -AudioManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("AUDIO_RESPONSE_EVENT") -{ -} - -AudioManager::ResponseHandler::~ResponseHandler() {} - void AudioManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { IMLOGI4("[processEvent] event[%d], sessionId[%d], paramA[%d], paramB[%d]", event, sessionId, paramA, paramB); + + if (sManager == nullptr) + { + IMLOGE0("[processEvent] not ready"); + return; + } + android::Parcel parcel; switch (event) { @@ -461,7 +463,7 @@ void AudioManager::ResponseHandler::processEvent( // fail reason parcel.writeInt32(static_cast(paramA)); } - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; case kAudioModifySessionResponse: // fall through case kAudioAddConfigResponse: // fall through @@ -473,7 +475,7 @@ void AudioManager::ResponseHandler::processEvent( if (config != nullptr) { config->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete config; } } @@ -485,7 +487,7 @@ void AudioManager::ResponseHandler::processEvent( if (config != nullptr) { config->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete config; } } @@ -505,7 +507,7 @@ void AudioManager::ResponseHandler::processEvent( extension.writeToParcel(&parcel); } - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete listExtension; } } @@ -517,7 +519,7 @@ void AudioManager::ResponseHandler::processEvent( if (status != nullptr) { status->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete status; } } @@ -529,7 +531,7 @@ void AudioManager::ResponseHandler::processEvent( parcel.writeInt32(event); parcel.writeByte(static_cast(paramA)); parcel.writeInt32(static_cast(paramB)); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; case kAudioCallQualityChangedInd: { @@ -538,7 +540,7 @@ void AudioManager::ResponseHandler::processEvent( if (quality != nullptr) { quality->writeToParcel(&parcel); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); delete quality; } } @@ -546,7 +548,7 @@ void AudioManager::ResponseHandler::processEvent( case kAudioSessionClosed: parcel.writeInt32(event); parcel.writeInt32(static_cast(sessionId)); - AudioManager::getInstance()->sendResponse(sessionId, parcel); + sManager->sendResponse(sessionId, parcel); break; default: break; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h index 231b4141..d2a0e441 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/audio/AudioManager.h @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -63,20 +55,22 @@ public: static AudioManager* getInstance(); virtual int getState(int sessionId); virtual void sendMessage(const int sessionId, const android::Parcel& parcel); - void SendInternalEvent(uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); -private: +protected: AudioManager(); virtual ~AudioManager(); ImsMediaResult openSession(int sessionId, int rtpFd, int rtcpFd, AudioConfig* config); ImsMediaResult closeSession(int sessionId); ImsMediaResult modifySession(int sessionId, AudioConfig* config); ImsMediaResult addConfig(int sessionId, AudioConfig* config); - ImsMediaResult deleteConfig(int sessionId, AudioConfig* config); + virtual ImsMediaResult deleteConfig(int sessionId, AudioConfig* config); ImsMediaResult confirmConfig(int sessionId, AudioConfig* config); - void sendDtmf(int sessionId, char dtmfDigit, int duration); - void sendRtpHeaderExtension(int sessionId, std::list* listExtension); - void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold); + virtual void sendDtmf(int sessionId, char dtmfDigit, int duration); + virtual void sendRtpHeaderExtension( + int sessionId, std::list* listExtension); + virtual void setMediaQualityThreshold(int sessionId, MediaQualityThreshold* threshold); + virtual void SendInternalEvent( + uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); static AudioManager* sManager; std::unordered_map> mSessions; diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h index 66201daa..67b24ed3 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/text/TextManager.h @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h index 4e5dc7f0..e01761da 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/utils/ImsMediaEventHandler.h @@ -46,8 +46,10 @@ private: std::mutex mMutexEvent; public: - ImsMediaEventHandler(const char* strName); + ImsMediaEventHandler(); virtual ~ImsMediaEventHandler(); + void Init(const char* strName); + void Deinit(); static void SendEvent(const char* strEventHandlerName, uint32_t event, uint64_t paramA, uint64_t paramB = 0, uint64_t paramC = 0); char* getName(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h index 7c780fd0..91c2747b 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/include/video/VideoManager.h @@ -37,10 +37,6 @@ public: */ class RequestHandler : public ImsMediaEventHandler { - public: - RequestHandler(); - virtual ~RequestHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); @@ -51,10 +47,6 @@ public: */ class ResponseHandler : public ImsMediaEventHandler { - public: - ResponseHandler(); - virtual ~ResponseHandler(); - protected: virtual void processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp index f50a0ba8..33ad7a20 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/text/TextManager.cpp @@ -21,9 +21,18 @@ using namespace android; TextManager* TextManager::manager; -TextManager::TextManager() {} +TextManager::TextManager() +{ + mRequestHandler.Init("TEXT_REQUEST_EVENT"); + mResponseHandler.Init("TEXT_RESPONSE_EVENT"); +} -TextManager::~TextManager() {} +TextManager::~TextManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + manager = nullptr; +} TextManager* TextManager::getInstance() { @@ -200,13 +209,6 @@ void TextManager::sendMessage(const int sessionId, const android::Parcel& parcel } } -TextManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("TEXT_REQUEST_EVENT") -{ -} - -TextManager::RequestHandler::~RequestHandler() {} - void TextManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { @@ -294,13 +296,6 @@ void TextManager::RequestHandler::processEvent( } } -TextManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("TEXT_RESPONSE_EVENT") -{ -} - -TextManager::ResponseHandler::~ResponseHandler() {} - void TextManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp index b3d165ab..336f2aa5 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/utils/ImsMediaEventHandler.cpp @@ -22,24 +22,29 @@ std::list ImsMediaEventHandler::gListEventHandler; std::mutex ImsMediaEventHandler::mMutex; -ImsMediaEventHandler::ImsMediaEventHandler(const char* strName) +ImsMediaEventHandler::ImsMediaEventHandler() {} + +ImsMediaEventHandler::~ImsMediaEventHandler() {} + +void ImsMediaEventHandler::Init(const char* strName) { strncpy(mName, strName, MAX_EVENTHANDLER_NAME); mbTerminate = false; gListEventHandler.push_back(this); - IMLOGD1("[ImsMediaEventHandler] %s", mName); + IMLOGD1("[Init] %s", mName); StartThread(); } -ImsMediaEventHandler::~ImsMediaEventHandler() +void ImsMediaEventHandler::Deinit() { - IMLOGD1("[~ImsMediaEventHandler] %s", mName); + IMLOGD2("[Deinit] %s, queue size[%d]", mName, mListevent.size()); + std::lock_guard guard(mMutexEvent); + StopThread(); gListEventHandler.remove(this); mListevent.clear(); mListParamA.clear(); mListParamB.clear(); mListParamC.clear(); - StopThread(); mCondition.signal(); mConditionExit.wait(); } @@ -73,13 +78,12 @@ char* ImsMediaEventHandler::getName() void ImsMediaEventHandler::AddEvent( uint32_t event, uint64_t paramA, uint64_t paramB, uint64_t paramC) { + std::lock_guard guard(mMutexEvent); IMLOGD3("[AddEvent] %s, event[%d], size[%d]", mName, event, mListevent.size()); - mMutexEvent.lock(); mListevent.push_back(event); mListParamA.push_back(paramA); mListParamB.push_back(paramB); mListParamC.push_back(paramC); - mMutexEvent.unlock(); mCondition.signal(); } @@ -94,17 +98,22 @@ void* ImsMediaEventHandler::run() for (;;) { - // lock + if (IsThreadStopped()) + { + break; + } + mMutexEvent.lock(); - if (IsThreadStopped() || mListevent.size() == 0) + + if (mListevent.size() == 0) { mMutexEvent.unlock(); break; } - mMutexEvent.unlock(); + processEvent(mListevent.front(), mListParamA.front(), mListParamB.front(), mListParamC.front()); - mMutexEvent.lock(); + mListevent.pop_front(); mListParamA.pop_front(); mListParamB.pop_front(); diff --git a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp index 9d98be37..37007b21 100644 --- a/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp +++ b/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/video/VideoManager.cpp @@ -21,9 +21,18 @@ using namespace android; VideoManager* VideoManager::manager; -VideoManager::VideoManager() {} +VideoManager::VideoManager() +{ + mRequestHandler.Init("VIDEO_REQUEST_EVENT"); + mResponseHandler.Init("VIDEO_RESPONSE_EVENT"); +} -VideoManager::~VideoManager() {} +VideoManager::~VideoManager() +{ + mRequestHandler.Deinit(); + mResponseHandler.Deinit(); + manager = nullptr; +} VideoManager* VideoManager::getInstance() { @@ -245,13 +254,6 @@ void VideoManager::SendInternalEvent( } } -VideoManager::RequestHandler::RequestHandler() : - ImsMediaEventHandler("VIDEO_REQUEST_EVENT") -{ -} - -VideoManager::RequestHandler::~RequestHandler() {} - void VideoManager::RequestHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { @@ -351,13 +353,6 @@ void VideoManager::RequestHandler::processEvent( } } -VideoManager::ResponseHandler::ResponseHandler() : - ImsMediaEventHandler("VIDEO_RESPONSE_EVENT") -{ -} - -VideoManager::ResponseHandler::~ResponseHandler() {} - void VideoManager::ResponseHandler::processEvent( uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB) { diff --git a/tests/native/Android.bp b/tests/native/Android.bp index 5000dee4..4171f753 100644 --- a/tests/native/Android.bp +++ b/tests/native/Android.bp @@ -22,6 +22,7 @@ cc_library_headers { name: "libimsmedia_tests_headers", export_include_dirs: [ "service/src/com/android/telephony/imsmedia/lib/libimsmedia/include", + "service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio", ], } diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp new file mode 100644 index 00000000..9f8cc4c5 --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/core/audio/AudioManagerTest.cpp @@ -0,0 +1,683 @@ +/** + * Copyright (C) 2023 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 +#include +#include +#include +#include +#include +#include + +using namespace android::telephony::imsmedia; + +using ::testing::_; +using ::testing::Eq; +using ::testing::Pointee; +using ::testing::Ref; +using ::testing::Return; + +// RtpConfig +const int32_t kMediaDirection = RtpConfig::MEDIA_DIRECTION_SEND_RECEIVE; +const android::String8 kRemoteAddress("127.0.0.1"); +const int32_t kRemotePort = 10000; +const int8_t kDscp = 0; +const int8_t kRxPayload = 96; +const int8_t kTxPayload = 96; +const int8_t kSamplingRate = 16; + +// RtcpConfig +const android::String8 kCanonicalName("name"); +const int32_t kTransmitPort = 1001; +const int32_t kIntervalSec = 5; +const int32_t kRtcpXrBlockTypes = RtcpConfig::FLAG_RTCPXR_STATISTICS_SUMMARY_REPORT_BLOCK | + RtcpConfig::FLAG_RTCPXR_VOIP_METRICS_REPORT_BLOCK; + +// AudioConfig +const int8_t kPTimeMillis = 20; +const int32_t kMaxPtimeMillis = 100; +const bool kDtxEnabled = true; +const int32_t kCodecType = AudioConfig::CODEC_AMR_WB; +const int8_t kDtmfTxPayloadTypeNumber = 100; +const int8_t kDtmfRxPayloadTypeNumber = 101; +const int8_t kDtmfsamplingRateKHz = 16; + +// AmrParam +const int32_t kAmrMode = 8; +const bool kOctetAligned = false; +const int32_t kMaxRedundancyMillis = 240; + +// EvsParam +const int32_t kEvsBandwidth = EvsParams::EVS_BAND_NONE; +const int32_t kEvsMode = 8; +const int8_t kChannelAwareMode = 3; +const bool kUseHeaderFullOnly = false; +const int8_t kcodecModeRequest = 15; + +int32_t kSessionId = 0; + +static ImsMediaCondition gCondition; + +class AudioManagerCallback +{ +public: + int32_t resSessionId; + int32_t response; + AudioConfig resConfig; + ImsMediaResult result; + std::list extensions; + MediaQualityStatus mediaQualityStatus; + char receivedDtmfDigit; + int32_t receivedDtmfDuration; + CallQuality callQuality; + + void resetRespond() + { + resSessionId = -1; + response = -1; + result = RESULT_NOT_READY; + } + + void onCallback(const int id, const int event, const ImsMediaResult res) + { + resSessionId = id; + response = event; + result = res; + } + + void onCallbackConfig( + const int id, const int event, const ImsMediaResult res, const AudioConfig& config) + { + resSessionId = id; + response = event; + resConfig = config; + result = res; + } + + void onCallbackHeaderExtension( + const int id, const int event, const std::list& list) + { + extensions.clear(); + resSessionId = id; + response = event; + std::copy(list.begin(), list.end(), std::back_inserter(extensions)); + } + + void onCallbackMediaQualityStatus( + const int id, const int event, const MediaQualityStatus& status) + { + resSessionId = id; + response = event; + mediaQualityStatus = status; + } + + void onCallbackDtmfReceived(const int id, const int event, char digit, int32_t duration) + { + resSessionId = id; + response = event; + receivedDtmfDigit = digit; + receivedDtmfDuration = duration; + } + + void onCallbackCallQuality(const int id, const int event, const CallQuality& status) + { + resSessionId = id; + response = event; + callQuality = status; + } +}; + +static std::unordered_map gMapCallback; + +class AudioManagerTest : public ::testing::Test +{ +public: + MockAudioManager manager; + AudioConfig config; + RtcpConfig rtcp; + AmrParams amr; + EvsParams evs; + int socketRtpFd; + int socketRtcpFd; + AudioManagerCallback callback; + + AudioManagerTest() + { + socketRtpFd = -1; + socketRtcpFd = -1; + callback.resetRespond(); + gCondition.reset(); + } + ~AudioManagerTest() {} + +protected: + virtual void SetUp() override + { + rtcp.setCanonicalName(kCanonicalName); + rtcp.setTransmitPort(kTransmitPort); + rtcp.setIntervalSec(kIntervalSec); + rtcp.setRtcpXrBlockTypes(kRtcpXrBlockTypes); + + amr.setAmrMode(kAmrMode); + amr.setOctetAligned(kOctetAligned); + amr.setMaxRedundancyMillis(kMaxRedundancyMillis); + + evs.setEvsBandwidth(kEvsBandwidth); + evs.setEvsMode(kEvsMode); + evs.setChannelAwareMode(kChannelAwareMode); + evs.setUseHeaderFullOnly(kUseHeaderFullOnly); + evs.setCodecModeRequest(kcodecModeRequest); + + config.setMediaDirection(kMediaDirection); + config.setRemoteAddress(kRemoteAddress); + config.setRemotePort(kRemotePort); + config.setRtcpConfig(rtcp); + config.setDscp(kDscp); + config.setRxPayloadTypeNumber(kRxPayload); + config.setTxPayloadTypeNumber(kTxPayload); + config.setSamplingRateKHz(kSamplingRate); + config.setPtimeMillis(kPTimeMillis); + config.setMaxPtimeMillis(kMaxPtimeMillis); + config.setDtxEnabled(kDtxEnabled); + config.setCodecType(kCodecType); + config.setTxDtmfPayloadTypeNumber(kDtmfTxPayloadTypeNumber); + config.setRxDtmfPayloadTypeNumber(kDtmfRxPayloadTypeNumber); + config.setDtmfsamplingRateKHz(kDtmfsamplingRateKHz); + config.setAmrParams(amr); + config.setEvsParams(evs); + + manager.setCallback(&audioCallback); + gMapCallback.insert(std::make_pair(kSessionId, &callback)); + const char testIp[] = "127.0.0.1"; + unsigned int testPortRtp = 30000; + socketRtpFd = ImsMediaNetworkUtil::openSocket(testIp, testPortRtp, AF_INET); + EXPECT_NE(socketRtpFd, -1); + unsigned int testPortRtcp = 30001; + socketRtcpFd = ImsMediaNetworkUtil::openSocket(testIp, testPortRtcp, AF_INET); + EXPECT_NE(socketRtcpFd, -1); + gCondition.reset(); + } + + virtual void TearDown() override + { + if (socketRtpFd != -1) + { + ImsMediaNetworkUtil::closeSocket(socketRtpFd); + } + + if (socketRtcpFd != -1) + { + ImsMediaNetworkUtil::closeSocket(socketRtcpFd); + } + + gMapCallback.erase(kSessionId); + } + + void openSession(const int32_t sessionId) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(kAudioOpenSession); + parcel.writeInt32(socketRtpFd); + parcel.writeInt32(socketRtcpFd); + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, kAudioOpenSessionSuccess); + } + + void closeSession(const int32_t sessionId) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(kAudioCloseSession); + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, kAudioSessionClosed); + } + + void testEventResponse(const int32_t sessionId, const int32_t event, AudioConfig* config, + const int32_t response, const int32_t result) + { + callback.resetRespond(); + android::Parcel parcel; + parcel.writeInt32(event); + + if (config != nullptr) + { + config->writeToParcel(&parcel); + } + + parcel.setDataPosition(0); + gCondition.reset(); + manager.sendMessage(sessionId, parcel); + EXPECT_TRUE(!gCondition.wait_timeout(1000)); + EXPECT_EQ(callback.resSessionId, sessionId); + EXPECT_EQ(callback.response, response); + + if (callback.response >= kAudioOpenSessionFailure && + callback.response <= kAudioConfirmConfigResponse) + { + EXPECT_EQ(result, result); + + if (config != nullptr && callback.response >= kAudioModifySessionResponse && + callback.response <= kAudioConfirmConfigResponse) + { + EXPECT_EQ(callback.resConfig, *config); + } + } + } + + static int32_t audioCallback(int sessionId, const android::Parcel& parcel) + { + parcel.setDataPosition(0); + + int response = parcel.readInt32(); + ImsMediaResult result = RESULT_INVALID_PARAM; + + auto callback = gMapCallback.find(sessionId); + + if (callback != gMapCallback.end()) + { + if (response >= kAudioOpenSessionFailure && response <= kAudioConfirmConfigResponse) + { + result = static_cast(parcel.readInt32()); + } + + switch (response) + { + case kAudioModifySessionResponse: + case kAudioAddConfigResponse: + case kAudioConfirmConfigResponse: + { + AudioConfig resConfig; + resConfig.readFromParcel(&parcel); + (callback->second)->onCallbackConfig(sessionId, response, result, resConfig); + } + break; + case kAudioFirstMediaPacketInd: + { + AudioConfig resConfig; + resConfig.readFromParcel(&parcel); + (callback->second) + ->onCallbackConfig(sessionId, response, RESULT_SUCCESS, resConfig); + } + break; + case kAudioRtpHeaderExtensionInd: + { + std::list listExtension; + int32_t listSize = parcel.readInt32(); + + for (int32_t i = 0; i < listSize; i++) + { + RtpHeaderExtension extension; + extension.readFromParcel(&parcel); + listExtension.push_back(extension); + } + + (callback->second) + ->onCallbackHeaderExtension(sessionId, response, listExtension); + } + break; + case kAudioMediaQualityStatusInd: + { + MediaQualityStatus status; + status.readFromParcel(&parcel); + (callback->second)->onCallbackMediaQualityStatus(sessionId, response, status); + } + break; + case kAudioDtmfReceivedInd: + (callback->second) + ->onCallbackDtmfReceived( + sessionId, response, parcel.readByte(), parcel.readInt32()); + break; + case kAudioCallQualityChangedInd: + { + CallQuality quality; + quality.readFromParcel(&parcel); + (callback->second)->onCallbackCallQuality(sessionId, response, quality); + } + break; + default: + (callback->second)->onCallback(sessionId, response, result); + break; + } + } + + if (response != kAudioCallQualityChangedInd) + { + gCondition.signal(); + } + + return 0; + } +}; + +TEST_F(AudioManagerTest, testOpenCloseSession) +{ + EXPECT_EQ(manager.getState(kSessionId), kSessionStateClosed); + openSession(kSessionId); + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testModifySession) +{ + testEventResponse(kSessionId, kAudioModifySession, nullptr, kAudioModifySessionResponse, + RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse(kSessionId, kAudioModifySession, nullptr, kAudioModifySessionResponse, + RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioModifySession, &config, kAudioModifySessionResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testAddConfig) +{ + testEventResponse( + kSessionId, kAudioAddConfig, nullptr, kAudioAddConfigResponse, RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse( + kSessionId, kAudioAddConfig, nullptr, kAudioAddConfigResponse, RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioAddConfig, &config, kAudioAddConfigResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testConfirmConfig) +{ + testEventResponse(kSessionId, kAudioConfirmConfig, nullptr, kAudioConfirmConfigResponse, + RESULT_INVALID_PARAM); + + openSession(kSessionId); + + testEventResponse(kSessionId, kAudioConfirmConfig, nullptr, kAudioConfirmConfigResponse, + RESULT_INVALID_PARAM); + + testEventResponse( + kSessionId, kAudioConfirmConfig, &config, kAudioConfirmConfigResponse, RESULT_SUCCESS); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testDeleteConfig) +{ + openSession(kSessionId); + + android::Parcel parcel; + parcel.writeInt32(kAudioDeleteConfig); + + if (config != nullptr) + { + config.writeToParcel(&parcel); + } + + EXPECT_CALL(manager, deleteConfig(kSessionId, Pointee(Eq(config)))) + .Times(1) + .WillOnce(Return(RESULT_INVALID_PARAM)); + + parcel.setDataPosition(0); + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendDtmf) +{ + openSession(kSessionId); + + const char kDigit = '1'; + const int32_t kDuration = 100; + + android::Parcel parcel; + parcel.writeInt32(kAudioSendDtmf); + parcel.writeByte(kDigit); + parcel.writeInt32(kDuration); + parcel.setDataPosition(0); + + EXPECT_CALL(manager, sendDtmf(kSessionId, kDigit, kDuration)).Times(1).WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendHeaderExtension) +{ + openSession(kSessionId); + + std::list extensions; + RtpHeaderExtension extension; + const uint8_t kExtensionData[] = {0x01, 0x02}; + const int32_t kExtensionDataSize = 2; + extension.setLocalIdentifier(15); + extension.setExtensionData(kExtensionData, kExtensionDataSize); + extensions.push_back(extension); + + android::Parcel parcel; + parcel.writeInt32(kAudioSendRtpHeaderExtension); + parcel.writeInt32(extensions.size()); + + for (auto& item : extensions) + { + item.writeToParcel(&parcel); + } + + parcel.setDataPosition(0); + + EXPECT_CALL(manager, sendRtpHeaderExtension(kSessionId, Pointee(Eq(extensions)))) + .Times(1) + .WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSetMediaQualityThreshold) +{ + openSession(kSessionId); + + const std::vector kRtpInactivityTimerMillis = {10000, 20000}; + const int32_t kRtcpInactivityTimerMillis = 20000; + const int32_t kRtpHysteresisTimeInMillis = 3000; + const int32_t kRtpPacketLossDurationMillis = 5000; + const std::vector kRtpPacketLossRate = {3, 5}; + const std::vector kRtpJitterMillis = {100, 200}; + const bool kNotifyCurrentStatus = false; + + MediaQualityThreshold threshold; + threshold.setRtpInactivityTimerMillis(kRtpInactivityTimerMillis); + threshold.setRtcpInactivityTimerMillis(kRtcpInactivityTimerMillis); + threshold.setRtpHysteresisTimeInMillis(kRtpHysteresisTimeInMillis); + threshold.setRtpPacketLossDurationMillis(kRtpPacketLossDurationMillis); + threshold.setRtpPacketLossRate(kRtpPacketLossRate); + threshold.setRtpJitterMillis(kRtpJitterMillis); + threshold.setNotifyCurrentStatus(kNotifyCurrentStatus); + + android::Parcel parcel; + parcel.writeInt32(kAudioSetMediaQualityThreshold); + threshold.writeToParcel(&parcel); + parcel.setDataPosition(0); + + EXPECT_CALL(manager, setMediaQualityThreshold(kSessionId, Pointee(Eq(threshold)))) + .Times(1) + .WillOnce(Return()); + + manager.sendMessage(kSessionId, parcel); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendInternalEventCmr) +{ + openSession(kSessionId); + + const int32_t kCmrCode = 1; + const int32_t kCmrDefine = 7; + + EXPECT_CALL(manager, SendInternalEvent(kRequestAudioCmr, kSessionId, kCmrCode, kCmrDefine)) + .Times(1) + .WillOnce(Return()); + + ImsMediaEventHandler::SendEvent( + "AUDIO_REQUEST_EVENT", kRequestAudioCmr, kSessionId, kCmrCode, kCmrDefine); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testSendInternalEventRtcpXr) +{ + openSession(kSessionId); + + const int32_t param1 = 10; + const int32_t param2 = 20; + + EXPECT_CALL(manager, SendInternalEvent(kRequestSendRtcpXrReport, kSessionId, param1, param2)) + .Times(1) + .WillOnce(Return()); + + ImsMediaEventHandler::SendEvent( + "AUDIO_REQUEST_EVENT", kRequestSendRtcpXrReport, kSessionId, param1, param2); + + closeSession(kSessionId); +} + +TEST_F(AudioManagerTest, testFirstMediaPacketInd) +{ + AudioConfig* param = new AudioConfig(config); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioFirstMediaPacketInd, kSessionId, + reinterpret_cast(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioFirstMediaPacketInd); + EXPECT_EQ(callback.resConfig, config); +} + +TEST_F(AudioManagerTest, testRtpHeaderExtensionInd) +{ + std::list extensions; + RtpHeaderExtension extension; + const uint8_t kExtensionData[] = {0x01, 0x02}; + const int32_t kExtensionDataSize = 2; + extension.setLocalIdentifier(15); + extension.setExtensionData(kExtensionData, kExtensionDataSize); + extensions.push_back(extension); + + std::list* param = new std::list(); + std::copy(extensions.begin(), extensions.end(), std::back_inserter(*param)); + + android::Parcel parcel; + parcel.writeInt32(param->size()); + + for (auto& item : *param) + { + item.writeToParcel(&parcel); + } + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioRtpHeaderExtensionInd, kSessionId, + reinterpret_cast(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioRtpHeaderExtensionInd); + EXPECT_EQ(callback.extensions, extensions); +} + +TEST_F(AudioManagerTest, testMediaQualityStatusInd) +{ + MediaQualityStatus status; + status.setRtpInactivityTimeMillis(10000); + status.setRtcpInactivityTimeMillis(10000); + status.setRtpPacketLossRate(1); + status.setRtpJitterMillis(100); + + MediaQualityStatus* param = new MediaQualityStatus(status); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioMediaQualityStatusInd, kSessionId, + reinterpret_cast(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioMediaQualityStatusInd); + EXPECT_EQ(callback.mediaQualityStatus, status); +} + +TEST_F(AudioManagerTest, testDtmfReceivedInd) +{ + const char digit = 1; + const int32_t duration = 100; + + ImsMediaEventHandler::SendEvent( + "AUDIO_RESPONSE_EVENT", kAudioDtmfReceivedInd, kSessionId, digit, duration); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioDtmfReceivedInd); + EXPECT_EQ(callback.receivedDtmfDigit, digit); + EXPECT_EQ(callback.receivedDtmfDuration, duration); +} + +TEST_F(AudioManagerTest, testCallQualityInd) +{ + CallQuality quality; + quality.setDownlinkCallQualityLevel(0); + quality.setUplinkCallQualityLevel(0); + quality.setCallDuration(30000); + quality.setNumRtpPacketsTransmitted(1500); + quality.setNumRtpPacketsReceived(1500); + quality.setNumRtpPacketsTransmittedLost(1); + quality.setNumRtpPacketsNotReceived(2); + quality.setAverageRelativeJitter(50); + quality.setMaxRelativeJitter(150); + quality.setAverageRoundTripTime(60); + quality.setCodecType(AudioConfig::CODEC_AMR_WB); + quality.setRtpInactivityDetected(false); + quality.setRxSilenceDetected(false); + quality.setTxSilenceDetected(false); + quality.setNumVoiceFrames(1400); + quality.setNumNoDataFrames(0); + quality.setNumDroppedRtpPackets(0); + quality.setMinPlayoutDelayMillis(100); + quality.setMaxPlayoutDelayMillis(180); + quality.setNumRtpSidPacketsReceived(100); + quality.setNumRtpDuplicatePackets(1); + + CallQuality* param = new CallQuality(quality); + + ImsMediaEventHandler::SendEvent("AUDIO_RESPONSE_EVENT", kAudioCallQualityChangedInd, kSessionId, + reinterpret_cast(param), 0); + + gCondition.wait_timeout(20); + EXPECT_EQ(callback.resSessionId, kSessionId); + EXPECT_EQ(callback.response, kAudioCallQualityChangedInd); + EXPECT_EQ(callback.callQuality, quality); +} \ No newline at end of file diff --git a/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h new file mode 100644 index 00000000..4a46634b --- /dev/null +++ b/tests/native/service/src/com/android/telephony/imsmedia/lib/libimsmedia/include/core/audio/MockAudioManager.h @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2023 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. + */ + +#ifndef MOCK_AUDIO_MANAGER_H +#define MOCK_AUDIO_MANAGER_H + +#include +#include +#include + +class MockAudioManager : public AudioManager +{ +public: + MockAudioManager() { sManager = this; } + virtual ~MockAudioManager() { sManager = nullptr; } + MOCK_METHOD(ImsMediaResult, deleteConfig, (int sessionId, AudioConfig* config), (override)); + MOCK_METHOD(void, sendDtmf, (int sessionId, char dtmfDigit, int duration), (override)); + MOCK_METHOD(void, sendRtpHeaderExtension, + (int sessionId, std::list* listExtension), (override)); + MOCK_METHOD(void, setMediaQualityThreshold, (int sessionId, MediaQualityThreshold* threshold), + (override)); + MOCK_METHOD(void, SendInternalEvent, + (uint32_t event, uint64_t sessionId, uint64_t paramA, uint64_t paramB), (override)); +}; + +#endif \ No newline at end of file -- cgit v1.2.3