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 "BroadcastRadio.h"
     21 #include "Tuner.h"
     22 
     23 #include <broadcastradio-utils/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 utils::HalRevision;
     39 
     40 using std::chrono::milliseconds;
     41 using std::lock_guard;
     42 using std::move;
     43 using std::mutex;
     44 using std::sort;
     45 using std::vector;
     46 
     47 const struct {
     48     milliseconds config = 50ms;
     49     milliseconds scan = 200ms;
     50     milliseconds step = 100ms;
     51     milliseconds tune = 150ms;
     52 } gDefaultDelay;
     53 
     54 Tuner::Tuner(V1_0::Class classId, const sp<V1_0::ITunerCallback>& callback)
     55     : mClassId(classId),
     56       mCallback(callback),
     57       mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)),
     58       mVirtualRadio(getRadio(classId)),
     59       mIsAnalogForced(false) {}
     60 
     61 void Tuner::forceClose() {
     62     lock_guard<mutex> lk(mMut);
     63     mIsClosed = true;
     64     mThread.cancelAll();
     65 }
     66 
     67 Return<Result> Tuner::setConfiguration(const BandConfig& config) {
     68     ALOGV("%s", __func__);
     69     lock_guard<mutex> lk(mMut);
     70     if (mIsClosed) return Result::NOT_INITIALIZED;
     71     if (mClassId != Class::AM_FM) {
     72         ALOGE("Can't set AM/FM configuration on SAT/DT radio tuner");
     73         return Result::INVALID_STATE;
     74     }
     75 
     76     if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
     77 
     78     auto task = [this, config]() {
     79         ALOGI("Setting AM/FM config");
     80         lock_guard<mutex> lk(mMut);
     81 
     82         mAmfmConfig = move(config);
     83         mAmfmConfig.antennaConnected = true;
     84         mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
     85 
     86         if (utils::isFm(mAmfmConfig.type)) {
     87             mVirtualRadio = std::ref(getFmRadio());
     88         } else {
     89             mVirtualRadio = std::ref(getAmRadio());
     90         }
     91 
     92         mIsAmfmConfigSet = true;
     93         mCallback->configChange(Result::OK, mAmfmConfig);
     94     };
     95     mThread.schedule(task, gDefaultDelay.config);
     96 
     97     return Result::OK;
     98 }
     99 
    100 Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
    101     ALOGV("%s", __func__);
    102     lock_guard<mutex> lk(mMut);
    103 
    104     if (!mIsClosed && mIsAmfmConfigSet) {
    105         _hidl_cb(Result::OK, mAmfmConfig);
    106     } else {
    107         _hidl_cb(Result::NOT_INITIALIZED, {});
    108     }
    109     return {};
    110 }
    111 
    112 // makes ProgramInfo that points to no program
    113 static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
    114     ProgramInfo info11 = {};
    115     auto& info10 = info11.base;
    116 
    117     utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel);
    118     info11.selector = selector;
    119     info11.flags |= ProgramInfoFlags::MUTED;
    120 
    121     return info11;
    122 }
    123 
    124 HalRevision Tuner::getHalRev() const {
    125     if (mCallback1_1 != nullptr) {
    126         return HalRevision::V1_1;
    127     } else {
    128         return HalRevision::V1_0;
    129     }
    130 }
    131 
    132 void Tuner::tuneInternalLocked(const ProgramSelector& sel) {
    133     VirtualProgram virtualProgram;
    134     if (mVirtualRadio.get().getProgram(sel, virtualProgram)) {
    135         mCurrentProgram = virtualProgram.selector;
    136         mCurrentProgramInfo = virtualProgram.getProgramInfo(getHalRev());
    137     } else {
    138         mCurrentProgram = sel;
    139         mCurrentProgramInfo = makeDummyProgramInfo(sel);
    140     }
    141     mIsTuneCompleted = true;
    142 
    143     if (mCallback1_1 == nullptr) {
    144         mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base);
    145     } else {
    146         mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo.selector);
    147         mCallback1_1->currentProgramInfoChanged(mCurrentProgramInfo);
    148     }
    149 }
    150 
    151 Return<Result> Tuner::scan(Direction direction, bool skipSubChannel __unused) {
    152     ALOGV("%s", __func__);
    153     lock_guard<mutex> lk(mMut);
    154     if (mIsClosed) return Result::NOT_INITIALIZED;
    155 
    156     auto list = mVirtualRadio.get().getProgramList();
    157 
    158     if (list.empty()) {
    159         mIsTuneCompleted = false;
    160         auto task = [this, direction]() {
    161             ALOGI("Performing failed scan %s", toString(direction).c_str());
    162 
    163             if (mCallback1_1 == nullptr) {
    164                 mCallback->tuneComplete(Result::TIMEOUT, {});
    165             } else {
    166                 mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
    167             }
    168         };
    169         mThread.schedule(task, gDefaultDelay.scan);
    170 
    171         return Result::OK;
    172     }
    173 
    174     // Not optimal (O(sort) instead of O(n)), but not a big deal here;
    175     // also, it's likely that list is already sorted (so O(n) anyway).
    176     sort(list.begin(), list.end());
    177     auto current = mCurrentProgram;
    178     auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
    179     if (direction == Direction::UP) {
    180         if (found < list.end() - 1) {
    181             if (utils::tunesTo(current, found->selector)) found++;
    182         } else {
    183             found = list.begin();
    184         }
    185     } else {
    186         if (found > list.begin() && found != list.end()) {
    187             found--;
    188         } else {
    189             found = list.end() - 1;
    190         }
    191     }
    192     auto tuneTo = found->selector;
    193 
    194     mIsTuneCompleted = false;
    195     auto task = [this, tuneTo, direction]() {
    196         ALOGI("Performing scan %s", toString(direction).c_str());
    197 
    198         lock_guard<mutex> lk(mMut);
    199         tuneInternalLocked(tuneTo);
    200     };
    201     mThread.schedule(task, gDefaultDelay.scan);
    202 
    203     return Result::OK;
    204 }
    205 
    206 Return<Result> Tuner::step(Direction direction, bool skipSubChannel) {
    207     ALOGV("%s", __func__);
    208     lock_guard<mutex> lk(mMut);
    209     if (mIsClosed) return Result::NOT_INITIALIZED;
    210 
    211     ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
    212 
    213     if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
    214         ALOGE("Can't step in anything else than AM/FM");
    215         return Result::NOT_INITIALIZED;
    216     }
    217 
    218     if (!mIsAmfmConfigSet) {
    219         ALOGW("AM/FM config not set");
    220         return Result::INVALID_STATE;
    221     }
    222     mIsTuneCompleted = false;
    223 
    224     auto task = [this, direction]() {
    225         ALOGI("Performing step %s", toString(direction).c_str());
    226 
    227         lock_guard<mutex> lk(mMut);
    228 
    229         auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
    230 
    231         if (direction == Direction::UP) {
    232             current += mAmfmConfig.spacings[0];
    233         } else {
    234             current -= mAmfmConfig.spacings[0];
    235         }
    236 
    237         if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
    238         if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
    239 
    240         tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current));
    241     };
    242     mThread.schedule(task, gDefaultDelay.step);
    243 
    244     return Result::OK;
    245 }
    246 
    247 Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
    248     ALOGV("%s(%d, %d)", __func__, channel, subChannel);
    249     Band band;
    250     {
    251         lock_guard<mutex> lk(mMut);
    252         band = mAmfmConfig.type;
    253     }
    254     return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
    255 }
    256 
    257 Return<Result> Tuner::tuneByProgramSelector(const ProgramSelector& sel) {
    258     ALOGV("%s(%s)", __func__, toString(sel).c_str());
    259     lock_guard<mutex> lk(mMut);
    260     if (mIsClosed) return Result::NOT_INITIALIZED;
    261 
    262     // checking if ProgramSelector is valid
    263     auto programType = utils::getType(sel);
    264     if (utils::isAmFm(programType)) {
    265         if (!mIsAmfmConfigSet) {
    266             ALOGW("AM/FM config not set");
    267             return Result::INVALID_STATE;
    268         }
    269 
    270         auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
    271         if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
    272             return Result::INVALID_ARGUMENTS;
    273         }
    274     } else if (programType == ProgramType::DAB) {
    275         if (!utils::hasId(sel, IdentifierType::DAB_SIDECC)) return Result::INVALID_ARGUMENTS;
    276     } else if (programType == ProgramType::DRMO) {
    277         if (!utils::hasId(sel, IdentifierType::DRMO_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
    278     } else if (programType == ProgramType::SXM) {
    279         if (!utils::hasId(sel, IdentifierType::SXM_SERVICE_ID)) return Result::INVALID_ARGUMENTS;
    280     } else {
    281         return Result::INVALID_ARGUMENTS;
    282     }
    283 
    284     mIsTuneCompleted = false;
    285     auto task = [this, sel]() {
    286         lock_guard<mutex> lk(mMut);
    287         tuneInternalLocked(sel);
    288     };
    289     mThread.schedule(task, gDefaultDelay.tune);
    290 
    291     return Result::OK;
    292 }
    293 
    294 Return<Result> Tuner::cancel() {
    295     ALOGV("%s", __func__);
    296     lock_guard<mutex> lk(mMut);
    297     if (mIsClosed) return Result::NOT_INITIALIZED;
    298 
    299     mThread.cancelAll();
    300     return Result::OK;
    301 }
    302 
    303 Return<Result> Tuner::cancelAnnouncement() {
    304     ALOGV("%s", __func__);
    305     lock_guard<mutex> lk(mMut);
    306     if (mIsClosed) return Result::NOT_INITIALIZED;
    307 
    308     return Result::OK;
    309 }
    310 
    311 Return<void> Tuner::getProgramInformation(getProgramInformation_cb _hidl_cb) {
    312     ALOGV("%s", __func__);
    313     return getProgramInformation_1_1([&](Result result, const ProgramInfo& info) {
    314         _hidl_cb(result, info.base);
    315     });
    316 }
    317 
    318 Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
    319     ALOGV("%s", __func__);
    320     lock_guard<mutex> lk(mMut);
    321 
    322     if (mIsClosed) {
    323         _hidl_cb(Result::NOT_INITIALIZED, {});
    324     } else if (mIsTuneCompleted) {
    325         _hidl_cb(Result::OK, mCurrentProgramInfo);
    326     } else {
    327         _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
    328     }
    329     return {};
    330 }
    331 
    332 Return<ProgramListResult> Tuner::startBackgroundScan() {
    333     ALOGV("%s", __func__);
    334     lock_guard<mutex> lk(mMut);
    335     if (mIsClosed) return ProgramListResult::NOT_INITIALIZED;
    336 
    337     return ProgramListResult::UNAVAILABLE;
    338 }
    339 
    340 Return<void> Tuner::getProgramList(const hidl_vec<VendorKeyValue>& vendorFilter,
    341                                    getProgramList_cb _hidl_cb) {
    342     ALOGV("%s(%s)", __func__, toString(vendorFilter).substr(0, 100).c_str());
    343     lock_guard<mutex> lk(mMut);
    344     if (mIsClosed) {
    345         _hidl_cb(ProgramListResult::NOT_INITIALIZED, {});
    346         return {};
    347     }
    348 
    349     auto list = mVirtualRadio.get().getProgramList();
    350     ALOGD("returning a list of %zu programs", list.size());
    351     _hidl_cb(ProgramListResult::OK, getProgramInfoVector(list, getHalRev()));
    352     return {};
    353 }
    354 
    355 Return<Result> Tuner::setAnalogForced(bool isForced) {
    356     ALOGV("%s", __func__);
    357     lock_guard<mutex> lk(mMut);
    358     if (mIsClosed) return Result::NOT_INITIALIZED;
    359 
    360     mIsAnalogForced = isForced;
    361     return Result::OK;
    362 }
    363 
    364 Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
    365     ALOGV("%s", __func__);
    366     lock_guard<mutex> lk(mMut);
    367 
    368     if (mIsClosed) {
    369         _hidl_cb(Result::NOT_INITIALIZED, false);
    370     } else {
    371         _hidl_cb(Result::OK, mIsAnalogForced);
    372     }
    373     return {};
    374 }
    375 
    376 }  // namespace implementation
    377 }  // namespace V1_1
    378 }  // namespace broadcastradio
    379 }  // namespace hardware
    380 }  // namespace android
    381