Initial Broadcast Radio HAL 1.1 default implementation.
The new implementation does not rely on legacy HAL, it implements
HAL 1.1 directly instead.
This is not a full implementation yet, but passes all existing tests.
Bug: b/36864090
Test: VTS, instrumentalization, Unit
Change-Id: Ic8eddd19d0bf6777b4086d099183d71e05d7da0e
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp
index ae5848c..64d8b89 100644
--- a/broadcastradio/1.1/default/Tuner.cpp
+++ b/broadcastradio/1.1/default/Tuner.cpp
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-#define LOG_TAG "Tuner"
-//#define LOG_NDEBUG 0
-
-#include <log/log.h>
+#define LOG_TAG "BroadcastRadioDefault.tuner"
+#define LOG_NDEBUG 0
#include "BroadcastRadio.h"
#include "Tuner.h"
-#include "Utils.h"
-#include <system/RadioMetadataWrapper.h>
+
+#include <log/log.h>
namespace android {
namespace hardware {
@@ -30,199 +28,283 @@
namespace V1_1 {
namespace implementation {
-void Tuner::onCallback(radio_hal_event_t *halEvent)
-{
- BandConfig config;
- ProgramInfo info;
- hidl_vec<MetaData> metadata;
+using namespace std::chrono_literals;
- if (mCallback != 0) {
- switch(halEvent->type) {
- case RADIO_EVENT_CONFIG:
- Utils::convertBandConfigFromHal(&config, &halEvent->config);
- mCallback->configChange(Utils::convertHalResult(halEvent->status), config);
- break;
- case RADIO_EVENT_ANTENNA:
- mCallback->antennaStateChange(halEvent->on);
- break;
- case RADIO_EVENT_TUNED:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->tuneComplete_1_1(Utils::convertHalResult(halEvent->status), info);
- }
- mCallback->tuneComplete(Utils::convertHalResult(halEvent->status), info.base);
- break;
- case RADIO_EVENT_METADATA: {
- uint32_t channel;
- uint32_t sub_channel;
- if (radio_metadata_get_channel(halEvent->metadata, &channel, &sub_channel) == 0) {
- Utils::convertMetaDataFromHal(metadata, halEvent->metadata);
- mCallback->newMetadata(channel, sub_channel, metadata);
- }
- } break;
- case RADIO_EVENT_TA:
- mCallback->trafficAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_AF_SWITCH:
- Utils::convertProgramInfoFromHal(&info, &halEvent->info);
- if (mCallback1_1 != nullptr) {
- mCallback1_1->afSwitch_1_1(info);
- }
- mCallback->afSwitch(info.base);
- break;
- case RADIO_EVENT_EA:
- mCallback->emergencyAnnouncement(halEvent->on);
- break;
- case RADIO_EVENT_HW_FAILURE:
- default:
- mCallback->hardwareFailure();
- break;
- }
- }
+using V1_0::Band;
+using V1_0::BandConfig;
+using V1_0::Direction;
+
+using std::chrono::milliseconds;
+using std::lock_guard;
+using std::move;
+using std::mutex;
+using std::sort;
+using std::vector;
+
+const struct {
+ milliseconds config = 50ms;
+ milliseconds scan = 200ms;
+ milliseconds step = 100ms;
+ milliseconds tune = 150ms;
+} gDefaultDelay;
+
+Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback)
+ : mCallback(callback),
+ mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)),
+ mVirtualFm(make_fm_radio()) {}
+
+void Tuner::forceClose() {
+ lock_guard<mutex> lk(mMut);
+ mIsClosed = true;
+ mThread.cancelAll();
}
-//static
-void Tuner::callback(radio_hal_event_t *halEvent, void *cookie)
-{
- wp<Tuner> weak(reinterpret_cast<Tuner*>(cookie));
- sp<Tuner> tuner = weak.promote();
- if (tuner == 0) return;
- tuner->onCallback(halEvent);
+Return<Result> Tuner::setConfiguration(const BandConfig& config) {
+ ALOGV("%s", __func__);
+
+ if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
+
+ auto task = [this, config]() {
+ ALOGI("Setting AM/FM config");
+ lock_guard<mutex> lk(mMut);
+
+ mAmfmConfig = move(config);
+ mAmfmConfig.antennaConnected = true;
+ mIsAmfmConfigSet = true;
+ mCallback->configChange(Result::OK, mAmfmConfig);
+ };
+ mThread.schedule(task, gDefaultDelay.config);
+
+ return Result::OK;
}
-Tuner::Tuner(const sp<V1_0::ITunerCallback>& callback, const wp<BroadcastRadio>& parentDevice)
- : mHalTuner(NULL), mCallback(callback), mCallback1_1(ITunerCallback::castFrom(callback)),
- mParentDevice(parentDevice)
-{
- ALOGV("%s", __FUNCTION__);
-}
+Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
+ ALOGV("%s", __func__);
-
-Tuner::~Tuner()
-{
- ALOGV("%s", __FUNCTION__);
- const sp<BroadcastRadio> parentDevice = mParentDevice.promote();
- if (parentDevice != 0) {
- parentDevice->closeHalTuner(mHalTuner);
+ lock_guard<mutex> lk(mMut);
+ if (mIsAmfmConfigSet) {
+ _hidl_cb(Result::OK, mAmfmConfig);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, {});
}
-}
-
-// Methods from ::android::hardware::broadcastradio::V1_1::ITuner follow.
-Return<Result> Tuner::setConfiguration(const BandConfig& config) {
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- radio_hal_band_config_t halConfig;
- Utils::convertBandConfigToHal(&halConfig, &config);
- int rc = mHalTuner->set_configuration(mHalTuner, &halConfig);
- return Utils::convertHalResult(rc);
-}
-
-Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
- int rc;
- radio_hal_band_config_t halConfig;
- BandConfig config;
-
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
- }
- rc = mHalTuner->get_configuration(mHalTuner, &halConfig);
- if (rc == 0) {
- Utils::convertBandConfigFromHal(&config, &halConfig);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), config);
return Void();
}
-Return<Result> Tuner::scan(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- int rc = mHalTuner->scan(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+// makes ProgramInfo that points to no channel
+static ProgramInfo makeDummyProgramInfo(uint32_t channel) {
+ ProgramInfo info11 = {};
+ auto& info10 = info11.base;
+
+ info10.channel = channel;
+ info11.flags |= ProgramInfoFlags::MUTED;
+
+ return info11;
}
-Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+void Tuner::tuneInternalLocked() {
+ VirtualRadio* virtualRadio = nullptr;
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ virtualRadio = &mVirtualFm;
}
- int rc = mHalTuner->step(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel);
- return Utils::convertHalResult(rc);
+
+ auto& info11 = mCurrentProgramInfo;
+ auto& info10 = info11.base;
+
+ VirtualProgram virtualProgram;
+ if (virtualRadio != nullptr && virtualRadio->getProgram(mCurrentProgram, virtualProgram)) {
+ // TODO(b/36864090): convert virtualProgram to ProgramInfo instead
+ info10.channel = mCurrentProgram;
+ info10.tuned = true;
+ info10.stereo = true;
+ info10.signalStrength = 100;
+ } else {
+ info11 = makeDummyProgramInfo(mCurrentProgram);
+ }
+ mIsTuneCompleted = true;
+
+ mCallback->tuneComplete(Result::OK, info10);
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::OK, info11);
+ }
+}
+
+Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+ lock_guard<mutex> lk(mMut);
+ vector<VirtualProgram> list;
+
+ if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
+ list = mVirtualFm.getProgramList();
+ }
+
+ if (list.size() == 0) {
+ mIsTuneCompleted = false;
+ auto task = [this, direction]() {
+ ALOGI("Performing failed scan %s", toString(direction).c_str());
+
+ mCallback->tuneComplete(Result::TIMEOUT, {});
+ if (mCallback1_1 != nullptr) {
+ mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
+ }
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+ }
+
+ // Not optimal (O(sort) instead of O(n)), but not a big deal here;
+ // also, it's likely that list is already sorted (so O(n) anyway).
+ sort(list.begin(), list.end());
+ auto current = mCurrentProgram;
+ auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
+ if (direction == Direction::UP) {
+ if (found < list.end() - 1) {
+ if (found->channel == current) found++;
+ } else {
+ found = list.begin();
+ }
+ } else {
+ if (found > list.begin() && found != list.end()) {
+ found--;
+ } else {
+ found = list.end() - 1;
+ }
+ }
+ auto tuneTo = found->channel;
+
+ mIsTuneCompleted = false;
+ auto task = [this, tuneTo, direction]() {
+ ALOGI("Performing scan %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+ mCurrentProgram = tuneTo;
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.scan);
+
+ return Result::OK;
+}
+
+Return<Result> Tuner::step(Direction direction, bool skipSubChannel __unused) {
+ ALOGV("%s", __func__);
+
+ lock_guard<mutex> lk(mMut);
+ ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+ if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+ mIsTuneCompleted = false;
+
+ auto task = [this, direction]() {
+ ALOGI("Performing step %s", toString(direction).c_str());
+
+ lock_guard<mutex> lk(mMut);
+
+ if (direction == Direction::UP) {
+ mCurrentProgram += mAmfmConfig.spacings[0];
+ } else {
+ mCurrentProgram -= mAmfmConfig.spacings[0];
+ }
+
+ if (mCurrentProgram > mAmfmConfig.upperLimit) mCurrentProgram = mAmfmConfig.lowerLimit;
+ if (mCurrentProgram < mAmfmConfig.lowerLimit) mCurrentProgram = mAmfmConfig.upperLimit;
+
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.step);
+
+ return Result::OK;
}
Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
+ ALOGV("%s(%d, %d)", __func__, channel, subChannel);
+
+ lock_guard<mutex> lk(mMut);
+ ALOGW_IF(!mIsAmfmConfigSet, "AM/FM config not set");
+ if (!mIsAmfmConfigSet) return Result::INVALID_STATE;
+ if (channel < mAmfmConfig.lowerLimit || channel > mAmfmConfig.upperLimit) {
+ return Result::INVALID_ARGUMENTS;
}
- int rc = mHalTuner->tune(mHalTuner, channel, subChannel);
- return Utils::convertHalResult(rc);
+ mIsTuneCompleted = false;
+
+ auto task = [this, channel]() {
+ lock_guard<mutex> lk(mMut);
+ mCurrentProgram = channel;
+ tuneInternalLocked();
+ };
+ mThread.schedule(task, gDefaultDelay.tune);
+
+ return Result::OK;
}
Return<Result> Tuner::cancel() {
- if (mHalTuner == NULL) {
- return Utils::convertHalResult(-ENODEV);
- }
- int rc = mHalTuner->cancel(mHalTuner);
- return Utils::convertHalResult(rc);
+ ALOGV("%s", __func__);
+ mThread.cancelAll();
+ return Result::OK;
}
Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
- ALOGV("%s", __FUNCTION__);
+ ALOGV("%s", __func__);
return getProgramInformation_1_1([&](Result result, const ProgramInfo& info) {
_hidl_cb(result, info.base);
});
}
Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
- int rc;
- radio_program_info_t halInfo;
- RadioMetadataWrapper metadataWrapper(&halInfo.metadata);
- ProgramInfo info;
+ ALOGV("%s", __func__);
- ALOGV("%s", __FUNCTION__);
- if (mHalTuner == NULL) {
- rc = -ENODEV;
- goto exit;
+ lock_guard<mutex> lk(mMut);
+ if (mIsTuneCompleted) {
+ _hidl_cb(Result::OK, mCurrentProgramInfo);
+ } else {
+ _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
}
-
- rc = mHalTuner->get_program_information(mHalTuner, &halInfo);
- if (rc == 0) {
- Utils::convertProgramInfoFromHal(&info, &halInfo);
- }
-
-exit:
- _hidl_cb(Utils::convertHalResult(rc), info);
return Void();
}
Return<ProgramListResult> Tuner::startBackgroundScan() {
+ ALOGV("%s", __func__);
return ProgramListResult::UNAVAILABLE;
}
Return<void> Tuner::getProgramList(const hidl_string& filter __unused, getProgramList_cb _hidl_cb) {
- hidl_vec<ProgramInfo> pList;
- // TODO(b/34054813): do the actual implementation.
- _hidl_cb(ProgramListResult::NOT_STARTED, pList);
+ ALOGV("%s", __func__);
+
+ auto& virtualRadio = mVirtualFm;
+ if (mAmfmConfig.type != Band::FM_HD && mAmfmConfig.type != Band::FM) {
+ _hidl_cb(ProgramListResult::OK, {});
+ return Void();
+ }
+
+ hidl_vec<ProgramInfo> list;
+ auto vList = virtualRadio.getProgramList();
+ list.resize(vList.size());
+ for (size_t i = 0; i < vList.size(); i++) {
+ auto& src = vList[i];
+ auto& dst11 = list[i];
+ auto& dst10 = dst11.base;
+
+ // TODO(b/36864090): convert virtualProgram to ProgramInfo instead
+ dst10.channel = src.channel;
+ dst10.tuned = true;
+ }
+
+ _hidl_cb(ProgramListResult::OK, list);
return Void();
}
Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
_hidl_cb(Result::INVALID_STATE, false);
return Void();
}
Return<Result> Tuner::setAnalogForced(bool isForced __unused) {
- // TODO(b/34348946): do the actual implementation.
+ ALOGV("%s", __func__);
+ // TODO(b/36864090): implement
return Result::INVALID_STATE;
}
-} // namespace implementation
+} // namespace implementation
} // namespace V1_1
} // namespace broadcastradio
} // namespace hardware