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