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