diff options
Diffstat (limited to 'broadcastradio/1.1/default/Tuner.cpp')
-rw-r--r-- | broadcastradio/1.1/default/Tuner.cpp | 449 |
1 files changed, 300 insertions, 149 deletions
diff --git a/broadcastradio/1.1/default/Tuner.cpp b/broadcastradio/1.1/default/Tuner.cpp index ae5848cec..9a34cb128 100644 --- a/broadcastradio/1.1/default/Tuner.cpp +++ b/broadcastradio/1.1/default/Tuner.cpp @@ -14,15 +14,14 @@ * 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 <broadcastradio-utils/Utils.h> +#include <log/log.h> namespace android { namespace hardware { @@ -30,199 +29,351 @@ namespace broadcastradio { namespace V1_1 { namespace implementation { -void Tuner::onCallback(radio_hal_event_t *halEvent) -{ - BandConfig config; - ProgramInfo info; - hidl_vec<MetaData> metadata; - - 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 namespace std::chrono_literals; + +using V1_0::Band; +using V1_0::BandConfig; +using V1_0::Class; +using V1_0::Direction; +using utils::HalRevision; + +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(V1_0::Class classId, const sp<V1_0::ITunerCallback>& callback) + : mClassId(classId), + mCallback(callback), + mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)), + mVirtualRadio(getRadio(classId)), + mIsAnalogForced(false) {} + +void Tuner::forceClose() { + lock_guard<mutex> lk(mMut); + mIsClosed = true; + mThread.cancelAll(); +} + +Return<Result> Tuner::setConfiguration(const BandConfig& config) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + if (mClassId != Class::AM_FM) { + ALOGE("Can't set AM/FM configuration on SAT/DT radio tuner"); + return Result::INVALID_STATE; + } + + 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; + mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit); + + if (utils::isFm(mAmfmConfig.type)) { + mVirtualRadio = std::ref(getFmRadio()); + } else { + mVirtualRadio = std::ref(getAmRadio()); } + + mIsAmfmConfigSet = true; + mCallback->configChange(Result::OK, mAmfmConfig); + }; + mThread.schedule(task, gDefaultDelay.config); + + return Result::OK; +} + +Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + + if (!mIsClosed && mIsAmfmConfigSet) { + _hidl_cb(Result::OK, mAmfmConfig); + } else { + _hidl_cb(Result::NOT_INITIALIZED, {}); } + return {}; } -//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); +// makes ProgramInfo that points to no program +static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) { + ProgramInfo info11 = {}; + auto& info10 = info11.base; + + utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel); + info11.selector = selector; + info11.flags |= ProgramInfoFlags::MUTED; + + return info11; } -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__); +HalRevision Tuner::getHalRev() const { + if (mCallback1_1 != nullptr) { + return HalRevision::V1_1; + } else { + return HalRevision::V1_0; + } } +void Tuner::tuneInternalLocked(const ProgramSelector& sel) { + VirtualProgram virtualProgram; + if (mVirtualRadio.get().getProgram(sel, virtualProgram)) { + mCurrentProgram = virtualProgram.selector; + mCurrentProgramInfo = virtualProgram.getProgramInfo(getHalRev()); + } else { + mCurrentProgram = sel; + mCurrentProgramInfo = makeDummyProgramInfo(sel); + } + mIsTuneCompleted = true; -Tuner::~Tuner() -{ - ALOGV("%s", __FUNCTION__); - const sp<BroadcastRadio> parentDevice = mParentDevice.promote(); - if (parentDevice != 0) { - parentDevice->closeHalTuner(mHalTuner); + if (mCallback1_1 == nullptr) { + mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base); + } else { + mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo.selector); + mCallback1_1->currentProgramInfoChanged(mCurrentProgramInfo); } } -// 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); +Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + + auto list = mVirtualRadio.get().getProgramList(); + + if (list.empty()) { + mIsTuneCompleted = false; + auto task = [this, direction]() { + ALOGI("Performing failed scan %s", toString(direction).c_str()); + + if (mCallback1_1 == nullptr) { + mCallback->tuneComplete(Result::TIMEOUT, {}); + } else { + 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 (utils::tunesTo(current, found->selector)) found++; + } else { + found = list.begin(); + } + } else { + if (found > list.begin() && found != list.end()) { + found--; + } else { + found = list.end() - 1; + } } - radio_hal_band_config_t halConfig; - Utils::convertBandConfigToHal(&halConfig, &config); - int rc = mHalTuner->set_configuration(mHalTuner, &halConfig); - return Utils::convertHalResult(rc); + auto tuneTo = found->selector; + + mIsTuneCompleted = false; + auto task = [this, tuneTo, direction]() { + ALOGI("Performing scan %s", toString(direction).c_str()); + + lock_guard<mutex> lk(mMut); + tuneInternalLocked(tuneTo); + }; + mThread.schedule(task, gDefaultDelay.scan); + + return Result::OK; } -Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) { - int rc; - radio_hal_band_config_t halConfig; - BandConfig config; +Return<Result> Tuner::step(Direction direction, bool skipSubChannel) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; - ALOGV("%s", __FUNCTION__); - if (mHalTuner == NULL) { - rc = -ENODEV; - goto exit; + ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel"); + + if (!utils::isAmFm(utils::getType(mCurrentProgram))) { + ALOGE("Can't step in anything else than AM/FM"); + return Result::NOT_INITIALIZED; } - rc = mHalTuner->get_configuration(mHalTuner, &halConfig); - if (rc == 0) { - Utils::convertBandConfigFromHal(&config, &halConfig); + + if (!mIsAmfmConfigSet) { + ALOGW("AM/FM config not set"); + return Result::INVALID_STATE; } + mIsTuneCompleted = false; + + auto task = [this, direction]() { + ALOGI("Performing step %s", toString(direction).c_str()); + + lock_guard<mutex> lk(mMut); -exit: - _hidl_cb(Utils::convertHalResult(rc), config); - return Void(); + auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0); + + if (direction == Direction::UP) { + current += mAmfmConfig.spacings[0]; + } else { + current -= mAmfmConfig.spacings[0]; + } + + if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit; + if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit; + + tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current)); + }; + mThread.schedule(task, gDefaultDelay.step); + + return Result::OK; } -Return<Result> Tuner::scan(Direction direction, bool skipSubChannel) { - if (mHalTuner == NULL) { - return Utils::convertHalResult(-ENODEV); +Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) { + ALOGV("%s(%d, %d)", __func__, channel, subChannel); + Band band; + { + lock_guard<mutex> lk(mMut); + band = mAmfmConfig.type; } - int rc = mHalTuner->scan(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel); - return Utils::convertHalResult(rc); + return tuneByProgramSelector(utils::make_selector(band, channel, subChannel)); } -Return<Result> Tuner::step(Direction direction, bool skipSubChannel) { - if (mHalTuner == NULL) { - return Utils::convertHalResult(-ENODEV); +Return<Result> Tuner::tuneByProgramSelector(const ProgramSelector& sel) { + ALOGV("%s(%s)", __func__, toString(sel).c_str()); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + + // checking if ProgramSelector is valid + auto programType = utils::getType(sel); + if (utils::isAmFm(programType)) { + if (!mIsAmfmConfigSet) { + ALOGW("AM/FM config not set"); + return Result::INVALID_STATE; + } + + auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY); + if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) { + return Result::INVALID_ARGUMENTS; + } + } else if (programType == ProgramType::DAB) { + if (!utils::hasId(sel, IdentifierType::DAB_SIDECC)) return Result::INVALID_ARGUMENTS; + } else if (programType == ProgramType::DRMO) { + if (!utils::hasId(sel, IdentifierType::DRMO_SERVICE_ID)) return Result::INVALID_ARGUMENTS; + } else if (programType == ProgramType::SXM) { + if (!utils::hasId(sel, IdentifierType::SXM_SERVICE_ID)) return Result::INVALID_ARGUMENTS; + } else { + return Result::INVALID_ARGUMENTS; } - int rc = mHalTuner->step(mHalTuner, static_cast<radio_direction_t>(direction), skipSubChannel); - return Utils::convertHalResult(rc); + + mIsTuneCompleted = false; + auto task = [this, sel]() { + lock_guard<mutex> lk(mMut); + tuneInternalLocked(sel); + }; + mThread.schedule(task, gDefaultDelay.tune); + + return Result::OK; } -Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) { - if (mHalTuner == NULL) { - return Utils::convertHalResult(-ENODEV); - } - int rc = mHalTuner->tune(mHalTuner, channel, subChannel); - return Utils::convertHalResult(rc); +Return<Result> Tuner::cancel() { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + + mThread.cancelAll(); + return Result::OK; } -Return<Result> Tuner::cancel() { - if (mHalTuner == NULL) { - return Utils::convertHalResult(-ENODEV); - } - int rc = mHalTuner->cancel(mHalTuner); - return Utils::convertHalResult(rc); +Return<Result> Tuner::cancelAnnouncement() { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + + return Result::OK; } -Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) { - ALOGV("%s", __FUNCTION__); +Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) { + 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", __FUNCTION__); - if (mHalTuner == NULL) { - rc = -ENODEV; - goto exit; - } +Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); - rc = mHalTuner->get_program_information(mHalTuner, &halInfo); - if (rc == 0) { - Utils::convertProgramInfoFromHal(&info, &halInfo); + if (mIsClosed) { + _hidl_cb(Result::NOT_INITIALIZED, {}); + } else if (mIsTuneCompleted) { + _hidl_cb(Result::OK, mCurrentProgramInfo); + } else { + _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram)); } - -exit: - _hidl_cb(Utils::convertHalResult(rc), info); - return Void(); + return {}; } Return<ProgramListResult> Tuner::startBackgroundScan() { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return ProgramListResult::NOT_INITIALIZED; + 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); - return Void(); +Return<void> Tuner::getProgramList(const hidl_vec<VendorKeyValue>& vendorFilter, + getProgramList_cb _hidl_cb) { + ALOGV("%s(%s)", __func__, toString(vendorFilter).substr(0, 100).c_str()); + lock_guard<mutex> lk(mMut); + if (mIsClosed) { + _hidl_cb(ProgramListResult::NOT_INITIALIZED, {}); + return {}; + } + + auto list = mVirtualRadio.get().getProgramList(); + ALOGD("returning a list of %zu programs", list.size()); + _hidl_cb(ProgramListResult::OK, getProgramInfoVector(list, getHalRev())); + return {}; } -Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) { - // TODO(b/34348946): do the actual implementation. - _hidl_cb(Result::INVALID_STATE, false); - return Void(); +Return<Result> Tuner::setAnalogForced(bool isForced) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + if (mIsClosed) return Result::NOT_INITIALIZED; + + mIsAnalogForced = isForced; + return Result::OK; } -Return<Result> Tuner::setAnalogForced(bool isForced __unused) { - // TODO(b/34348946): do the actual implementation. - return Result::INVALID_STATE; +Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) { + ALOGV("%s", __func__); + lock_guard<mutex> lk(mMut); + + if (mIsClosed) { + _hidl_cb(Result::NOT_INITIALIZED, false); + } else { + _hidl_cb(Result::OK, mIsAnalogForced); + } + return {}; } -} // namespace implementation +} // namespace implementation } // namespace V1_1 } // namespace broadcastradio } // namespace hardware |