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