2 * Copyright (C) 2017 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "BroadcastRadioDefault.tuner"
20 #include "BroadcastRadio.h"
23 #include <broadcastradio-utils/Utils.h>
28 namespace broadcastradio {
30 namespace implementation {
32 using namespace std::chrono_literals;
35 using V1_0::BandConfig;
37 using V1_0::Direction;
38 using utils::HalRevision;
40 using std::chrono::milliseconds;
41 using std::lock_guard;
48 milliseconds config = 50ms;
49 milliseconds scan = 200ms;
50 milliseconds step = 100ms;
51 milliseconds tune = 150ms;
54 Tuner::Tuner(V1_0::Class classId, const sp<V1_0::ITunerCallback>& callback)
57 mCallback1_1(ITunerCallback::castFrom(callback).withDefault(nullptr)),
58 mVirtualRadio(getRadio(classId)),
59 mIsAnalogForced(false) {}
61 void Tuner::forceClose() {
62 lock_guard<mutex> lk(mMut);
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;
76 if (config.lowerLimit >= config.upperLimit) return Result::INVALID_ARGUMENTS;
78 auto task = [this, config]() {
79 ALOGI("Setting AM/FM config");
80 lock_guard<mutex> lk(mMut);
82 mAmfmConfig = move(config);
83 mAmfmConfig.antennaConnected = true;
84 mCurrentProgram = utils::make_selector(mAmfmConfig.type, mAmfmConfig.lowerLimit);
86 if (mAmfmConfig.type == Band::FM_HD || mAmfmConfig.type == Band::FM) {
87 mVirtualRadio = std::ref(getFmRadio());
89 mVirtualRadio = std::ref(getAmRadio());
92 mIsAmfmConfigSet = true;
93 mCallback->configChange(Result::OK, mAmfmConfig);
95 mThread.schedule(task, gDefaultDelay.config);
100 Return<void> Tuner::getConfiguration(getConfiguration_cb _hidl_cb) {
101 ALOGV("%s", __func__);
102 lock_guard<mutex> lk(mMut);
104 if (!mIsClosed && mIsAmfmConfigSet) {
105 _hidl_cb(Result::OK, mAmfmConfig);
107 _hidl_cb(Result::NOT_INITIALIZED, {});
112 // makes ProgramInfo that points to no program
113 static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
114 ProgramInfo info11 = {};
115 auto& info10 = info11.base;
117 utils::getLegacyChannel(selector, &info10.channel, &info10.subChannel);
118 info11.selector = selector;
119 info11.flags |= ProgramInfoFlags::MUTED;
124 HalRevision Tuner::getHalRev() const {
125 if (mCallback1_1 != nullptr) {
126 return HalRevision::V1_1;
128 return HalRevision::V1_0;
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());
138 mCurrentProgram = sel;
139 mCurrentProgramInfo = makeDummyProgramInfo(sel);
141 mIsTuneCompleted = true;
143 if (mCallback1_1 == nullptr) {
144 mCallback->tuneComplete(Result::OK, mCurrentProgramInfo.base);
146 mCallback1_1->tuneComplete_1_1(Result::OK, mCurrentProgramInfo.selector);
147 mCallback1_1->currentProgramInfoChanged(mCurrentProgramInfo);
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;
156 auto list = mVirtualRadio.get().getProgramList();
159 mIsTuneCompleted = false;
160 auto task = [this, direction]() {
161 ALOGI("Performing failed scan %s", toString(direction).c_str());
163 if (mCallback1_1 == nullptr) {
164 mCallback->tuneComplete(Result::TIMEOUT, {});
166 mCallback1_1->tuneComplete_1_1(Result::TIMEOUT, {});
169 mThread.schedule(task, gDefaultDelay.scan);
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++;
183 found = list.begin();
186 if (found > list.begin() && found != list.end()) {
189 found = list.end() - 1;
192 auto tuneTo = found->selector;
194 mIsTuneCompleted = false;
195 auto task = [this, tuneTo, direction]() {
196 ALOGI("Performing scan %s", toString(direction).c_str());
198 lock_guard<mutex> lk(mMut);
199 tuneInternalLocked(tuneTo);
201 mThread.schedule(task, gDefaultDelay.scan);
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;
211 ALOGW_IF(!skipSubChannel, "can't step to next frequency without ignoring subChannel");
213 if (!utils::isAmFm(utils::getType(mCurrentProgram))) {
214 ALOGE("Can't step in anything else than AM/FM");
215 return Result::NOT_INITIALIZED;
218 if (!mIsAmfmConfigSet) {
219 ALOGW("AM/FM config not set");
220 return Result::INVALID_STATE;
222 mIsTuneCompleted = false;
224 auto task = [this, direction]() {
225 ALOGI("Performing step %s", toString(direction).c_str());
227 lock_guard<mutex> lk(mMut);
229 auto current = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY, 0);
231 if (direction == Direction::UP) {
232 current += mAmfmConfig.spacings[0];
234 current -= mAmfmConfig.spacings[0];
237 if (current > mAmfmConfig.upperLimit) current = mAmfmConfig.lowerLimit;
238 if (current < mAmfmConfig.lowerLimit) current = mAmfmConfig.upperLimit;
240 tuneInternalLocked(utils::make_selector(mAmfmConfig.type, current));
242 mThread.schedule(task, gDefaultDelay.step);
247 Return<Result> Tuner::tune(uint32_t channel, uint32_t subChannel) {
248 ALOGV("%s(%d, %d)", __func__, channel, subChannel);
251 lock_guard<mutex> lk(mMut);
252 band = mAmfmConfig.type;
254 return tuneByProgramSelector(utils::make_selector(band, channel, subChannel));
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;
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;
270 auto freq = utils::getId(sel, IdentifierType::AMFM_FREQUENCY);
271 if (freq < mAmfmConfig.lowerLimit || freq > mAmfmConfig.upperLimit) {
272 return Result::INVALID_ARGUMENTS;
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;
281 return Result::INVALID_ARGUMENTS;
284 mIsTuneCompleted = false;
285 auto task = [this, sel]() {
286 lock_guard<mutex> lk(mMut);
287 tuneInternalLocked(sel);
289 mThread.schedule(task, gDefaultDelay.tune);
294 Return<Result> Tuner::cancel() {
295 ALOGV("%s", __func__);
296 lock_guard<mutex> lk(mMut);
297 if (mIsClosed) return Result::NOT_INITIALIZED;
303 Return<Result> Tuner::cancelAnnouncement() {
304 ALOGV("%s", __func__);
305 lock_guard<mutex> lk(mMut);
306 if (mIsClosed) return Result::NOT_INITIALIZED;
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);
318 Return<void> Tuner::getProgramInformation_1_1(getProgramInformation_1_1_cb _hidl_cb) {
319 ALOGV("%s", __func__);
320 lock_guard<mutex> lk(mMut);
323 _hidl_cb(Result::NOT_INITIALIZED, {});
324 } else if (mIsTuneCompleted) {
325 _hidl_cb(Result::OK, mCurrentProgramInfo);
327 _hidl_cb(Result::NOT_INITIALIZED, makeDummyProgramInfo(mCurrentProgram));
332 Return<ProgramListResult> Tuner::startBackgroundScan() {
333 ALOGV("%s", __func__);
334 lock_guard<mutex> lk(mMut);
335 if (mIsClosed) return ProgramListResult::NOT_INITIALIZED;
337 return ProgramListResult::UNAVAILABLE;
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);
345 _hidl_cb(ProgramListResult::NOT_INITIALIZED, {});
349 auto list = mVirtualRadio.get().getProgramList();
350 ALOGD("returning a list of %zu programs", list.size());
351 _hidl_cb(ProgramListResult::OK, getProgramInfoVector(list, getHalRev()));
355 Return<Result> Tuner::setAnalogForced(bool isForced) {
356 ALOGV("%s", __func__);
357 lock_guard<mutex> lk(mMut);
358 if (mIsClosed) return Result::NOT_INITIALIZED;
360 mIsAnalogForced = isForced;
364 Return<void> Tuner::isAnalogForced(isAnalogForced_cb _hidl_cb) {
365 ALOGV("%s", __func__);
366 lock_guard<mutex> lk(mMut);
369 _hidl_cb(Result::NOT_INITIALIZED, false);
371 _hidl_cb(Result::OK, mIsAnalogForced);
376 } // namespace implementation
378 } // namespace broadcastradio
379 } // namespace hardware
380 } // namespace android