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