blob: 244544a00319188bef7b47a8acf6662e73c648f9 [file] [log] [blame]
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -08001/*
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 "BcRadioDef.tuner"
18#define LOG_NDEBUG 0
19
20#include "TunerSession.h"
21
22#include "BroadcastRadio.h"
23
24#include <broadcastradio-utils-2x/Utils.h>
25#include <log/log.h>
26
27namespace android {
28namespace hardware {
29namespace broadcastradio {
30namespace V2_0 {
31namespace implementation {
32
33using namespace std::chrono_literals;
34
35using utils::tunesTo;
36
37using std::lock_guard;
38using std::move;
39using std::mutex;
40using std::sort;
41using std::vector;
42
43namespace delay {
44
45static constexpr auto scan = 200ms;
46static constexpr auto step = 100ms;
47static constexpr auto tune = 150ms;
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -080048static constexpr auto list = 1s;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080049
50} // namespace delay
51
52TunerSession::TunerSession(BroadcastRadio& module, const sp<ITunerCallback>& callback)
53 : mCallback(callback), mModule(module) {}
54
55// makes ProgramInfo that points to no program
56static ProgramInfo makeDummyProgramInfo(const ProgramSelector& selector) {
57 ProgramInfo info = {};
58 info.selector = selector;
Tomasz Wasilczyk4ce63822017-12-21 14:25:54 -080059 info.logicallyTunedTo = utils::make_identifier(
60 IdentifierType::AMFM_FREQUENCY, utils::getId(selector, IdentifierType::AMFM_FREQUENCY));
61 info.physicallyTunedTo = info.logicallyTunedTo;
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -080062 return info;
63}
64
65void TunerSession::tuneInternalLocked(const ProgramSelector& sel) {
66 VirtualProgram virtualProgram;
67 ProgramInfo programInfo;
68 if (virtualRadio().getProgram(sel, virtualProgram)) {
69 mCurrentProgram = virtualProgram.selector;
70 programInfo = virtualProgram;
71 } else {
72 mCurrentProgram = sel;
73 programInfo = makeDummyProgramInfo(sel);
74 }
75 mIsTuneCompleted = true;
76
77 mCallback->onCurrentProgramInfoChanged(programInfo);
78}
79
80const VirtualRadio& TunerSession::virtualRadio() const {
81 return mModule.get().mVirtualRadio;
82}
83
84Return<Result> TunerSession::tune(const ProgramSelector& sel) {
85 ALOGV("%s(%s)", __func__, toString(sel).c_str());
86 lock_guard<mutex> lk(mMut);
87 if (mIsClosed) return Result::INVALID_STATE;
88
89 if (!utils::isSupported(mModule.get().mProperties, sel)) {
90 ALOGW("Selector not supported");
91 return Result::NOT_SUPPORTED;
92 }
93
94 if (!utils::isValid(sel)) {
95 ALOGE("ProgramSelector is not valid");
96 return Result::INVALID_ARGUMENTS;
97 }
98
99 mIsTuneCompleted = false;
100 auto task = [this, sel]() {
101 lock_guard<mutex> lk(mMut);
102 tuneInternalLocked(sel);
103 };
104 mThread.schedule(task, delay::tune);
105
106 return Result::OK;
107}
108
109Return<Result> TunerSession::scan(bool directionUp, bool /* skipSubChannel */) {
110 ALOGV("%s", __func__);
111 lock_guard<mutex> lk(mMut);
112 if (mIsClosed) return Result::INVALID_STATE;
113
114 auto list = virtualRadio().getProgramList();
115
116 if (list.empty()) {
117 mIsTuneCompleted = false;
118 auto task = [this, directionUp]() {
119 ALOGI("Performing failed scan up=%d", directionUp);
120
121 mCallback->onTuneFailed(Result::TIMEOUT, {});
122 };
123 mThread.schedule(task, delay::scan);
124
125 return Result::OK;
126 }
127
128 // Not optimal (O(sort) instead of O(n)), but not a big deal here;
129 // also, it's likely that list is already sorted (so O(n) anyway).
130 sort(list.begin(), list.end());
131 auto current = mCurrentProgram;
132 auto found = lower_bound(list.begin(), list.end(), VirtualProgram({current}));
133 if (directionUp) {
134 if (found < list.end() - 1) {
135 if (tunesTo(current, found->selector)) found++;
136 } else {
137 found = list.begin();
138 }
139 } else {
140 if (found > list.begin() && found != list.end()) {
141 found--;
142 } else {
143 found = list.end() - 1;
144 }
145 }
146 auto tuneTo = found->selector;
147
148 mIsTuneCompleted = false;
149 auto task = [this, tuneTo, directionUp]() {
150 ALOGI("Performing scan up=%d", directionUp);
151
152 lock_guard<mutex> lk(mMut);
153 tuneInternalLocked(tuneTo);
154 };
155 mThread.schedule(task, delay::scan);
156
157 return Result::OK;
158}
159
160Return<Result> TunerSession::step(bool directionUp) {
161 ALOGV("%s", __func__);
162 lock_guard<mutex> lk(mMut);
163 if (mIsClosed) return Result::INVALID_STATE;
164
165 if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY)) {
166 ALOGE("Can't step in anything else than AM/FM");
167 return Result::NOT_SUPPORTED;
168 }
169
170 mIsTuneCompleted = false;
171
172 auto stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY);
173#if 0
174 // TODO(b/69958423): handle regions
175 if (directionUp) {
176 stepTo += mAmfmConfig.spacings[0];
177 } else {
178 stepTo -= mAmfmConfig.spacings[0];
179 }
180
181 if (stepTo > mAmfmConfig.upperLimit) stepTo = mAmfmConfig.lowerLimit;
182 if (stepTo < mAmfmConfig.lowerLimit) stepTo = mAmfmConfig.upperLimit;
183#else
184 if (directionUp) {
185 stepTo += 100;
186 } else {
187 stepTo -= 100;
188 }
189#endif
190
191 auto task = [this, stepTo]() {
Tomasz Wasilczyk28ca7e92017-12-13 10:09:22 -0800192 ALOGI("Performing step to %s", std::to_string(stepTo).c_str());
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800193
194 lock_guard<mutex> lk(mMut);
195
196 tuneInternalLocked(utils::make_selector_amfm(stepTo));
197 };
198 mThread.schedule(task, delay::step);
199
200 return Result::OK;
201}
202
203Return<void> TunerSession::cancel() {
204 ALOGV("%s", __func__);
205 lock_guard<mutex> lk(mMut);
206 if (mIsClosed) return {};
207
208 mThread.cancelAll();
209 return {};
210}
211
Tomasz Wasilczykbceb8852017-12-18 13:59:29 -0800212Return<Result> TunerSession::startProgramListUpdates(const ProgramFilter& filter) {
213 ALOGV("%s(%s)", __func__, toString(filter).c_str());
214 lock_guard<mutex> lk(mMut);
215 if (mIsClosed) return Result::INVALID_STATE;
216
217 auto list = virtualRadio().getProgramList();
218 vector<VirtualProgram> filteredList;
219 auto filterCb = [&filter](const VirtualProgram& program) {
220 return utils::satisfies(filter, program.selector);
221 };
222 std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
223
224 auto task = [this, list]() {
225 lock_guard<mutex> lk(mMut);
226
227 ProgramListChunk chunk = {};
228 chunk.purge = true;
229 chunk.complete = true;
230 chunk.modified = hidl_vec<ProgramInfo>(list.begin(), list.end());
231
232 mCallback->onProgramListUpdated(chunk);
233 };
234 mThread.schedule(task, delay::list);
235
236 return Result::OK;
237}
238
239Return<void> TunerSession::stopProgramListUpdates() {
240 ALOGV("%s", __func__);
241 return {};
242}
243
Tomasz Wasilczyk43fe8942017-12-14 11:44:12 -0800244Return<void> TunerSession::getConfigFlag(ConfigFlag flag, getConfigFlag_cb _hidl_cb) {
245 ALOGV("%s(%s)", __func__, toString(flag).c_str());
246
247 _hidl_cb(Result::NOT_SUPPORTED, false);
248 return {};
249}
250
251Return<Result> TunerSession::setConfigFlag(ConfigFlag flag, bool value) {
252 ALOGV("%s(%s, %d)", __func__, toString(flag).c_str(), value);
253
254 return Result::NOT_SUPPORTED;
255}
256
Tomasz Wasilczyk06100b32017-12-04 09:53:32 -0800257Return<void> TunerSession::setParameters(const hidl_vec<VendorKeyValue>& /* parameters */,
258 setParameters_cb _hidl_cb) {
259 ALOGV("%s", __func__);
260
261 _hidl_cb({});
262 return {};
263}
264
265Return<void> TunerSession::getParameters(const hidl_vec<hidl_string>& /* keys */,
266 getParameters_cb _hidl_cb) {
267 ALOGV("%s", __func__);
268
269 _hidl_cb({});
270 return {};
271}
272
273Return<void> TunerSession::close() {
274 ALOGV("%s", __func__);
275 lock_guard<mutex> lk(mMut);
276 if (mIsClosed) return {};
277
278 mIsClosed = true;
279 mThread.cancelAll();
280 return {};
281}
282
283} // namespace implementation
284} // namespace V2_0
285} // namespace broadcastradio
286} // namespace hardware
287} // namespace android