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