Add mock HD programs to default AIDL bcradio HAL
Added mocked HD radio programs and HD radio tune/seek/step support with
subchannel in the default AIDL broadcast radio HAL implementiation.
Bug: 309694368
Test: atest VtsHalBroadcastradioAidlTargetTest
Change-Id: Id0ba9b127860d98822e9102ba48d913dc38cade1
diff --git a/broadcastradio/aidl/default/BroadcastRadio.cpp b/broadcastradio/aidl/default/BroadcastRadio.cpp
index 63073d7..c7e1134 100644
--- a/broadcastradio/aidl/default/BroadcastRadio.cpp
+++ b/broadcastradio/aidl/default/BroadcastRadio.cpp
@@ -81,10 +81,29 @@
ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
ProgramInfo info = {};
info.selector = selector;
- info.logicallyTunedTo =
- utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
- utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
- info.physicallyTunedTo = info.logicallyTunedTo;
+ switch (info.selector.primaryId.type) {
+ case IdentifierType::AMFM_FREQUENCY_KHZ:
+ info.logicallyTunedTo = utils::makeIdentifier(
+ IdentifierType::AMFM_FREQUENCY_KHZ,
+ utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
+ info.physicallyTunedTo = info.logicallyTunedTo;
+ break;
+ case IdentifierType::HD_STATION_ID_EXT:
+ info.logicallyTunedTo = utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
+ utils::getAmFmFrequency(info.selector));
+ info.physicallyTunedTo = info.logicallyTunedTo;
+ break;
+ case IdentifierType::DAB_SID_EXT:
+ info.logicallyTunedTo = info.selector.primaryId;
+ info.physicallyTunedTo = utils::makeIdentifier(
+ IdentifierType::DAB_FREQUENCY_KHZ,
+ utils::getId(selector, IdentifierType::DAB_FREQUENCY_KHZ));
+ break;
+ default:
+ info.logicallyTunedTo = info.selector.primaryId;
+ info.physicallyTunedTo = info.logicallyTunedTo;
+ break;
+ }
return info;
}
@@ -246,6 +265,99 @@
return ScopedAStatus::ok();
}
+bool BroadcastRadio::findNextLocked(const ProgramSelector& current, bool directionUp,
+ bool skipSubChannel, VirtualProgram* nextProgram) const {
+ if (mProgramList.empty()) {
+ return false;
+ }
+ // The list is not sorted here since it has already stored in VirtualRadio.
+ bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
+ uint32_t currentFreq = hasAmFmFrequency ? utils::getAmFmFrequency(current) : 0;
+ auto found =
+ std::lower_bound(mProgramList.begin(), mProgramList.end(), VirtualProgram({current}));
+ if (directionUp) {
+ if (found < mProgramList.end() - 1) {
+ // When seeking up, tuner will jump to the first selector which is main program service
+ // greater than the current program selector in the program list (if not exist, jump
+ // to the first selector) for skipping sub-channels case or AM/FM without HD radio
+ // enabled case. Otherwise, the tuner will jump to the first selector greater than the
+ // current program selector.
+ if (utils::tunesTo(current, found->selector)) found++;
+ if (skipSubChannel && hasAmFmFrequency) {
+ auto firstFound = found;
+ while (utils::getAmFmFrequency(found->selector) == currentFreq) {
+ if (found < mProgramList.end() - 1) {
+ found++;
+ } else {
+ found = mProgramList.begin();
+ }
+ if (found == firstFound) {
+ // Only one main channel exists in the program list, the tuner cannot skip
+ // sub-channel to the next program selector.
+ return false;
+ }
+ }
+ }
+ } else {
+ // If the selector of current program is no less than all selectors or not found in the
+ // program list, seeking up should wrap the tuner to the beginning of the program list.
+ found = mProgramList.begin();
+ }
+ } else {
+ if (found > mProgramList.begin() && found != mProgramList.end()) {
+ // When seeking down, tuner will jump to the first selector which is main program
+ // service less than the current program selector in the program list (if not exist,
+ // jump to the last main program service selector) for skipping sub-channels case or
+ // AM/FM without HD radio enabled case. Otherwise, the tuner will jump to the first
+ // selector less than the current program selector.
+ found--;
+ if (hasAmFmFrequency && utils::hasAmFmFrequency(found->selector)) {
+ uint32_t nextFreq = utils::getAmFmFrequency(found->selector);
+ if (nextFreq != currentFreq) {
+ jumpToFirstSubChannelLocked(found);
+ } else if (skipSubChannel) {
+ jumpToFirstSubChannelLocked(found);
+ auto firstFound = found;
+ if (found > mProgramList.begin()) {
+ found--;
+ } else {
+ found = mProgramList.end() - 1;
+ }
+ jumpToFirstSubChannelLocked(found);
+ if (found == firstFound) {
+ // Only one main channel exists in the program list, the tuner cannot skip
+ // sub-channel to the next program selector.
+ return false;
+ }
+ }
+ }
+ } else {
+ // If the selector of current program is no greater than all selectors or not found
+ // in the program list, seeking down should wrap the tuner to the end of the program
+ // list. If the last program selector in the program list is sub-channel and skipping
+ // sub-channels is needed, the tuner will jump to the last main program service in
+ // the program list.
+ found = mProgramList.end() - 1;
+ jumpToFirstSubChannelLocked(found);
+ }
+ }
+ *nextProgram = *found;
+ return true;
+}
+
+void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
+ if (!utils::hasAmFmFrequency(it->selector) || it == mProgramList.begin()) {
+ return;
+ }
+ uint32_t currentFrequency = utils::getAmFmFrequency(it->selector);
+ it--;
+ while (it != mProgramList.begin() && utils::hasAmFmFrequency(it->selector) &&
+ utils::getAmFmFrequency(it->selector) == currentFrequency) {
+ it--;
+ }
+ it++;
+}
+
ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
<< (skipSubChannel ? "yes" : "no") << "...";
@@ -259,11 +371,14 @@
cancelLocked();
- const auto& list = mVirtualRadio.getProgramList();
+ mProgramList = mVirtualRadio.getProgramList();
std::shared_ptr<ITunerCallback> callback = mCallback;
auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
- if (list.empty()) {
- mIsTuneCompleted = false;
+
+ VirtualProgram nextProgram = {};
+ bool foundNext = findNextLocked(mCurrentProgram, directionUp, skipSubChannel, &nextProgram);
+ mIsTuneCompleted = false;
+ if (!foundNext) {
auto task = [callback]() {
LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
@@ -274,31 +389,11 @@
return ScopedAStatus::ok();
}
- // The list is not sorted here since it has already stored in VirtualRadio.
- // If the list is not sorted in advance, it should be sorted here.
- const auto& current = mCurrentProgram;
- auto found = std::lower_bound(list.begin(), list.end(), VirtualProgram({current}));
- if (directionUp) {
- if (found < list.end() - 1) {
- if (tunesTo(current, found->selector)) found++;
- } else {
- found = list.begin();
- }
- } else {
- if (found > list.begin() && found != list.end()) {
- found--;
- } else {
- found = list.end() - 1;
- }
- }
- const ProgramSelector tuneTo = found->selector;
-
- mIsTuneCompleted = false;
- auto task = [this, tuneTo, callback]() {
+ auto task = [this, nextProgram, callback]() {
ProgramInfo programInfo = {};
{
lock_guard<mutex> lk(mMutex);
- programInfo = tuneInternalLocked(tuneTo);
+ programInfo = tuneInternalLocked(nextProgram.selector);
}
callback->onCurrentProgramInfoChanged(programInfo);
};
@@ -319,13 +414,17 @@
cancelLocked();
- if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+ int64_t stepTo;
+ if (utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+ stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+ } else if (mCurrentProgram.primaryId.type == IdentifierType::HD_STATION_ID_EXT) {
+ stepTo = utils::getHdFrequency(mCurrentProgram);
+ } else {
LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
}
- int64_t stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
std::optional<AmFmBandRange> range = getAmFmRangeLocked();
if (!range) {
LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
@@ -473,12 +572,12 @@
LOG(WARNING) << __func__ << ": tune operation is in process";
return {};
}
- if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
+ if (!utils::hasAmFmFrequency(mCurrentProgram)) {
LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
return {};
}
- int64_t freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
+ int64_t freq = utils::getAmFmFrequency(mCurrentProgram);
for (const auto& range : mAmFmConfig.ranges) {
if (range.lowerBound <= freq && range.upperBound >= freq) {
return range;