blob: 4d6d81dc1d4582a1d34e215951c0656bfa9683a5 [file] [log] [blame]
Weilin Xub2a6ca62022-05-08 23:47:04 +00001/*
2 * Copyright (C) 2022 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#include "BroadcastRadio.h"
18#include <broadcastradio-utils-aidl/Utils.h>
Weilin Xu31c541c2023-09-07 17:00:57 -070019#include <broadcastradio-utils-aidl/UtilsV2.h>
Weilin Xub2a6ca62022-05-08 23:47:04 +000020#include "resources.h"
21
22#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
23#include <aidl/android/hardware/broadcastradio/Result.h>
24
25#include <android-base/logging.h>
Weilin Xu97203b02022-06-23 00:19:03 +000026#include <android-base/strings.h>
27
28#include <private/android_filesystem_config.h>
Weilin Xub2a6ca62022-05-08 23:47:04 +000029
30namespace aidl::android::hardware::broadcastradio {
31
32using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
33using ::aidl::android::hardware::broadcastradio::utils::tunesTo;
Weilin Xu97203b02022-06-23 00:19:03 +000034using ::android::base::EqualsIgnoreCase;
Weilin Xub2a6ca62022-05-08 23:47:04 +000035using ::ndk::ScopedAStatus;
36using ::std::literals::chrono_literals::operator""ms;
37using ::std::literals::chrono_literals::operator""s;
38using ::std::lock_guard;
39using ::std::mutex;
40using ::std::string;
41using ::std::vector;
42
43namespace {
44
45inline constexpr std::chrono::milliseconds kSeekDelayTimeMs = 200ms;
46inline constexpr std::chrono::milliseconds kStepDelayTimeMs = 100ms;
47inline constexpr std::chrono::milliseconds kTuneDelayTimeMs = 150ms;
48inline constexpr std::chrono::seconds kListDelayTimeS = 1s;
49
50// clang-format off
Weilin Xu48338962023-11-03 14:31:15 -070051const AmFmBandRange kFmFullBandRange = {65000, 108000, 10, 0};
52const AmFmBandRange kAmFullBandRange = {150, 30000, 1, 0};
Weilin Xub2a6ca62022-05-08 23:47:04 +000053const AmFmRegionConfig kDefaultAmFmConfig = {
54 {
55 {87500, 108000, 100, 100}, // FM
56 {153, 282, 3, 9}, // AM LW
57 {531, 1620, 9, 9}, // AM MW
58 {1600, 30000, 1, 5}, // AM SW
59 },
60 AmFmRegionConfig::DEEMPHASIS_D50,
61 AmFmRegionConfig::RDS};
62// clang-format on
63
64Properties initProperties(const VirtualRadio& virtualRadio) {
65 Properties prop = {};
66
67 prop.maker = "Android";
68 prop.product = virtualRadio.getName();
Weilin Xu90e39f52023-11-07 20:07:23 -080069 prop.supportedIdentifierTypes = virtualRadio.getSupportedIdentifierTypes();
Weilin Xub2a6ca62022-05-08 23:47:04 +000070 prop.vendorInfo = vector<VendorKeyValue>({
71 {"com.android.sample", "sample"},
72 });
73
74 return prop;
75}
76
Weilin Xu48338962023-11-03 14:31:15 -070077bool isDigitalProgramAllowed(const ProgramSelector& sel, bool forceAnalogFm, bool forceAnalogAm) {
78 if (sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
79 return true;
80 }
81 int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
82 bool isFm = freq >= kFmFullBandRange.lowerBound && freq <= kFmFullBandRange.upperBound;
83 return isFm ? !forceAnalogFm : !forceAnalogAm;
84}
85
86/**
87 * Checks whether a program selector is in the current band.
88 *
89 * <p>For an AM/FM program, this method checks whether it is in the current AM/FM band. For a
90 * program selector is also an HD program, it is also checked whether HD radio is enabled in the
91 * current AM/FM band. For a non-AM/FM program, the method will returns {@code true} directly.
92 * @param sel Program selector to be checked
93 * @param currentAmFmBandRange the current AM/FM band
94 * @param forceAnalogFm whether FM band is forced to be analog
95 * @param forceAnalogAm whether AM band is forced to be analog
96 * @return whether the program selector is in the current band if it is an AM/FM (including HD)
97 * selector, {@code true} otherwise
98 */
99bool isProgramInBand(const ProgramSelector& sel,
100 const std::optional<AmFmBandRange>& currentAmFmBandRange, bool forceAnalogFm,
101 bool forceAnalogAm) {
102 if (!utils::hasAmFmFrequency(sel)) {
103 return true;
104 }
105 if (!currentAmFmBandRange.has_value()) {
106 return false;
107 }
108 int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
109 if (freq < currentAmFmBandRange->lowerBound || freq > currentAmFmBandRange->upperBound) {
110 return false;
111 }
112 return isDigitalProgramAllowed(sel, forceAnalogFm, forceAnalogAm);
113}
114
Weilin Xub2a6ca62022-05-08 23:47:04 +0000115// Makes ProgramInfo that does not point to any particular program
116ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
117 ProgramInfo info = {};
118 info.selector = selector;
Weilin Xu39dd0f82023-09-07 17:00:57 -0700119 switch (info.selector.primaryId.type) {
120 case IdentifierType::AMFM_FREQUENCY_KHZ:
121 info.logicallyTunedTo = utils::makeIdentifier(
122 IdentifierType::AMFM_FREQUENCY_KHZ,
123 utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
124 info.physicallyTunedTo = info.logicallyTunedTo;
125 break;
126 case IdentifierType::HD_STATION_ID_EXT:
127 info.logicallyTunedTo = utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
128 utils::getAmFmFrequency(info.selector));
129 info.physicallyTunedTo = info.logicallyTunedTo;
130 break;
131 case IdentifierType::DAB_SID_EXT:
132 info.logicallyTunedTo = info.selector.primaryId;
133 info.physicallyTunedTo = utils::makeIdentifier(
134 IdentifierType::DAB_FREQUENCY_KHZ,
135 utils::getId(selector, IdentifierType::DAB_FREQUENCY_KHZ));
136 break;
137 default:
138 info.logicallyTunedTo = info.selector.primaryId;
139 info.physicallyTunedTo = info.logicallyTunedTo;
140 break;
141 }
Weilin Xub2a6ca62022-05-08 23:47:04 +0000142 return info;
143}
144
Weilin Xu97203b02022-06-23 00:19:03 +0000145static bool checkDumpCallerHasWritePermissions(int fd) {
146 uid_t uid = AIBinder_getCallingUid();
147 if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
148 return true;
149 }
150 dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
151 return false;
152}
153
Weilin Xub2a6ca62022-05-08 23:47:04 +0000154} // namespace
155
156BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
157 : mVirtualRadio(virtualRadio),
158 mAmFmConfig(kDefaultAmFmConfig),
159 mProperties(initProperties(virtualRadio)) {
160 const auto& ranges = kDefaultAmFmConfig.ranges;
161 if (ranges.size() > 0) {
162 ProgramSelector sel = utils::makeSelectorAmfm(ranges[0].lowerBound);
163 VirtualProgram virtualProgram = {};
164 if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
165 mCurrentProgram = virtualProgram.selector;
166 } else {
167 mCurrentProgram = sel;
168 }
Weilin Xu48338962023-11-03 14:31:15 -0700169 adjustAmFmRangeLocked();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000170 }
171}
172
173BroadcastRadio::~BroadcastRadio() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000174 mTuningThread.reset();
175 mProgramListThread.reset();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000176}
177
178ScopedAStatus BroadcastRadio::getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) {
179 if (full) {
180 *returnConfigs = {};
181 returnConfigs->ranges = vector<AmFmBandRange>({
Weilin Xu48338962023-11-03 14:31:15 -0700182 kFmFullBandRange,
183 kAmFullBandRange,
Weilin Xub2a6ca62022-05-08 23:47:04 +0000184 });
185 returnConfigs->fmDeemphasis =
186 AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
187 returnConfigs->fmRds = AmFmRegionConfig::RDS | AmFmRegionConfig::RBDS;
188 return ScopedAStatus::ok();
189 }
190 lock_guard<mutex> lk(mMutex);
191 *returnConfigs = mAmFmConfig;
192 return ScopedAStatus::ok();
193}
194
195ScopedAStatus BroadcastRadio::getDabRegionConfig(vector<DabTableEntry>* returnConfigs) {
196 *returnConfigs = {
197 {"5A", 174928}, {"7D", 194064}, {"8A", 195936}, {"8B", 197648}, {"9A", 202928},
198 {"9B", 204640}, {"9C", 206352}, {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
199 {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
200 {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
201 };
202 return ScopedAStatus::ok();
203}
204
205ScopedAStatus BroadcastRadio::getImage(int32_t id, vector<uint8_t>* returnImage) {
206 LOG(DEBUG) << __func__ << ": fetching image " << std::hex << id;
207
208 if (id == resources::kDemoPngId) {
209 *returnImage = vector<uint8_t>(resources::kDemoPng, std::end(resources::kDemoPng));
210 return ScopedAStatus::ok();
211 }
212
213 LOG(WARNING) << __func__ << ": image of id " << std::hex << id << " doesn't exist";
214 *returnImage = {};
215 return ScopedAStatus::ok();
216}
217
218ScopedAStatus BroadcastRadio::getProperties(Properties* returnProperties) {
219 lock_guard<mutex> lk(mMutex);
220 *returnProperties = mProperties;
221 return ScopedAStatus::ok();
222}
223
224ProgramInfo BroadcastRadio::tuneInternalLocked(const ProgramSelector& sel) {
225 LOG(DEBUG) << __func__ << ": tune (internal) to " << sel.toString();
226
227 VirtualProgram virtualProgram = {};
228 ProgramInfo programInfo;
Weilin Xu48338962023-11-03 14:31:15 -0700229 bool isProgramAllowed =
230 isDigitalProgramAllowed(sel, isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
231 isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
232 if (isProgramAllowed && mVirtualRadio.getProgram(sel, &virtualProgram)) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000233 mCurrentProgram = virtualProgram.selector;
234 programInfo = virtualProgram;
235 } else {
Weilin Xu48338962023-11-03 14:31:15 -0700236 if (!isProgramAllowed) {
237 mCurrentProgram = utils::makeSelectorAmfm(utils::getAmFmFrequency(sel));
238 } else {
239 mCurrentProgram = sel;
240 }
Weilin Xub2a6ca62022-05-08 23:47:04 +0000241 programInfo = makeSampleProgramInfo(sel);
242 }
Weilin Xu79919962023-11-06 19:11:02 -0800243 programInfo.infoFlags |= ProgramInfo::FLAG_SIGNAL_ACQUISITION;
244 if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
245 mIsTuneCompleted = true;
246 }
Weilin Xu48338962023-11-03 14:31:15 -0700247 if (adjustAmFmRangeLocked()) {
248 startProgramListUpdatesLocked({});
249 }
Weilin Xub2a6ca62022-05-08 23:47:04 +0000250
251 return programInfo;
252}
253
254ScopedAStatus BroadcastRadio::setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) {
255 LOG(DEBUG) << __func__ << ": setTunerCallback";
256
257 if (callback == nullptr) {
258 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
259 resultToInt(Result::INVALID_ARGUMENTS), "cannot set tuner callback to null");
260 }
261
262 lock_guard<mutex> lk(mMutex);
263 mCallback = callback;
264
265 return ScopedAStatus::ok();
266}
267
268ScopedAStatus BroadcastRadio::unsetTunerCallback() {
269 LOG(DEBUG) << __func__ << ": unsetTunerCallback";
270
271 lock_guard<mutex> lk(mMutex);
272 mCallback = nullptr;
273
274 return ScopedAStatus::ok();
275}
276
Weilin Xu79919962023-11-06 19:11:02 -0800277void BroadcastRadio::handleProgramInfoUpdateRadioCallback(
278 ProgramInfo programInfo, const std::shared_ptr<ITunerCallback>& callback) {
279 callback->onCurrentProgramInfoChanged(programInfo);
280 if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
281 return;
282 }
283 ProgramSelector sel = programInfo.selector;
284 auto cancelTask = [sel, callback]() { callback->onTuneFailed(Result::CANCELED, sel); };
285 programInfo.infoFlags |= ProgramInfo::FLAG_HD_SIS_ACQUISITION;
286 auto sisAcquiredTask = [this, callback, programInfo, cancelTask]() {
287 callback->onCurrentProgramInfoChanged(programInfo);
288 auto audioAcquiredTask = [this, callback, programInfo]() {
289 ProgramInfo hdProgramInfoWithAudio = programInfo;
290 hdProgramInfoWithAudio.infoFlags |= ProgramInfo::FLAG_HD_AUDIO_ACQUISITION;
291 callback->onCurrentProgramInfoChanged(hdProgramInfoWithAudio);
292 lock_guard<mutex> lk(mMutex);
293 mIsTuneCompleted = true;
294 };
295 lock_guard<mutex> lk(mMutex);
296 mTuningThread->schedule(audioAcquiredTask, cancelTask, kTuneDelayTimeMs);
297 };
298
299 lock_guard<mutex> lk(mMutex);
300 mTuningThread->schedule(sisAcquiredTask, cancelTask, kTuneDelayTimeMs);
301}
302
Weilin Xub2a6ca62022-05-08 23:47:04 +0000303ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
304 LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
305
306 lock_guard<mutex> lk(mMutex);
307 if (mCallback == nullptr) {
308 LOG(ERROR) << __func__ << ": callback is not registered.";
309 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
310 resultToInt(Result::INVALID_STATE), "callback is not registered");
311 }
312
313 if (!utils::isSupported(mProperties, program)) {
314 LOG(WARNING) << __func__ << ": selector not supported: " << program.toString();
315 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
316 resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
317 }
318
Weilin Xu31c541c2023-09-07 17:00:57 -0700319 if (!utils::isValidV2(program)) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000320 LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
321 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
322 resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
323 }
324
325 cancelLocked();
326
327 mIsTuneCompleted = false;
328 std::shared_ptr<ITunerCallback> callback = mCallback;
329 auto task = [this, program, callback]() {
330 ProgramInfo programInfo = {};
331 {
332 lock_guard<mutex> lk(mMutex);
333 programInfo = tuneInternalLocked(program);
334 }
Weilin Xu79919962023-11-06 19:11:02 -0800335 handleProgramInfoUpdateRadioCallback(programInfo, callback);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000336 };
Weilin Xud7483322022-11-29 01:12:36 +0000337 auto cancelTask = [program, callback]() { callback->onTuneFailed(Result::CANCELED, program); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000338 mTuningThread->schedule(task, cancelTask, kTuneDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000339
340 return ScopedAStatus::ok();
341}
342
Weilin Xu39dd0f82023-09-07 17:00:57 -0700343bool BroadcastRadio::findNextLocked(const ProgramSelector& current, bool directionUp,
344 bool skipSubChannel, VirtualProgram* nextProgram) const {
345 if (mProgramList.empty()) {
346 return false;
347 }
348 // The list is not sorted here since it has already stored in VirtualRadio.
349 bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
Weilin Xu90e39f52023-11-07 20:07:23 -0800350 bool hasDabSId = utils::hasId(current, IdentifierType::DAB_SID_EXT);
351 uint32_t currentChannel =
352 hasAmFmFrequency ? utils::getAmFmFrequency(current) : utils::getDabSId(current);
Weilin Xu39dd0f82023-09-07 17:00:57 -0700353 auto found =
354 std::lower_bound(mProgramList.begin(), mProgramList.end(), VirtualProgram({current}));
355 if (directionUp) {
356 if (found < mProgramList.end() - 1) {
357 // When seeking up, tuner will jump to the first selector which is main program service
Weilin Xu48338962023-11-03 14:31:15 -0700358 // greater than and of the same band as the current program selector in the program
359 // list (if not exist, jump to the first selector in the same band) for skipping
360 // sub-channels case or AM/FM without HD radio enabled case. Otherwise, the tuner will
361 // jump to the first selector which is greater than and of the same band as the current
362 // program selector.
Weilin Xu39dd0f82023-09-07 17:00:57 -0700363 if (utils::tunesTo(current, found->selector)) found++;
Weilin Xu90e39f52023-11-07 20:07:23 -0800364 if (skipSubChannel) {
365 if (hasAmFmFrequency || hasDabSId) {
366 auto firstFound = found;
367 while ((hasAmFmFrequency &&
368 utils::getAmFmFrequency(found->selector) == currentChannel) ||
369 (hasDabSId && utils::getDabSId(found->selector) == currentChannel)) {
370 if (found < mProgramList.end() - 1) {
371 found++;
372 } else {
373 found = mProgramList.begin();
374 }
375 if (found == firstFound) {
376 // Only one main channel exists in the program list, the tuner cannot
377 // skip sub-channel to the next program selector.
378 return false;
379 }
Weilin Xu39dd0f82023-09-07 17:00:57 -0700380 }
381 }
382 }
383 } else {
Weilin Xu48338962023-11-03 14:31:15 -0700384 // If the selector of current program is no less than all selectors of the same band or
385 // not found in the program list, seeking up should wrap the tuner to the first program
386 // selector of the same band in the program list.
Weilin Xu39dd0f82023-09-07 17:00:57 -0700387 found = mProgramList.begin();
388 }
389 } else {
390 if (found > mProgramList.begin() && found != mProgramList.end()) {
391 // When seeking down, tuner will jump to the first selector which is main program
Weilin Xu48338962023-11-03 14:31:15 -0700392 // service less than and of the same band as the current program selector in the
393 // program list (if not exist, jump to the last main program service selector of the
394 // same band) for skipping sub-channels case or AM/FM without HD radio enabled case.
395 // Otherwise, the tuner will jump to the first selector less than and of the same band
396 // as the current program selector.
Weilin Xu39dd0f82023-09-07 17:00:57 -0700397 found--;
Weilin Xu90e39f52023-11-07 20:07:23 -0800398 if ((hasAmFmFrequency && utils::hasAmFmFrequency(found->selector)) ||
399 (hasDabSId && utils::hasId(found->selector, IdentifierType::DAB_SID_EXT))) {
400 uint32_t nextChannel = hasAmFmFrequency ? utils::getAmFmFrequency(found->selector)
401 : utils::getDabSId(found->selector);
402 if (nextChannel != currentChannel) {
Weilin Xu39dd0f82023-09-07 17:00:57 -0700403 jumpToFirstSubChannelLocked(found);
404 } else if (skipSubChannel) {
405 jumpToFirstSubChannelLocked(found);
406 auto firstFound = found;
407 if (found > mProgramList.begin()) {
408 found--;
409 } else {
410 found = mProgramList.end() - 1;
411 }
412 jumpToFirstSubChannelLocked(found);
413 if (found == firstFound) {
414 // Only one main channel exists in the program list, the tuner cannot skip
415 // sub-channel to the next program selector.
416 return false;
417 }
418 }
419 }
420 } else {
Weilin Xu48338962023-11-03 14:31:15 -0700421 // If the selector of current program is no greater than all selectors of the same band
422 // or not found in the program list, seeking down should wrap the tuner to the last
423 // selector of the same band in the program list. If the last program selector in the
424 // program list is sub-channel and skipping sub-channels is needed, the tuner will jump
425 // to the last main program service of the same band in the program list.
Weilin Xu39dd0f82023-09-07 17:00:57 -0700426 found = mProgramList.end() - 1;
427 jumpToFirstSubChannelLocked(found);
428 }
429 }
430 *nextProgram = *found;
431 return true;
432}
433
434void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
Weilin Xu90e39f52023-11-07 20:07:23 -0800435 if (it == mProgramList.begin()) {
Weilin Xu39dd0f82023-09-07 17:00:57 -0700436 return;
437 }
Weilin Xu90e39f52023-11-07 20:07:23 -0800438 bool hasAmFmFrequency = utils::hasAmFmFrequency(it->selector);
439 bool hasDabSId = utils::hasId(it->selector, IdentifierType::DAB_SID_EXT);
440 if (hasAmFmFrequency || hasDabSId) {
441 uint32_t currentChannel = hasAmFmFrequency ? utils::getAmFmFrequency(it->selector)
442 : utils::getDabSId(it->selector);
Weilin Xu39dd0f82023-09-07 17:00:57 -0700443 it--;
Weilin Xu90e39f52023-11-07 20:07:23 -0800444 while (it != mProgramList.begin()) {
445 if (hasAmFmFrequency && utils::hasAmFmFrequency(it->selector) &&
446 utils::getAmFmFrequency(it->selector) == currentChannel) {
447 it--;
448 } else if (hasDabSId && utils::hasId(it->selector, IdentifierType::DAB_SID_EXT) &&
449 utils::getDabSId(it->selector) == currentChannel) {
450 it--;
451 } else {
452 break;
453 }
454 }
455 it++;
Weilin Xu39dd0f82023-09-07 17:00:57 -0700456 }
Weilin Xu39dd0f82023-09-07 17:00:57 -0700457}
458
Weilin Xub2a6ca62022-05-08 23:47:04 +0000459ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
460 LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
461 << (skipSubChannel ? "yes" : "no") << "...";
462
463 lock_guard<mutex> lk(mMutex);
464 if (mCallback == nullptr) {
465 LOG(ERROR) << __func__ << ": callback is not registered.";
466 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
467 resultToInt(Result::INVALID_STATE), "callback is not registered");
468 }
469
470 cancelLocked();
471
Weilin Xu48338962023-11-03 14:31:15 -0700472 auto filterCb = [this](const VirtualProgram& program) {
473 return isProgramInBand(program.selector, mCurrentAmFmBandRange,
474 isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
475 isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
476 };
477 const auto& list = mVirtualRadio.getProgramList();
478 mProgramList.clear();
479 std::copy_if(list.begin(), list.end(), std::back_inserter(mProgramList), filterCb);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000480 std::shared_ptr<ITunerCallback> callback = mCallback;
Weilin Xud7483322022-11-29 01:12:36 +0000481 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xu39dd0f82023-09-07 17:00:57 -0700482
483 VirtualProgram nextProgram = {};
484 bool foundNext = findNextLocked(mCurrentProgram, directionUp, skipSubChannel, &nextProgram);
485 mIsTuneCompleted = false;
486 if (!foundNext) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000487 auto task = [callback]() {
488 LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
489
490 callback->onTuneFailed(Result::TIMEOUT, {});
491 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000492 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000493
494 return ScopedAStatus::ok();
495 }
496
Weilin Xu39dd0f82023-09-07 17:00:57 -0700497 auto task = [this, nextProgram, callback]() {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000498 ProgramInfo programInfo = {};
499 {
500 lock_guard<mutex> lk(mMutex);
Weilin Xu39dd0f82023-09-07 17:00:57 -0700501 programInfo = tuneInternalLocked(nextProgram.selector);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000502 }
Weilin Xu79919962023-11-06 19:11:02 -0800503 handleProgramInfoUpdateRadioCallback(programInfo, callback);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000504 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000505 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000506
507 return ScopedAStatus::ok();
508}
509
510ScopedAStatus BroadcastRadio::step(bool directionUp) {
511 LOG(DEBUG) << __func__ << ": step " << (directionUp ? "up" : "down") << "...";
512
513 lock_guard<mutex> lk(mMutex);
514 if (mCallback == nullptr) {
515 LOG(ERROR) << __func__ << ": callback is not registered.";
516 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
517 resultToInt(Result::INVALID_STATE), "callback is not registered");
518 }
519
520 cancelLocked();
521
Weilin Xu39dd0f82023-09-07 17:00:57 -0700522 int64_t stepTo;
523 if (utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
524 stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
525 } else if (mCurrentProgram.primaryId.type == IdentifierType::HD_STATION_ID_EXT) {
526 stepTo = utils::getHdFrequency(mCurrentProgram);
527 } else {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000528 LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
529 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
530 resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
531 }
532
Weilin Xu48338962023-11-03 14:31:15 -0700533 if (!mCurrentAmFmBandRange.has_value()) {
534 LOG(ERROR) << __func__ << ": can't find current band";
Weilin Xuee546a62023-11-14 12:57:50 -0800535 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
Weilin Xu48338962023-11-03 14:31:15 -0700536 resultToInt(Result::INTERNAL_ERROR), "can't find current band");
Weilin Xub2a6ca62022-05-08 23:47:04 +0000537 }
538
539 if (directionUp) {
Weilin Xu48338962023-11-03 14:31:15 -0700540 stepTo += mCurrentAmFmBandRange->spacing;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000541 } else {
Weilin Xu48338962023-11-03 14:31:15 -0700542 stepTo -= mCurrentAmFmBandRange->spacing;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000543 }
Weilin Xu48338962023-11-03 14:31:15 -0700544 if (stepTo > mCurrentAmFmBandRange->upperBound) {
545 stepTo = mCurrentAmFmBandRange->lowerBound;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000546 }
Weilin Xu48338962023-11-03 14:31:15 -0700547 if (stepTo < mCurrentAmFmBandRange->lowerBound) {
548 stepTo = mCurrentAmFmBandRange->upperBound;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000549 }
550
551 mIsTuneCompleted = false;
552 std::shared_ptr<ITunerCallback> callback = mCallback;
553 auto task = [this, stepTo, callback]() {
554 ProgramInfo programInfo;
555 {
556 lock_guard<mutex> lk(mMutex);
557 programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
558 }
Weilin Xu79919962023-11-06 19:11:02 -0800559 handleProgramInfoUpdateRadioCallback(programInfo, callback);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000560 };
Weilin Xud7483322022-11-29 01:12:36 +0000561 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000562 mTuningThread->schedule(task, cancelTask, kStepDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000563
564 return ScopedAStatus::ok();
565}
566
567void BroadcastRadio::cancelLocked() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000568 LOG(DEBUG) << __func__ << ": cancelling current tuning operations...";
Weilin Xub2a6ca62022-05-08 23:47:04 +0000569
Weilin Xu764fe0d2023-07-26 18:07:05 +0000570 mTuningThread->cancelAll();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000571 if (mCurrentProgram.primaryId.type != IdentifierType::INVALID) {
572 mIsTuneCompleted = true;
573 }
574}
575
576ScopedAStatus BroadcastRadio::cancel() {
577 LOG(DEBUG) << __func__ << ": cancel pending tune, seek and step...";
578
579 lock_guard<mutex> lk(mMutex);
580 cancelLocked();
581
582 return ScopedAStatus::ok();
583}
584
Weilin Xu48338962023-11-03 14:31:15 -0700585void BroadcastRadio::startProgramListUpdatesLocked(const ProgramFilter& filter) {
586 auto filterCb = [&filter, this](const VirtualProgram& program) {
587 return utils::satisfies(filter, program.selector) &&
588 isProgramInBand(program.selector, mCurrentAmFmBandRange,
589 isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
590 isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
Weilin Xub2a6ca62022-05-08 23:47:04 +0000591 };
592
Weilin Xu764fe0d2023-07-26 18:07:05 +0000593 cancelProgramListUpdateLocked();
594
Weilin Xub2a6ca62022-05-08 23:47:04 +0000595 const auto& list = mVirtualRadio.getProgramList();
596 vector<VirtualProgram> filteredList;
597 std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
598
599 auto task = [this, filteredList]() {
600 std::shared_ptr<ITunerCallback> callback;
601 {
602 lock_guard<mutex> lk(mMutex);
603 if (mCallback == nullptr) {
604 LOG(WARNING) << "Callback is null when updating program List";
605 return;
606 }
607 callback = mCallback;
608 }
609
610 ProgramListChunk chunk = {};
611 chunk.purge = true;
612 chunk.complete = true;
613 chunk.modified = vector<ProgramInfo>(filteredList.begin(), filteredList.end());
614
615 callback->onProgramListUpdated(chunk);
616 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000617 mProgramListThread->schedule(task, kListDelayTimeS);
Weilin Xu48338962023-11-03 14:31:15 -0700618}
619
620ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
621 LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
622 << "...";
623
624 lock_guard<mutex> lk(mMutex);
625
626 startProgramListUpdatesLocked(filter);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000627
628 return ScopedAStatus::ok();
629}
630
Weilin Xu764fe0d2023-07-26 18:07:05 +0000631void BroadcastRadio::cancelProgramListUpdateLocked() {
632 LOG(DEBUG) << __func__ << ": cancelling current program list update operations...";
633 mProgramListThread->cancelAll();
634}
635
Weilin Xub2a6ca62022-05-08 23:47:04 +0000636ScopedAStatus BroadcastRadio::stopProgramListUpdates() {
637 LOG(DEBUG) << __func__ << ": requested program list updates to stop...";
Weilin Xu764fe0d2023-07-26 18:07:05 +0000638 lock_guard<mutex> lk(mMutex);
639 cancelProgramListUpdateLocked();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000640 return ScopedAStatus::ok();
641}
642
Weilin Xu48338962023-11-03 14:31:15 -0700643bool BroadcastRadio::isConfigFlagSetLocked(ConfigFlag flag) const {
644 int flagBit = static_cast<int>(flag);
645 return ((mConfigFlagValues >> flagBit) & 1) == 1;
646}
647
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000648ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000649 LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
650
Weilin Xu48338962023-11-03 14:31:15 -0700651 if (flag == ConfigFlag::FORCE_ANALOG) {
652 flag = ConfigFlag::FORCE_ANALOG_FM;
653 }
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000654 lock_guard<mutex> lk(mMutex);
Weilin Xu48338962023-11-03 14:31:15 -0700655 *returnIsSet = isConfigFlagSetLocked(flag);
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000656 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000657}
658
659ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
660 LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
661
Weilin Xu48338962023-11-03 14:31:15 -0700662 if (flag == ConfigFlag::FORCE_ANALOG) {
663 flag = ConfigFlag::FORCE_ANALOG_FM;
664 }
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000665 int flagBitMask = 1 << (static_cast<int>(flag));
666 lock_guard<mutex> lk(mMutex);
667 if (value) {
668 mConfigFlagValues |= flagBitMask;
669 } else {
670 mConfigFlagValues &= ~flagBitMask;
671 }
Weilin Xu48338962023-11-03 14:31:15 -0700672 if (flag == ConfigFlag::FORCE_ANALOG_AM || flag == ConfigFlag::FORCE_ANALOG_FM) {
673 startProgramListUpdatesLocked({});
674 }
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000675 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000676}
677
678ScopedAStatus BroadcastRadio::setParameters(
679 [[maybe_unused]] const vector<VendorKeyValue>& parameters,
680 vector<VendorKeyValue>* returnParameters) {
681 // TODO(b/243682330) Support vendor parameter functionality
682 *returnParameters = {};
683 return ScopedAStatus::ok();
684}
685
686ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
687 vector<VendorKeyValue>* returnParameters) {
688 // TODO(b/243682330) Support vendor parameter functionality
689 *returnParameters = {};
690 return ScopedAStatus::ok();
691}
692
Weilin Xu48338962023-11-03 14:31:15 -0700693bool BroadcastRadio::adjustAmFmRangeLocked() {
694 bool hasBandBefore = mCurrentAmFmBandRange.has_value();
Weilin Xu39dd0f82023-09-07 17:00:57 -0700695 if (!utils::hasAmFmFrequency(mCurrentProgram)) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000696 LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
Weilin Xu48338962023-11-03 14:31:15 -0700697 mCurrentAmFmBandRange.reset();
698 return hasBandBefore;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000699 }
700
Weilin Xu48338962023-11-03 14:31:15 -0700701 int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(mCurrentProgram));
Weilin Xub2a6ca62022-05-08 23:47:04 +0000702 for (const auto& range : mAmFmConfig.ranges) {
703 if (range.lowerBound <= freq && range.upperBound >= freq) {
Weilin Xu48338962023-11-03 14:31:15 -0700704 bool isBandChanged = hasBandBefore ? *mCurrentAmFmBandRange != range : true;
705 mCurrentAmFmBandRange = range;
706 return isBandChanged;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000707 }
708 }
709
Weilin Xu48338962023-11-03 14:31:15 -0700710 mCurrentAmFmBandRange.reset();
711 return !hasBandBefore;
Weilin Xub2a6ca62022-05-08 23:47:04 +0000712}
713
714ScopedAStatus BroadcastRadio::registerAnnouncementListener(
715 [[maybe_unused]] const std::shared_ptr<IAnnouncementListener>& listener,
716 const vector<AnnouncementType>& enabled, std::shared_ptr<ICloseHandle>* returnCloseHandle) {
717 LOG(DEBUG) << __func__ << ": registering announcement listener for "
718 << utils::vectorToString(enabled);
719
720 // TODO(b/243683842) Support announcement listener
721 *returnCloseHandle = nullptr;
722 LOG(INFO) << __func__ << ": registering announcementListener is not supported";
723 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
724 resultToInt(Result::NOT_SUPPORTED),
725 "registering announcementListener is not supported");
726}
727
Weilin Xu97203b02022-06-23 00:19:03 +0000728binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
729 if (numArgs == 0) {
730 return dumpsys(fd);
731 }
732
733 string option = string(args[0]);
734 if (EqualsIgnoreCase(option, "--help")) {
735 return cmdHelp(fd);
736 } else if (EqualsIgnoreCase(option, "--tune")) {
737 return cmdTune(fd, args, numArgs);
738 } else if (EqualsIgnoreCase(option, "--seek")) {
739 return cmdSeek(fd, args, numArgs);
740 } else if (EqualsIgnoreCase(option, "--step")) {
741 return cmdStep(fd, args, numArgs);
742 } else if (EqualsIgnoreCase(option, "--cancel")) {
743 return cmdCancel(fd, numArgs);
744 } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
745 return cmdStartProgramListUpdates(fd, args, numArgs);
746 } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
747 return cmdStopProgramListUpdates(fd, numArgs);
748 }
749 dprintf(fd, "Invalid option: %s\n", option.c_str());
750 return STATUS_BAD_VALUE;
751}
752
753binder_status_t BroadcastRadio::dumpsys(int fd) {
754 if (!checkDumpCallerHasWritePermissions(fd)) {
755 return STATUS_PERMISSION_DENIED;
756 }
757 lock_guard<mutex> lk(mMutex);
758 dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
759 dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
760 if (mIsTuneCompleted) {
761 dprintf(fd, "Tune completed\n");
762 } else {
763 dprintf(fd, "Tune not completed\n");
764 }
765 if (mCallback == nullptr) {
766 dprintf(fd, "No ITunerCallback registered\n");
767 } else {
768 dprintf(fd, "ITunerCallback registered\n");
769 }
770 dprintf(fd, "CurrentProgram: %s \n", mCurrentProgram.toString().c_str());
771 return STATUS_OK;
772}
773
774binder_status_t BroadcastRadio::cmdHelp(int fd) const {
775 dprintf(fd, "Usage: \n\n");
776 dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
777 dprintf(fd, "--help: shows this help\n");
778 dprintf(fd,
779 "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
780 "frequency (int) \n"
781 "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
782 "sidExt (int), ensemble (int) \n");
783 dprintf(fd,
784 "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
785 "option whether skipping sub channel: "
786 "skipSubChannel (string, should be either \"true\" or \"false\")\n");
787 dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
788 dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
789 dprintf(fd,
790 "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
791 "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
792 "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
793 "where TYPE is int), "
794 "program identifiers (string, in format "
795 "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
796 "where TYPE is int and VALUE is long), "
797 "includeCategories (string, should be either \"true\" or \"false\"), "
798 "excludeModifications (string, should be either \"true\" or \"false\")\n");
799 dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
800 dprintf(fd,
801 "Note on <TYPE> for --startProgramList command: it is int for identifier type. "
802 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
803 "for its definition.\n");
804 dprintf(fd,
805 "Note on <VALUE> for --startProgramList command: it is long type for identifier value. "
806 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
807 "for its value.\n");
808
809 return STATUS_OK;
810}
811
812binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
813 if (!checkDumpCallerHasWritePermissions(fd)) {
814 return STATUS_PERMISSION_DENIED;
815 }
816 if (numArgs != 3 && numArgs != 4) {
817 dprintf(fd,
818 "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
819 "or --tune dab <SID> <ENSEMBLE>\n");
820 return STATUS_BAD_VALUE;
821 }
822 bool isDab = false;
823 if (EqualsIgnoreCase(string(args[1]), "dab")) {
824 isDab = true;
825 } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
826 dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
827 return STATUS_BAD_VALUE;
828 }
829 ProgramSelector sel = {};
830 if (isDab) {
Weilin Xu64cb9632023-03-15 23:46:43 +0000831 if (numArgs != 5 && numArgs != 3) {
Weilin Xu97203b02022-06-23 00:19:03 +0000832 dprintf(fd,
Weilin Xu0d4207d2022-12-09 00:37:44 +0000833 "Invalid number of arguments: please provide "
Weilin Xu64cb9632023-03-15 23:46:43 +0000834 "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
835 "--tune dab <SID>\n");
Weilin Xu97203b02022-06-23 00:19:03 +0000836 return STATUS_BAD_VALUE;
837 }
838 int sid;
839 if (!utils::parseArgInt(string(args[2]), &sid)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000840 dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000841 return STATUS_BAD_VALUE;
842 }
Weilin Xu64cb9632023-03-15 23:46:43 +0000843 if (numArgs == 3) {
844 sel = utils::makeSelectorDab(sid);
845 } else {
846 int ensemble;
847 if (!utils::parseArgInt(string(args[3]), &ensemble)) {
848 dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
849 return STATUS_BAD_VALUE;
850 }
851 int freq;
852 if (!utils::parseArgInt(string(args[4]), &freq)) {
853 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
854 return STATUS_BAD_VALUE;
855 }
856 sel = utils::makeSelectorDab(sid, ensemble, freq);
Weilin Xu97203b02022-06-23 00:19:03 +0000857 }
Weilin Xu97203b02022-06-23 00:19:03 +0000858 } else {
859 if (numArgs != 3) {
860 dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
861 return STATUS_BAD_VALUE;
862 }
863 int freq;
864 if (!utils::parseArgInt(string(args[2]), &freq)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000865 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000866 return STATUS_BAD_VALUE;
867 }
868 sel = utils::makeSelectorAmfm(freq);
869 }
870
871 auto tuneResult = tune(sel);
872 if (!tuneResult.isOk()) {
873 dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
874 return STATUS_BAD_VALUE;
875 }
876 dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
877 return STATUS_OK;
878}
879
880binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
881 if (!checkDumpCallerHasWritePermissions(fd)) {
882 return STATUS_PERMISSION_DENIED;
883 }
884 if (numArgs != 3) {
885 dprintf(fd,
886 "Invalid number of arguments: please provide --seek <DIRECTION> "
887 "<SKIP_SUB_CHANNEL>\n");
888 return STATUS_BAD_VALUE;
889 }
890 string seekDirectionIn = string(args[1]);
891 bool seekDirectionUp;
892 if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
893 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
894 seekDirectionIn.c_str());
895 return STATUS_BAD_VALUE;
896 }
897 string skipSubChannelIn = string(args[2]);
898 bool skipSubChannel;
899 if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
900 dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
901 skipSubChannelIn.c_str());
902 return STATUS_BAD_VALUE;
903 }
904
905 auto seekResult = seek(seekDirectionUp, skipSubChannel);
906 if (!seekResult.isOk()) {
907 dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
908 return STATUS_BAD_VALUE;
909 }
910 dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
911 return STATUS_OK;
912}
913
914binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
915 if (!checkDumpCallerHasWritePermissions(fd)) {
916 return STATUS_PERMISSION_DENIED;
917 }
918 if (numArgs != 2) {
919 dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
920 return STATUS_BAD_VALUE;
921 }
922 string stepDirectionIn = string(args[1]);
923 bool stepDirectionUp;
924 if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
925 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
926 stepDirectionIn.c_str());
927 return STATUS_BAD_VALUE;
928 }
929
930 auto stepResult = step(stepDirectionUp);
931 if (!stepResult.isOk()) {
932 dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
933 return STATUS_BAD_VALUE;
934 }
935 dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
936 return STATUS_OK;
937}
938
939binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
940 if (!checkDumpCallerHasWritePermissions(fd)) {
941 return STATUS_PERMISSION_DENIED;
942 }
943 if (numArgs != 1) {
944 dprintf(fd,
945 "Invalid number of arguments: please provide --cancel "
946 "only and no more arguments\n");
947 return STATUS_BAD_VALUE;
948 }
949
950 auto cancelResult = cancel();
951 if (!cancelResult.isOk()) {
952 dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
953 return STATUS_BAD_VALUE;
954 }
955 dprintf(fd, "Canceled pending tune, seek, and step\n");
956 return STATUS_OK;
957}
958
959binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
960 uint32_t numArgs) {
961 if (!checkDumpCallerHasWritePermissions(fd)) {
962 return STATUS_PERMISSION_DENIED;
963 }
964 if (numArgs != 5) {
965 dprintf(fd,
966 "Invalid number of arguments: please provide --startProgramListUpdates "
967 "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
968 "<EXCLUDE_MODIFICATIONS>\n");
969 return STATUS_BAD_VALUE;
970 }
971 string filterTypesStr = string(args[1]);
972 std::vector<IdentifierType> filterTypeList;
973 if (!EqualsIgnoreCase(filterTypesStr, "null") &&
974 !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
975 dprintf(fd,
976 "Invalid identifier types provided with startProgramListUpdates: %s, "
977 "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
978 filterTypesStr.c_str());
979 return STATUS_BAD_VALUE;
980 }
981 string filtersStr = string(args[2]);
982 std::vector<ProgramIdentifier> filterList;
983 if (!EqualsIgnoreCase(filtersStr, "null") &&
984 !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
985 dprintf(fd,
986 "Invalid program identifiers provided with startProgramListUpdates: %s, "
987 "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
988 filtersStr.c_str());
989 return STATUS_BAD_VALUE;
990 }
991 string includeCategoriesStr = string(args[3]);
992 bool includeCategories;
993 if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
994 dprintf(fd,
995 "Invalid includeCategories (\"true\" or \"false\") "
996 "provided with startProgramListUpdates : %s\n",
997 includeCategoriesStr.c_str());
998 return STATUS_BAD_VALUE;
999 }
1000 string excludeModificationsStr = string(args[4]);
1001 bool excludeModifications;
1002 if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
1003 dprintf(fd,
1004 "Invalid excludeModifications(\"true\" or \"false\") "
1005 "provided with startProgramListUpdates : %s\n",
1006 excludeModificationsStr.c_str());
1007 return STATUS_BAD_VALUE;
1008 }
1009 ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
1010
1011 auto updateResult = startProgramListUpdates(filter);
1012 if (!updateResult.isOk()) {
1013 dprintf(fd, "Unable to start program list update for filter %s \n",
1014 filter.toString().c_str());
1015 return STATUS_BAD_VALUE;
1016 }
1017 dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
1018 return STATUS_OK;
1019}
1020
1021binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
1022 if (!checkDumpCallerHasWritePermissions(fd)) {
1023 return STATUS_PERMISSION_DENIED;
1024 }
1025 if (numArgs != 1) {
1026 dprintf(fd,
1027 "Invalid number of arguments: please provide --stopProgramListUpdates "
1028 "only and no more arguments\n");
1029 return STATUS_BAD_VALUE;
1030 }
1031
1032 auto stopResult = stopProgramListUpdates();
1033 if (!stopResult.isOk()) {
1034 dprintf(fd, "Unable to stop pending program list update\n");
1035 return STATUS_BAD_VALUE;
1036 }
1037 dprintf(fd, "Stop pending program list update\n");
1038 return STATUS_OK;
1039}
1040
Weilin Xub2a6ca62022-05-08 23:47:04 +00001041} // namespace aidl::android::hardware::broadcastradio