Loading broadcastradio/2.0/default/BroadcastRadio.cpp +6 −1 Original line number Diff line number Diff line Loading @@ -112,6 +112,12 @@ Return<void> BroadcastRadio::getDabRegionConfig(getDabRegionConfig_cb _hidl_cb) Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback, openSession_cb _hidl_cb) { ALOGV("%s", __func__); /* For the needs of default implementation it's fine to instantiate new session object * out of the lock scope. If your implementation needs it, use reentrant lock. */ sp<TunerSession> newSession = new TunerSession(*this, callback); lock_guard<mutex> lk(mMut); auto oldSession = mSession.promote(); Loading @@ -121,7 +127,6 @@ Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback, mSession = nullptr; } sp<TunerSession> newSession = new TunerSession(*this, callback); mSession = newSession; _hidl_cb(Result::OK, newSession); Loading broadcastradio/2.0/default/TunerSession.cpp +30 −5 Original line number Diff line number Diff line Loading @@ -50,7 +50,12 @@ static constexpr auto list = 1s; } // namespace delay TunerSession::TunerSession(BroadcastRadio& module, const sp<ITunerCallback>& callback) : mCallback(callback), mModule(module) {} : mCallback(callback), mModule(module) { auto&& ranges = module.getAmFmConfig().ranges; if (ranges.size() > 0) { tuneInternalLocked(utils::make_selector_amfm(ranges[0].lowerBound)); } } // makes ProgramInfo that points to no program static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) { Loading @@ -63,6 +68,8 @@ static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) { } void TunerSession::tuneInternalLocked(const ProgramSelector& sel) { ALOGV("%s(%s)", __func__, toString(sel).c_str()); VirtualProgram virtualProgram; ProgramInfo programInfo; if (virtualRadio().getProgram(sel, virtualProgram)) { Loading Loading @@ -100,6 +107,8 @@ Return<Result> TunerSession::tune(const ProgramSelector& sel) { return Result::INVALID_ARGUMENTS; } cancelLocked(); mIsTuneCompleted = false; auto task = [this, sel]() { lock_guard<mutex> lk(mMut); Loading @@ -115,6 +124,8 @@ Return<Result> TunerSession::scan(bool directionUp, bool /* skipSubChannel */) { lock_guard<mutex> lk(mMut); if (mIsClosed) return Result::INVALID_STATE; cancelLocked(); auto list = virtualRadio().getProgramList(); if (list.empty()) { Loading Loading @@ -166,13 +177,13 @@ Return<Result> TunerSession::step(bool directionUp) { lock_guard<mutex> lk(mMut); if (mIsClosed) return Result::INVALID_STATE; cancelLocked(); if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) { ALOGE("Can't step in anything else than AM/FM"); return Result::NOT_SUPPORTED; } mIsTuneCompleted = false; auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY); auto range = getAmFmRangeLocked(); if (!range) { Loading @@ -188,6 +199,7 @@ Return<Result> TunerSession::step(bool directionUp) { if (stepTo > range->upperBound) stepTo = range->lowerBound; if (stepTo < range->lowerBound) stepTo = range->upperBound; mIsTuneCompleted = false; auto task = [this, stepTo]() { ALOGI("Performing step to %s", std::to_string(stepTo).c_str()); Loading @@ -200,12 +212,22 @@ Return<Result> TunerSession::step(bool directionUp) { return Result::OK; } void TunerSession::cancelLocked() { ALOGV("%s", __func__); mThread.cancelAll(); if (utils::getType(mCurrentProgram.primaryId) != IdentifierType::INVALID) { mIsTuneCompleted = true; } } Return<void> TunerSession::cancel() { ALOGV("%s", __func__); lock_guard<mutex> lk(mMut); if (mIsClosed) return {}; mThread.cancelAll(); cancelLocked(); return {}; } Loading Loading @@ -281,7 +303,10 @@ Return<void> TunerSession::close() { } std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const { if (!mIsTuneCompleted) return {}; if (!mIsTuneCompleted) { ALOGW("tune operation in process"); return {}; } if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {}; auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY); Loading broadcastradio/2.0/default/TunerSession.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ struct TunerSession : public ITunerSession { bool mIsTuneCompleted = false; ProgramSelector mCurrentProgram = {}; void cancelLocked(); void tuneInternalLocked(const ProgramSelector& sel); const VirtualRadio& virtualRadio() const; const BroadcastRadio& module() const; Loading broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp +33 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ #define LOG_TAG "BcRadio.vts" #define LOG_NDEBUG 0 #define EGMOCK_VERBOSE 1 #include <VtsHalHidlTargetTestBase.h> #include <android-base/logging.h> Loading Loading @@ -113,7 +115,15 @@ static void printSkipped(std::string msg) { std::cout << "[ SKIPPED ] " << msg << std::endl; } MATCHER_P(InfoHasId, id, std::string(negation ? "does not contain" : "contains") + " " + toString(id)) { auto ids = utils::getAllIds(arg.selector, utils::getType(id)); return ids.end() != find(ids.begin(), ids.end(), id.value); } TunerCallbackMock::TunerCallbackMock() { EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged, _).Times(AnyNumber()); // we expect the antenna is connected through the whole test EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0); } Loading Loading @@ -362,9 +372,19 @@ TEST_F(BroadcastRadioHalTest, FmTune) { uint64_t freq = 100100; // 100.1 FM auto sel = make_selector_amfm(freq); /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged * callback setting infoCb, because egmock cannot distinguish calls with different matchers * (there is one here and one in callback constructor). * * This sleep workaround will fix default implementation, but the real HW tests will still be * flaky. We probably need to implement egmock alternative based on actions. */ std::this_thread::sleep_for(100ms); // try tuning ProgramInfo infoCb = {}; EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _) EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq))) .Times(AnyNumber()) .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void())))); auto result = mSession->tune(sel); Loading @@ -379,6 +399,8 @@ TEST_F(BroadcastRadioHalTest, FmTune) { EXPECT_EQ(Result::OK, result); EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune); ALOGD("current program info: %s", toString(infoCb).c_str()); // it should tune exactly to what was requested auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY); EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq)); Loading Loading @@ -442,6 +464,9 @@ TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) { TEST_F(BroadcastRadioHalTest, Scan) { ASSERT_TRUE(openSession()); // TODO(b/69958777): see FmTune workaround std::this_thread::sleep_for(100ms); EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _); auto result = mSession->scan(true /* up */, true /* skip subchannel */); EXPECT_EQ(Result::OK, result); Loading @@ -464,9 +489,15 @@ TEST_F(BroadcastRadioHalTest, Scan) { TEST_F(BroadcastRadioHalTest, Step) { ASSERT_TRUE(openSession()); // TODO(b/69958777): see FmTune workaround std::this_thread::sleep_for(100ms); EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber()); auto result = mSession->step(true /* up */); if (result == Result::NOT_SUPPORTED) return; if (result == Result::NOT_SUPPORTED) { printSkipped("step not supported"); return; } EXPECT_EQ(Result::OK, result); EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune); Loading broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h +21 −3 Original line number Diff line number Diff line Loading @@ -13,12 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #ifndef ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #define ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #include <gmock/gmock.h> #include <thread> #ifndef EGMOCK_VERBOSE #define EGMOCK_VERBOSE 0 #endif /** * Print log message. * * INTERNAL IMPLEMENTATION - don't use in user code. */ #if EGMOCK_VERBOSE #define EGMOCK_LOG_(...) ALOGV("egmock: " __VA_ARGS__) #else #define EGMOCK_LOG_(...) #endif /** * Common helper objects for gmock timeout extension. * Loading Loading @@ -61,6 +76,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo auto invokeMock = [&]() { return egmock_##Method(__VA_ARGS__); }; \ auto notify = [&]() { \ std::lock_guard<std::mutex> lk(egmock_mut_##Method); \ EGMOCK_LOG_(#Method " called"); \ egmock_called_##Method = true; \ egmock_cond_##Method.notify_all(); \ }; \ Loading Loading @@ -105,6 +121,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo * EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD); */ #define EXPECT_TIMEOUT_CALL(obj, Method, ...) \ EGMOCK_LOG_(#Method " expected to call"); \ (obj).egmock_called_##Method = false; \ EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__)) Loading @@ -124,6 +141,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo */ #define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout) \ { \ EGMOCK_LOG_("waiting for " #Method " call"); \ std::unique_lock<std::mutex> lk((obj).egmock_mut_##Method); \ if (!(obj).egmock_called_##Method) { \ auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \ Loading @@ -131,4 +149,4 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo } \ } #endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #endif // ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT Loading
broadcastradio/2.0/default/BroadcastRadio.cpp +6 −1 Original line number Diff line number Diff line Loading @@ -112,6 +112,12 @@ Return<void> BroadcastRadio::getDabRegionConfig(getDabRegionConfig_cb _hidl_cb) Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback, openSession_cb _hidl_cb) { ALOGV("%s", __func__); /* For the needs of default implementation it's fine to instantiate new session object * out of the lock scope. If your implementation needs it, use reentrant lock. */ sp<TunerSession> newSession = new TunerSession(*this, callback); lock_guard<mutex> lk(mMut); auto oldSession = mSession.promote(); Loading @@ -121,7 +127,6 @@ Return<void> BroadcastRadio::openSession(const sp<ITunerCallback>& callback, mSession = nullptr; } sp<TunerSession> newSession = new TunerSession(*this, callback); mSession = newSession; _hidl_cb(Result::OK, newSession); Loading
broadcastradio/2.0/default/TunerSession.cpp +30 −5 Original line number Diff line number Diff line Loading @@ -50,7 +50,12 @@ static constexpr auto list = 1s; } // namespace delay TunerSession::TunerSession(BroadcastRadio& module, const sp<ITunerCallback>& callback) : mCallback(callback), mModule(module) {} : mCallback(callback), mModule(module) { auto&& ranges = module.getAmFmConfig().ranges; if (ranges.size() > 0) { tuneInternalLocked(utils::make_selector_amfm(ranges[0].lowerBound)); } } // makes ProgramInfo that points to no program static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) { Loading @@ -63,6 +68,8 @@ static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) { } void TunerSession::tuneInternalLocked(const ProgramSelector& sel) { ALOGV("%s(%s)", __func__, toString(sel).c_str()); VirtualProgram virtualProgram; ProgramInfo programInfo; if (virtualRadio().getProgram(sel, virtualProgram)) { Loading Loading @@ -100,6 +107,8 @@ Return<Result> TunerSession::tune(const ProgramSelector& sel) { return Result::INVALID_ARGUMENTS; } cancelLocked(); mIsTuneCompleted = false; auto task = [this, sel]() { lock_guard<mutex> lk(mMut); Loading @@ -115,6 +124,8 @@ Return<Result> TunerSession::scan(bool directionUp, bool /* skipSubChannel */) { lock_guard<mutex> lk(mMut); if (mIsClosed) return Result::INVALID_STATE; cancelLocked(); auto list = virtualRadio().getProgramList(); if (list.empty()) { Loading Loading @@ -166,13 +177,13 @@ Return<Result> TunerSession::step(bool directionUp) { lock_guard<mutex> lk(mMut); if (mIsClosed) return Result::INVALID_STATE; cancelLocked(); if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) { ALOGE("Can't step in anything else than AM/FM"); return Result::NOT_SUPPORTED; } mIsTuneCompleted = false; auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY); auto range = getAmFmRangeLocked(); if (!range) { Loading @@ -188,6 +199,7 @@ Return<Result> TunerSession::step(bool directionUp) { if (stepTo > range->upperBound) stepTo = range->lowerBound; if (stepTo < range->lowerBound) stepTo = range->upperBound; mIsTuneCompleted = false; auto task = [this, stepTo]() { ALOGI("Performing step to %s", std::to_string(stepTo).c_str()); Loading @@ -200,12 +212,22 @@ Return<Result> TunerSession::step(bool directionUp) { return Result::OK; } void TunerSession::cancelLocked() { ALOGV("%s", __func__); mThread.cancelAll(); if (utils::getType(mCurrentProgram.primaryId) != IdentifierType::INVALID) { mIsTuneCompleted = true; } } Return<void> TunerSession::cancel() { ALOGV("%s", __func__); lock_guard<mutex> lk(mMut); if (mIsClosed) return {}; mThread.cancelAll(); cancelLocked(); return {}; } Loading Loading @@ -281,7 +303,10 @@ Return<void> TunerSession::close() { } std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const { if (!mIsTuneCompleted) return {}; if (!mIsTuneCompleted) { ALOGW("tune operation in process"); return {}; } if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {}; auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY); Loading
broadcastradio/2.0/default/TunerSession.h +1 −0 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ struct TunerSession : public ITunerSession { bool mIsTuneCompleted = false; ProgramSelector mCurrentProgram = {}; void cancelLocked(); void tuneInternalLocked(const ProgramSelector& sel); const VirtualRadio& virtualRadio() const; const BroadcastRadio& module() const; Loading
broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp +33 −2 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ #define LOG_TAG "BcRadio.vts" #define LOG_NDEBUG 0 #define EGMOCK_VERBOSE 1 #include <VtsHalHidlTargetTestBase.h> #include <android-base/logging.h> Loading Loading @@ -113,7 +115,15 @@ static void printSkipped(std::string msg) { std::cout << "[ SKIPPED ] " << msg << std::endl; } MATCHER_P(InfoHasId, id, std::string(negation ? "does not contain" : "contains") + " " + toString(id)) { auto ids = utils::getAllIds(arg.selector, utils::getType(id)); return ids.end() != find(ids.begin(), ids.end(), id.value); } TunerCallbackMock::TunerCallbackMock() { EXPECT_TIMEOUT_CALL(*this, onCurrentProgramInfoChanged, _).Times(AnyNumber()); // we expect the antenna is connected through the whole test EXPECT_CALL(*this, onAntennaStateChange(false)).Times(0); } Loading Loading @@ -362,9 +372,19 @@ TEST_F(BroadcastRadioHalTest, FmTune) { uint64_t freq = 100100; // 100.1 FM auto sel = make_selector_amfm(freq); /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged * callback setting infoCb, because egmock cannot distinguish calls with different matchers * (there is one here and one in callback constructor). * * This sleep workaround will fix default implementation, but the real HW tests will still be * flaky. We probably need to implement egmock alternative based on actions. */ std::this_thread::sleep_for(100ms); // try tuning ProgramInfo infoCb = {}; EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _) EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, InfoHasId(utils::make_identifier(IdentifierType::AMFM_FREQUENCY, freq))) .Times(AnyNumber()) .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void())))); auto result = mSession->tune(sel); Loading @@ -379,6 +399,8 @@ TEST_F(BroadcastRadioHalTest, FmTune) { EXPECT_EQ(Result::OK, result); EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune); ALOGD("current program info: %s", toString(infoCb).c_str()); // it should tune exactly to what was requested auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::AMFM_FREQUENCY); EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq)); Loading Loading @@ -442,6 +464,9 @@ TEST_F(BroadcastRadioHalTest, TuneFailsWithEmpty) { TEST_F(BroadcastRadioHalTest, Scan) { ASSERT_TRUE(openSession()); // TODO(b/69958777): see FmTune workaround std::this_thread::sleep_for(100ms); EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _); auto result = mSession->scan(true /* up */, true /* skip subchannel */); EXPECT_EQ(Result::OK, result); Loading @@ -464,9 +489,15 @@ TEST_F(BroadcastRadioHalTest, Scan) { TEST_F(BroadcastRadioHalTest, Step) { ASSERT_TRUE(openSession()); // TODO(b/69958777): see FmTune workaround std::this_thread::sleep_for(100ms); EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged, _).Times(AnyNumber()); auto result = mSession->step(true /* up */); if (result == Result::NOT_SUPPORTED) return; if (result == Result::NOT_SUPPORTED) { printSkipped("step not supported"); return; } EXPECT_EQ(Result::OK, result); EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged, timeout::tune); Loading
broadcastradio/common/vts/utils/include/broadcastradio-vts-utils/mock-timeout.h +21 −3 Original line number Diff line number Diff line Loading @@ -13,12 +13,27 @@ * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #define ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #ifndef ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #define ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT #include <gmock/gmock.h> #include <thread> #ifndef EGMOCK_VERBOSE #define EGMOCK_VERBOSE 0 #endif /** * Print log message. * * INTERNAL IMPLEMENTATION - don't use in user code. */ #if EGMOCK_VERBOSE #define EGMOCK_LOG_(...) ALOGV("egmock: " __VA_ARGS__) #else #define EGMOCK_LOG_(...) #endif /** * Common helper objects for gmock timeout extension. * Loading Loading @@ -61,6 +76,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo auto invokeMock = [&]() { return egmock_##Method(__VA_ARGS__); }; \ auto notify = [&]() { \ std::lock_guard<std::mutex> lk(egmock_mut_##Method); \ EGMOCK_LOG_(#Method " called"); \ egmock_called_##Method = true; \ egmock_cond_##Method.notify_all(); \ }; \ Loading Loading @@ -105,6 +121,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo * EXPECT_TIMEOUT_CALL(account, charge, 100, Currency::USD); */ #define EXPECT_TIMEOUT_CALL(obj, Method, ...) \ EGMOCK_LOG_(#Method " expected to call"); \ (obj).egmock_called_##Method = false; \ EXPECT_CALL(obj, egmock_##Method(__VA_ARGS__)) Loading @@ -124,6 +141,7 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo */ #define EXPECT_TIMEOUT_CALL_WAIT(obj, Method, timeout) \ { \ EGMOCK_LOG_("waiting for " #Method " call"); \ std::unique_lock<std::mutex> lk((obj).egmock_mut_##Method); \ if (!(obj).egmock_called_##Method) { \ auto status = (obj).egmock_cond_##Method.wait_for(lk, timeout); \ Loading @@ -131,4 +149,4 @@ inline void EGMockFlippedComma_(std::function<void()> returned, std::function<vo } \ } #endif // ANDROID_HARDWARE_BROADCASTRADIO_V1_1_MOCK_TIMEOUT #endif // ANDROID_HARDWARE_BROADCASTRADIO_VTS_MOCK_TIMEOUT