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