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