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 #include "TunerSession.h"
     18 
     19 #include "BroadcastRadio.h"
     20 
     21 #include <android-base/logging.h>
     22 #include <broadcastradio-utils-2x/Utils.h>
     23 
     24 namespace android {
     25 namespace hardware {
     26 namespace broadcastradio {
     27 namespace V2_0 {
     28 namespace implementation {
     29 
     30 using namespace std::chrono_literals;
     31 
     32 using utils::tunesTo;
     33 
     34 using std::lock_guard;
     35 using std::move;
     36 using std::mutex;
     37 using std::sort;
     38 using std::vector;
     39 
     40 namespace delay {
     41 
     42 static constexpr auto seek = 200ms;
     43 static constexpr auto step = 100ms;
     44 static constexpr auto tune = 150ms;
     45 static constexpr auto list = 1s;
     46 
     47 }  // namespace delay
     48 
     49 TunerSession::TunerSession(BroadcastRadio& module, const sp<ITunerCallback>& callback)
     50     : mCallback(callback), mModule(module) {
     51     auto&& ranges = module.getAmFmConfig().ranges;
     52     if (ranges.size() > 0) {
     53         tuneInternalLocked(utils::make_selector_amfm(ranges[0].lowerBound));
     54     }
     55 }
     56 
     57 // makes ProgramInfo that points to no program
     58 static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
     59     ProgramInfo info = {};
     60     info.selector = selector;
     61     info.logicallyTunedTo = utils::make_identifier(
     62         IdentifierType::AMFM_FREQUENCY, utils::getId(selector, IdentifierType::AMFM_FREQUENCY));
     63     info.physicallyTunedTo = info.logicallyTunedTo;
     64     return info;
     65 }
     66 
     67 void TunerSession::tuneInternalLocked(const ProgramSelector& sel) {
     68     LOG(VERBOSE) << "tune (internal) to " << toString(sel);
     69 
     70     VirtualProgram virtualProgram;
     71     ProgramInfo programInfo;
     72     if (virtualRadio().getProgram(sel, virtualProgram)) {
     73         mCurrentProgram = virtualProgram.selector;
     74         programInfo = virtualProgram;
     75     } else {
     76         mCurrentProgram = sel;
     77         programInfo = makeDummyProgramInfo(sel);
     78     }
     79     mIsTuneCompleted = true;
     80 
     81     mCallback->onCurrentProgramInfoChanged(programInfo);
     82 }
     83 
     84 const BroadcastRadio& TunerSession::module() const {
     85     return mModule.get();
     86 }
     87 
     88 const VirtualRadio& TunerSession::virtualRadio() const {
     89     return module().mVirtualRadio;
     90 }
     91 
     92 Return<Result> TunerSession::tune(const ProgramSelector& sel) {
     93     LOG(DEBUG) << "tune to " << toString(sel);
     94 
     95     lock_guard<mutex> lk(mMut);
     96     if (mIsClosed) return Result::INVALID_STATE;
     97 
     98     if (!utils::isSupported(module().mProperties, sel)) {
     99         LOG(WARNING) << "selector not supported: " << toString(sel);
    100         return Result::NOT_SUPPORTED;
    101     }
    102 
    103     if (!utils::isValid(sel)) {
    104         LOG(ERROR) << "selector is not valid: " << toString(sel);
    105         return Result::INVALID_ARGUMENTS;
    106     }
    107 
    108     cancelLocked();
    109 
    110     mIsTuneCompleted = false;
    111     auto task = [this, sel]() {
    112         lock_guard<mutex> lk(mMut);
    113         tuneInternalLocked(sel);
    114     };
    115     mThread.schedule(task, delay::tune);
    116 
    117     return Result::OK;
    118 }
    119 
    120 Return<Result> TunerSession::scan(bool directionUp, bool skipSubChannel) {
    121     LOG(DEBUG) << "seek up=" << directionUp << " skipSubChannel=" << skipSubChannel;
    122 
    123     lock_guard<mutex> lk(mMut);
    124     if (mIsClosed) return Result::INVALID_STATE;
    125 
    126     cancelLocked();
    127 
    128     auto list = virtualRadio().getProgramList();
    129 
    130     if (list.empty()) {
    131         mIsTuneCompleted = false;
    132         auto task = [this]() {
    133             LOG(DEBUG) << "program list is empty, seek couldn't stop";
    134 
    135             mCallback->onTuneFailed(Result::TIMEOUT, {});
    136         };
    137         mThread.schedule(task, delay::seek);
    138 
    139         return Result::OK;
    140     }
    141 
    142     // Not optimal (O(sort) instead of O(n)), but not a big deal here;
    143     // also, it's likely that list is already sorted (so O(n) anyway).
    144     sort(list.begin(), list.end());
    145     auto current = mCurrentProgram;
    146     auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
    147     if (directionUp) {
    148         if (found < list.end() - 1) {
    149             if (tunesTo(current, found->selector)) found++;
    150         } else {
    151             found = list.begin();
    152         }
    153     } else {
    154         if (found > list.begin() && found != list.end()) {
    155             found--;
    156         } else {
    157             found = list.end() - 1;
    158         }
    159     }
    160     auto tuneTo = found->selector;
    161 
    162     mIsTuneCompleted = false;
    163     auto task = [this, tuneTo, directionUp]() {
    164         LOG(VERBOSE) << "executing seek up=" << directionUp;
    165 
    166         lock_guard<mutex> lk(mMut);
    167         tuneInternalLocked(tuneTo);
    168     };
    169     mThread.schedule(task, delay::seek);
    170 
    171     return Result::OK;
    172 }
    173 
    174 Return<Result> TunerSession::step(bool directionUp) {
    175     LOG(DEBUG) << "step up=" << directionUp;
    176     lock_guard<mutex> lk(mMut);
    177     if (mIsClosed) return Result::INVALID_STATE;
    178 
    179     cancelLocked();
    180 
    181     if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) {
    182         LOG(WARNING) << "can't step in anything else than AM/FM";
    183         return Result::NOT_SUPPORTED;
    184     }
    185 
    186     auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
    187     auto range = getAmFmRangeLocked();
    188     if (!range) {
    189         LOG(ERROR) << "can't find current band";
    190         return Result::INTERNAL_ERROR;
    191     }
    192 
    193     if (directionUp) {
    194         stepTo += range->spacing;
    195     } else {
    196         stepTo -= range->spacing;
    197     }
    198     if (stepTo > range->upperBound) stepTo = range->lowerBound;
    199     if (stepTo < range->lowerBound) stepTo = range->upperBound;
    200 
    201     mIsTuneCompleted = false;
    202     auto task = [this, stepTo]() {
    203         LOG(VERBOSE) << "executing step to " << stepTo;
    204 
    205         lock_guard<mutex> lk(mMut);
    206 
    207         tuneInternalLocked(utils::make_selector_amfm(stepTo));
    208     };
    209     mThread.schedule(task, delay::step);
    210 
    211     return Result::OK;
    212 }
    213 
    214 void TunerSession::cancelLocked() {
    215     LOG(VERBOSE) << "cancelling current operations...";
    216 
    217     mThread.cancelAll();
    218     if (utils::getType(mCurrentProgram.primaryId) != IdentifierType::INVALID) {
    219         mIsTuneCompleted = true;
    220     }
    221 }
    222 
    223 Return<void> TunerSession::cancel() {
    224     lock_guard<mutex> lk(mMut);
    225     if (mIsClosed) return {};
    226 
    227     cancelLocked();
    228 
    229     return {};
    230 }
    231 
    232 Return<Result> TunerSession::startProgramListUpdates(const ProgramFilter& filter) {
    233     LOG(DEBUG) << "requested program list updates, filter=" << toString(filter);
    234     lock_guard<mutex> lk(mMut);
    235     if (mIsClosed) return Result::INVALID_STATE;
    236 
    237     auto list = virtualRadio().getProgramList();
    238     vector<VirtualProgram> filteredList;
    239     auto filterCb = [&filter](const VirtualProgram& program) {
    240         return utils::satisfies(filter, program.selector);
    241     };
    242     std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
    243 
    244     auto task = [this, list]() {
    245         lock_guard<mutex> lk(mMut);
    246 
    247         ProgramListChunk chunk = {};
    248         chunk.purge = true;
    249         chunk.complete = true;
    250         chunk.modified = hidl_vec<ProgramInfo>(list.begin(), list.end());
    251 
    252         mCallback->onProgramListUpdated(chunk);
    253     };
    254     mThread.schedule(task, delay::list);
    255 
    256     return Result::OK;
    257 }
    258 
    259 Return<void> TunerSession::stopProgramListUpdates() {
    260     LOG(DEBUG) << "requested program list updates to stop";
    261     return {};
    262 }
    263 
    264 Return<void> TunerSession::isConfigFlagSet(ConfigFlag flag, isConfigFlagSet_cb _hidl_cb) {
    265     LOG(VERBOSE) << __func__ << " " << toString(flag);
    266 
    267     _hidl_cb(Result::NOT_SUPPORTED, false);
    268     return {};
    269 }
    270 
    271 Return<Result> TunerSession::setConfigFlag(ConfigFlag flag, bool value) {
    272     LOG(VERBOSE) << __func__ << " " << toString(flag) << " " << value;
    273 
    274     return Result::NOT_SUPPORTED;
    275 }
    276 
    277 Return<void> TunerSession::setParameters(const hidl_vec<VendorKeyValue>& /* parameters */,
    278                                          setParameters_cb _hidl_cb) {
    279     _hidl_cb({});
    280     return {};
    281 }
    282 
    283 Return<void> TunerSession::getParameters(const hidl_vec<hidl_string>& /* keys */,
    284                                          getParameters_cb _hidl_cb) {
    285     _hidl_cb({});
    286     return {};
    287 }
    288 
    289 Return<void> TunerSession::close() {
    290     LOG(DEBUG) << "closing session...";
    291     lock_guard<mutex> lk(mMut);
    292     if (mIsClosed) return {};
    293 
    294     mIsClosed = true;
    295     mThread.cancelAll();
    296     return {};
    297 }
    298 
    299 std::optional<AmFmBandRange> TunerSession::getAmFmRangeLocked() const {
    300     if (!mIsTuneCompleted) {
    301         LOG(WARNING) << "tune operation is in process";
    302         return {};
    303     }
    304     if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) return {};
    305 
    306     auto freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
    307     for (auto&& range : module().getAmFmConfig().ranges) {
    308         if (range.lowerBound <= freq && range.upperBound >= freq) return range;
    309     }
    310 
    311     return {};
    312 }
    313 
    314 }  // namespace implementation
    315 }  // namespace V2_0
    316 }  // namespace broadcastradio
    317 }  // namespace hardware
    318 }  // namespace android
    319