Home | History | Annotate | Download | only in default
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "BroadcastRadioDefault.tuner"
     18 #define LOG_NDEBUG 0
     19 
     20 #include "Tuner.h"
     21 #include "BroadcastRadio.h"
     22 
     23 #include <broadcastradio-utils-1x/Utils.h>
     24 #include <log/log.h>
     25 
     26 namespace android {
     27 namespace hardware {
     28 namespace broadcastradio {
     29 namespace V1_1 {
     30 namespace implementation {
     31 
     32 using namespace std::chrono_literals;
     33 
     34 using V1_0::Band;
     35 using V1_0::BandConfig;
     36 using V1_0::Class;
     37 using V1_0::Direction;
     38 using V1_1::IdentifierType;
     39 using V1_1::ProgramInfo;
     40 using V1_1::ProgramInfoFlags;
     41 using V1_1::ProgramListResult;
     42 using V1_1::ProgramSelector;
     43 using V1_1::ProgramType;
     44 using V1_1::VendorKeyValue;
     45 using utils::HalRevision;
     46 
     47 using std::chrono::milliseconds;
     48 using std::lock_guard;
     49 using std::move;
     50 using std::mutex;
     51 using std::sort;
     52 using std::vector;
     53 
     54 const struct {
     55     milliseconds config = 50ms;
     56     milliseconds scan = 200ms;
     57     milliseconds step = 100ms;
     58     milliseconds tune = 150ms;
     59 } gDefaultDelay;
     60 
     61 Tuner::Tuner(const sp<BroadcastRadio> module, V1_0::Class classId,
     62              const sp<V1_0::ITunerCallback>& callback)
     63     : mModule(module),
     64       mClassId(classId),
     65       mCallback(callback),
     66       mCallback1_1(V1_1::ITunerCallback::castFrom(callback).withDefault(nullptr)),
     67       mVirtualRadio(getRadio(classId)),
     68       mIsAnalogForced(false) {}
     69 
     70 void Tuner::forceClose() {
     71     lock_guard<mutex> lk(mMut);
     72     mIsClosed = true;
     73     mThread.cancelAll();
     74 }
     75 
     76 void Tuner::setConfigurationInternalLocked(const BandConfig& config) {
     77     mAmfmConfig = config;
     78     mAmfmConfig.antennaConnected = true;
     79     mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
     80 
     81     if (utils::isFm(mAmfmConfig.type)) {
     82         mVirtualRadio = std::ref(getFmRadio());
     83     } else {
     84         mVirtualRadio = std::ref(getAmRadio());
     85     }
     86 
     87     mIsAmfmConfigSet = true;
     88     mCallback->configChange(Result::OK, mAmfmConfig);
     89     if (mCallback1_1 != nullptr) mCallback1_1->programListChanged();
     90 }
     91 
     92 bool Tuner::autoConfigureLocked(uint64_t frequency) {
     93     for (auto&& config : mModule->getAmFmBands()) {
     94         // The check here is rather poor, but it's enough for default implementation.
     95         if (config.lowerLimit <= frequency && config.upperLimit >= frequency) {
     96             ALOGI("Auto-switching band to %s", toString(config).c_str());
     97             setConfigurationInternalLocked(config);
     98             return true;
     99         }
    100     }
    101     return false;
    102 }
    103 
    104 Return<Result> Tuner::setConfiguration(const BandConfig& config) {
    105     ALOGV("%s", __func__);
    106     lock_guard<mutex> lk(mMut);
    107     if (mIsClosed) return Result::NOT_INITIALIZED;
    108     if (mClassId != Class::AM_FM) {
    109         ALOGE("Can't set AM/FM configuration on SAT/DT radio tuner");
    110         return Result::INVALID_STATE;
    111     }
    112 
    113     if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
    114 
    115     auto task = [this, config]() {
    116         ALOGI("Setting AM/FM config");
    117         lock_guard<mutex> lk(mMut);
    118         setConfigurationInternalLocked(config);
    119     };
    120     mThread.schedule(task, gDefaultDelay.config);
    121 
    122     return Result::OK;
    123 }
    124 
    125 Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
    126     ALOGV("%s", __func__);
    127     lock_guard<mutex> lk(mMut);
    128 
    129     if (!mIsClosed && mIsAmfmConfigSet) {
    130         _hidl_cb(Result::OK, mAmfmConfig);
    131     } else {
    132         _hidl_cb(Result::NOT_INITIALIZED, {});
    133     }
    134     return {};
    135 }
    136 
    137 // makes ProgramInfo that points to no program
    138 static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
    139     ProgramInfo info11 = {};
    140     auto& info10 = info11.base;
    141 
    142     utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel);
    143     info11.selector = selector;
    144     info11.flags |= ProgramInfoFlags::MUTED;
    145 
    146     return info11;
    147 }
    148 
    149 HalRevision Tuner::getHalRev() const {
    150     if (mCallback1_1 != nullptr) {
    151         return HalRevision::V1_1;
    152     } else {
    153         return HalRevision::V1_0;
    154     }
    155 }
    156 
    157 void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
    158     VirtualProgram virtualProgram;
    159     if (mVirtualRadio.get().getProgram(sel, virtualProgram)) {
    160         mCurrentProgram = virtualProgram.selector;
    161         mCurrentProgramInfo = virtualProgram.getProgramInfo(getHalRev());
    162     } else {
    163         mCurrentProgram = sel;
    164         mCurrentProgramInfo = makeDummyProgramInfo(sel);
    165     }
    166     mIsTuneCompleted = true;
    167 
    168     if (mCallback1_1 == nullptr) {
    169         mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base);
    170     } else {
    171         mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo.selector);
    172         mCallback1_1->currentProgramInfoChanged(mCurrentProgramInfo);
    173     }
    174 }
    175 
    176 Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
    177     ALOGV("%s", __func__);
    178     lock_guard<mutex> lk(mMut);
    179     if (mIsClosed) return Result::NOT_INITIALIZED;
    180 
    181     auto list = mVirtualRadio.get().getProgramList();
    182 
    183     if (list.empty()) {
    184         mIsTuneCompleted = false;
    185         auto task = [this, direction]() {
    186             ALOGI("Performing failed scan %s", toString(direction).c_str());
    187 
    188             if (mCallback1_1 == nullptr) {
    189                 mCallback->tuneComplete(Result::TIMEOUT, {});
    190             } else {
    191                 mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
    192             }
    193         };
    194         mThread.schedule(task, gDefaultDelay.scan);
    195 
    196         return Result::OK;
    197     }
    198 
    199     // Not optimal (O(sort) instead of O(n)), but not a big deal here;
    200     // also, it's likely that list is already sorted (so O(n) anyway).
    201     sort(list.begin(), list.end());
    202     auto current = mCurrentProgram;
    203     auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
    204     if (direction == Direction::UP) {
    205         if (found < list.end() - 1) {
    206             if (utils::tunesTo(current, found->selector)) found++;
    207         } else {
    208             found = list.begin();
    209         }
    210     } else {
    211         if (found > list.begin() && found != list.end()) {
    212             found--;
    213         } else {
    214             found = list.end() - 1;
    215         }
    216     }
    217     auto tuneTo = found->selector;
    218 
    219     mIsTuneCompleted = false;
    220     auto task = [this, tuneTo, direction]() {
    221         ALOGI("Performing scan %s", toString(direction).c_str());
    222 
    223         lock_guard<mutex> lk(mMut);
    224         tuneInternalLocked(tuneTo);
    225     };
    226     mThread.schedule(task, gDefaultDelay.scan);
    227 
    228     return Result::OK;
    229 }
    230 
    231 Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
    232     ALOGV("%s", __func__);
    233     lock_guard<mutex> lk(mMut);
    234     if (mIsClosed) return Result::NOT_INITIALIZED;
    235 
    236     ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
    237 
    238     if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
    239         ALOGE("Can't step in anything else than AM/FM");
    240         return Result::NOT_INITIALIZED;
    241     }
    242 
    243     if (!mIsAmfmConfigSet) {
    244         ALOGW("AM/FM config not set");
    245         return Result::INVALID_STATE;
    246     }
    247     mIsTuneCompleted = false;
    248 
    249     auto task = [this, direction]() {
    250         ALOGI("Performing step %s", toString(direction).c_str());
    251 
    252         lock_guard<mutex> lk(mMut);
    253 
    254         auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
    255 
    256         if (direction == Direction::UP) {
    257             current += mAmfmConfig.spacings[0];
    258         } else {
    259             current -= mAmfmConfig.spacings[0];
    260         }
    261 
    262         if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
    263         if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
    264 
    265         tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current));
    266     };
    267     mThread.schedule(task, gDefaultDelay.step);
    268 
    269     return Result::OK;
    270 }
    271 
    272 Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
    273     ALOGV("%s(%d, %d)", __func__, channel, subChannel);
    274     Band band;
    275     {
    276         lock_guard<mutex> lk(mMut);
    277         band = mAmfmConfig.type;
    278     }
    279     return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
    280 }
    281 
    282 Return<Result> Tuner::tuneByProgramSelector(const ProgramSelector& sel) {
    283     ALOGV("%s(%s)", __func__, toString(sel).c_str());
    284     lock_guard<mutex> lk(mMut);
    285     if (mIsClosed) return Result::NOT_INITIALIZED;
    286 
    287     // checking if ProgramSelector is valid
    288     auto programType = utils::getType(sel);
    289     if (utils::isAmFm(programType)) {
    290         if (!mIsAmfmConfigSet) {
    291             ALOGW("AM/FM config not set");
    292             return Result::INVALID_STATE;
    293         }
    294 
    295         auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
    296         if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
    297             if (!autoConfigureLocked(freq)) return Result::INVALID_ARGUMENTS;
    298         }
    299     } else if (programType == ProgramType::DAB) {
    300         if (!utils::hasId(sel, IdentifierType::DAB_SIDECC)) return Result::INVALID_ARGUMENTS;
    301     } else if (programType == ProgramType::DRMO) {
    302         if (!utils::hasId(sel, IdentifierType::DRMO_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
    303     } else if (programType == ProgramType::SXM) {
    304         if (!utils::hasId(sel, IdentifierType::SXM_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
    305     } else {
    306         return Result::INVALID_ARGUMENTS;
    307     }
    308 
    309     mIsTuneCompleted = false;
    310     auto task = [this, sel]() {
    311         lock_guard<mutex> lk(mMut);
    312         tuneInternalLocked(sel);
    313     };
    314     mThread.schedule(task, gDefaultDelay.tune);
    315 
    316     return Result::OK;
    317 }
    318 
    319 Return<Result> Tuner::cancel() {
    320     ALOGV("%s", __func__);
    321     lock_guard<mutex> lk(mMut);
    322     if (mIsClosed) return Result::NOT_INITIALIZED;
    323 
    324     mThread.cancelAll();
    325     return Result::OK;
    326 }
    327 
    328 Return<Result> Tuner::cancelAnnouncement() {
    329     ALOGV("%s", __func__);
    330     lock_guard<mutex> lk(mMut);
    331     if (mIsClosed) return Result::NOT_INITIALIZED;
    332 
    333     return Result::OK;
    334 }
    335 
    336 Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
    337     ALOGV("%s", __func__);
    338     return getProgramInformation_1_1(
    339         [&](Result result, const ProgramInfo& info) { _hidl_cb(result, info.base); });
    340 }
    341 
    342 Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
    343     ALOGV("%s", __func__);
    344     lock_guard<mutex> lk(mMut);
    345 
    346     if (mIsClosed) {
    347         _hidl_cb(Result::NOT_INITIALIZED, {});
    348     } else if (mIsTuneCompleted) {
    349         _hidl_cb(Result::OK, mCurrentProgramInfo);
    350     } else {
    351         _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
    352     }
    353     return {};
    354 }
    355 
    356 Return<ProgramListResult> Tuner::startBackgroundScan() {
    357     ALOGV("%s", __func__);
    358     lock_guard<mutex> lk(mMut);
    359     if (mIsClosed) return ProgramListResult::NOT_INITIALIZED;
    360 
    361     if (mCallback1_1 != nullptr) {
    362         mCallback1_1->backgroundScanComplete(ProgramListResult::OK);
    363     }
    364 
    365     return ProgramListResult::OK;
    366 }
    367 
    368 Return<void> Tuner::getProgramList(const hidl_vec<VendorKeyValue>& vendorFilter,
    369                                    getProgramList_cb _hidl_cb) {
    370     ALOGV("%s(%s)", __func__, toString(vendorFilter).substr(0, 100).c_str());
    371     lock_guard<mutex> lk(mMut);
    372     if (mIsClosed) {
    373         _hidl_cb(ProgramListResult::NOT_INITIALIZED, {});
    374         return {};
    375     }
    376 
    377     auto list = mVirtualRadio.get().getProgramList();
    378     ALOGD("returning a list of %zu programs", list.size());
    379     _hidl_cb(ProgramListResult::OK, getProgramInfoVector(list, getHalRev()));
    380     return {};
    381 }
    382 
    383 Return<Result> Tuner::setAnalogForced(bool isForced) {
    384     ALOGV("%s", __func__);
    385     lock_guard<mutex> lk(mMut);
    386     if (mIsClosed) return Result::NOT_INITIALIZED;
    387 
    388     mIsAnalogForced = isForced;
    389     return Result::OK;
    390 }
    391 
    392 Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
    393     ALOGV("%s", __func__);
    394     lock_guard<mutex> lk(mMut);
    395 
    396     if (mIsClosed) {
    397         _hidl_cb(Result::NOT_INITIALIZED, false);
    398     } else {
    399         _hidl_cb(Result::OK, mIsAnalogForced);
    400     }
    401     return {};
    402 }
    403 
    404 }  // namespace implementation
    405 }  // namespace V1_1
    406 }  // namespace broadcastradio
    407 }  // namespace hardware
    408 }  // namespace android
    409