blob: 8584921fba3fdac37a600bfe778f7eec6f0ad616 [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>
19#include "resources.h"
20
21#include <aidl/android/hardware/broadcastradio/IdentifierType.h>
22#include <aidl/android/hardware/broadcastradio/Result.h>
23
24#include <android-base/logging.h>
Weilin Xu97203b02022-06-23 00:19:03 +000025#include <android-base/strings.h>
26
27#include <private/android_filesystem_config.h>
Weilin Xub2a6ca62022-05-08 23:47:04 +000028
29namespace aidl::android::hardware::broadcastradio {
30
31using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
32using ::aidl::android::hardware::broadcastradio::utils::tunesTo;
Weilin Xu97203b02022-06-23 00:19:03 +000033using ::android::base::EqualsIgnoreCase;
Weilin Xub2a6ca62022-05-08 23:47:04 +000034using ::ndk::ScopedAStatus;
35using ::std::literals::chrono_literals::operator""ms;
36using ::std::literals::chrono_literals::operator""s;
37using ::std::lock_guard;
38using ::std::mutex;
39using ::std::string;
40using ::std::vector;
41
42namespace {
43
44inline constexpr std::chrono::milliseconds kSeekDelayTimeMs = 200ms;
45inline constexpr std::chrono::milliseconds kStepDelayTimeMs = 100ms;
46inline constexpr std::chrono::milliseconds kTuneDelayTimeMs = 150ms;
47inline constexpr std::chrono::seconds kListDelayTimeS = 1s;
48
49// clang-format off
50const AmFmRegionConfig kDefaultAmFmConfig = {
51 {
52 {87500, 108000, 100, 100}, // FM
53 {153, 282, 3, 9}, // AM LW
54 {531, 1620, 9, 9}, // AM MW
55 {1600, 30000, 1, 5}, // AM SW
56 },
57 AmFmRegionConfig::DEEMPHASIS_D50,
58 AmFmRegionConfig::RDS};
59// clang-format on
60
61Properties initProperties(const VirtualRadio& virtualRadio) {
62 Properties prop = {};
63
64 prop.maker = "Android";
65 prop.product = virtualRadio.getName();
66 prop.supportedIdentifierTypes = vector<IdentifierType>({
67 IdentifierType::AMFM_FREQUENCY_KHZ,
68 IdentifierType::RDS_PI,
69 IdentifierType::HD_STATION_ID_EXT,
70 IdentifierType::DAB_SID_EXT,
71 });
72 prop.vendorInfo = vector<VendorKeyValue>({
73 {"com.android.sample", "sample"},
74 });
75
76 return prop;
77}
78
79// Makes ProgramInfo that does not point to any particular program
80ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
81 ProgramInfo info = {};
82 info.selector = selector;
83 info.logicallyTunedTo =
84 utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
85 utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
86 info.physicallyTunedTo = info.logicallyTunedTo;
87 return info;
88}
89
Weilin Xu97203b02022-06-23 00:19:03 +000090static bool checkDumpCallerHasWritePermissions(int fd) {
91 uid_t uid = AIBinder_getCallingUid();
92 if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
93 return true;
94 }
95 dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
96 return false;
97}
98
Weilin Xub2a6ca62022-05-08 23:47:04 +000099} // namespace
100
101BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
102 : mVirtualRadio(virtualRadio),
103 mAmFmConfig(kDefaultAmFmConfig),
104 mProperties(initProperties(virtualRadio)) {
105 const auto& ranges = kDefaultAmFmConfig.ranges;
106 if (ranges.size() > 0) {
107 ProgramSelector sel = utils::makeSelectorAmfm(ranges[0].lowerBound);
108 VirtualProgram virtualProgram = {};
109 if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
110 mCurrentProgram = virtualProgram.selector;
111 } else {
112 mCurrentProgram = sel;
113 }
114 }
115}
116
117BroadcastRadio::~BroadcastRadio() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000118 mTuningThread.reset();
119 mProgramListThread.reset();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000120}
121
122ScopedAStatus BroadcastRadio::getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) {
123 if (full) {
124 *returnConfigs = {};
125 returnConfigs->ranges = vector<AmFmBandRange>({
126 {65000, 108000, 10, 0}, // FM
127 {150, 30000, 1, 0}, // AM
128 });
129 returnConfigs->fmDeemphasis =
130 AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
131 returnConfigs->fmRds = AmFmRegionConfig::RDS | AmFmRegionConfig::RBDS;
132 return ScopedAStatus::ok();
133 }
134 lock_guard<mutex> lk(mMutex);
135 *returnConfigs = mAmFmConfig;
136 return ScopedAStatus::ok();
137}
138
139ScopedAStatus BroadcastRadio::getDabRegionConfig(vector<DabTableEntry>* returnConfigs) {
140 *returnConfigs = {
141 {"5A", 174928}, {"7D", 194064}, {"8A", 195936}, {"8B", 197648}, {"9A", 202928},
142 {"9B", 204640}, {"9C", 206352}, {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
143 {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
144 {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
145 };
146 return ScopedAStatus::ok();
147}
148
149ScopedAStatus BroadcastRadio::getImage(int32_t id, vector<uint8_t>* returnImage) {
150 LOG(DEBUG) << __func__ << ": fetching image " << std::hex << id;
151
152 if (id == resources::kDemoPngId) {
153 *returnImage = vector<uint8_t>(resources::kDemoPng, std::end(resources::kDemoPng));
154 return ScopedAStatus::ok();
155 }
156
157 LOG(WARNING) << __func__ << ": image of id " << std::hex << id << " doesn't exist";
158 *returnImage = {};
159 return ScopedAStatus::ok();
160}
161
162ScopedAStatus BroadcastRadio::getProperties(Properties* returnProperties) {
163 lock_guard<mutex> lk(mMutex);
164 *returnProperties = mProperties;
165 return ScopedAStatus::ok();
166}
167
168ProgramInfo BroadcastRadio::tuneInternalLocked(const ProgramSelector& sel) {
169 LOG(DEBUG) << __func__ << ": tune (internal) to " << sel.toString();
170
171 VirtualProgram virtualProgram = {};
172 ProgramInfo programInfo;
173 if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
174 mCurrentProgram = virtualProgram.selector;
175 programInfo = virtualProgram;
176 } else {
177 mCurrentProgram = sel;
178 programInfo = makeSampleProgramInfo(sel);
179 }
180 mIsTuneCompleted = true;
181
182 return programInfo;
183}
184
185ScopedAStatus BroadcastRadio::setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) {
186 LOG(DEBUG) << __func__ << ": setTunerCallback";
187
188 if (callback == nullptr) {
189 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
190 resultToInt(Result::INVALID_ARGUMENTS), "cannot set tuner callback to null");
191 }
192
193 lock_guard<mutex> lk(mMutex);
194 mCallback = callback;
195
196 return ScopedAStatus::ok();
197}
198
199ScopedAStatus BroadcastRadio::unsetTunerCallback() {
200 LOG(DEBUG) << __func__ << ": unsetTunerCallback";
201
202 lock_guard<mutex> lk(mMutex);
203 mCallback = nullptr;
204
205 return ScopedAStatus::ok();
206}
207
208ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
209 LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
210
211 lock_guard<mutex> lk(mMutex);
212 if (mCallback == nullptr) {
213 LOG(ERROR) << __func__ << ": callback is not registered.";
214 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
215 resultToInt(Result::INVALID_STATE), "callback is not registered");
216 }
217
218 if (!utils::isSupported(mProperties, program)) {
219 LOG(WARNING) << __func__ << ": selector not supported: " << program.toString();
220 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
221 resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
222 }
223
224 if (!utils::isValid(program)) {
225 LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
226 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
227 resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
228 }
229
230 cancelLocked();
231
232 mIsTuneCompleted = false;
233 std::shared_ptr<ITunerCallback> callback = mCallback;
234 auto task = [this, program, callback]() {
235 ProgramInfo programInfo = {};
236 {
237 lock_guard<mutex> lk(mMutex);
238 programInfo = tuneInternalLocked(program);
239 }
240 callback->onCurrentProgramInfoChanged(programInfo);
241 };
Weilin Xud7483322022-11-29 01:12:36 +0000242 auto cancelTask = [program, callback]() { callback->onTuneFailed(Result::CANCELED, program); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000243 mTuningThread->schedule(task, cancelTask, kTuneDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000244
245 return ScopedAStatus::ok();
246}
247
248ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
249 LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
250 << (skipSubChannel ? "yes" : "no") << "...";
251
252 lock_guard<mutex> lk(mMutex);
253 if (mCallback == nullptr) {
254 LOG(ERROR) << __func__ << ": callback is not registered.";
255 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
256 resultToInt(Result::INVALID_STATE), "callback is not registered");
257 }
258
259 cancelLocked();
260
261 const auto& list = mVirtualRadio.getProgramList();
262 std::shared_ptr<ITunerCallback> callback = mCallback;
Weilin Xud7483322022-11-29 01:12:36 +0000263 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xub2a6ca62022-05-08 23:47:04 +0000264 if (list.empty()) {
265 mIsTuneCompleted = false;
266 auto task = [callback]() {
267 LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
268
269 callback->onTuneFailed(Result::TIMEOUT, {});
270 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000271 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000272
273 return ScopedAStatus::ok();
274 }
275
276 // The list is not sorted here since it has already stored in VirtualRadio.
277 // If the list is not sorted in advance, it should be sorted here.
278 const auto& current = mCurrentProgram;
279 auto found = std::lower_bound(list.begin(), list.end(), VirtualProgram({current}));
280 if (directionUp) {
281 if (found < list.end() - 1) {
282 if (tunesTo(current, found->selector)) found++;
283 } else {
284 found = list.begin();
285 }
286 } else {
287 if (found > list.begin() && found != list.end()) {
288 found--;
289 } else {
290 found = list.end() - 1;
291 }
292 }
293 const ProgramSelector tuneTo = found->selector;
294
295 mIsTuneCompleted = false;
296 auto task = [this, tuneTo, callback]() {
297 ProgramInfo programInfo = {};
298 {
299 lock_guard<mutex> lk(mMutex);
300 programInfo = tuneInternalLocked(tuneTo);
301 }
302 callback->onCurrentProgramInfoChanged(programInfo);
303 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000304 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000305
306 return ScopedAStatus::ok();
307}
308
309ScopedAStatus BroadcastRadio::step(bool directionUp) {
310 LOG(DEBUG) << __func__ << ": step " << (directionUp ? "up" : "down") << "...";
311
312 lock_guard<mutex> lk(mMutex);
313 if (mCallback == nullptr) {
314 LOG(ERROR) << __func__ << ": callback is not registered.";
315 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
316 resultToInt(Result::INVALID_STATE), "callback is not registered");
317 }
318
319 cancelLocked();
320
321 if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
322 LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
323 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
324 resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
325 }
326
327 int64_t stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
328 std::optional<AmFmBandRange> range = getAmFmRangeLocked();
329 if (!range) {
330 LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
331 ScopedAStatus::fromServiceSpecificErrorWithMessage(
332 resultToInt(Result::INTERNAL_ERROR),
333 "can't find current band or tune operation is in process");
334 }
335
336 if (directionUp) {
337 stepTo += range->spacing;
338 } else {
339 stepTo -= range->spacing;
340 }
341 if (stepTo > range->upperBound) {
342 stepTo = range->lowerBound;
343 }
344 if (stepTo < range->lowerBound) {
345 stepTo = range->upperBound;
346 }
347
348 mIsTuneCompleted = false;
349 std::shared_ptr<ITunerCallback> callback = mCallback;
350 auto task = [this, stepTo, callback]() {
351 ProgramInfo programInfo;
352 {
353 lock_guard<mutex> lk(mMutex);
354 programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
355 }
356 callback->onCurrentProgramInfoChanged(programInfo);
357 };
Weilin Xud7483322022-11-29 01:12:36 +0000358 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000359 mTuningThread->schedule(task, cancelTask, kStepDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000360
361 return ScopedAStatus::ok();
362}
363
364void BroadcastRadio::cancelLocked() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000365 LOG(DEBUG) << __func__ << ": cancelling current tuning operations...";
Weilin Xub2a6ca62022-05-08 23:47:04 +0000366
Weilin Xu764fe0d2023-07-26 18:07:05 +0000367 mTuningThread->cancelAll();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000368 if (mCurrentProgram.primaryId.type != IdentifierType::INVALID) {
369 mIsTuneCompleted = true;
370 }
371}
372
373ScopedAStatus BroadcastRadio::cancel() {
374 LOG(DEBUG) << __func__ << ": cancel pending tune, seek and step...";
375
376 lock_guard<mutex> lk(mMutex);
377 cancelLocked();
378
379 return ScopedAStatus::ok();
380}
381
382ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
383 LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
384 << "...";
385
386 auto filterCb = [&filter](const VirtualProgram& program) {
387 return utils::satisfies(filter, program.selector);
388 };
389
390 lock_guard<mutex> lk(mMutex);
391
Weilin Xu764fe0d2023-07-26 18:07:05 +0000392 cancelProgramListUpdateLocked();
393
Weilin Xub2a6ca62022-05-08 23:47:04 +0000394 const auto& list = mVirtualRadio.getProgramList();
395 vector<VirtualProgram> filteredList;
396 std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
397
398 auto task = [this, filteredList]() {
399 std::shared_ptr<ITunerCallback> callback;
400 {
401 lock_guard<mutex> lk(mMutex);
402 if (mCallback == nullptr) {
403 LOG(WARNING) << "Callback is null when updating program List";
404 return;
405 }
406 callback = mCallback;
407 }
408
409 ProgramListChunk chunk = {};
410 chunk.purge = true;
411 chunk.complete = true;
412 chunk.modified = vector<ProgramInfo>(filteredList.begin(), filteredList.end());
413
414 callback->onProgramListUpdated(chunk);
415 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000416 mProgramListThread->schedule(task, kListDelayTimeS);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000417
418 return ScopedAStatus::ok();
419}
420
Weilin Xu764fe0d2023-07-26 18:07:05 +0000421void BroadcastRadio::cancelProgramListUpdateLocked() {
422 LOG(DEBUG) << __func__ << ": cancelling current program list update operations...";
423 mProgramListThread->cancelAll();
424}
425
Weilin Xub2a6ca62022-05-08 23:47:04 +0000426ScopedAStatus BroadcastRadio::stopProgramListUpdates() {
427 LOG(DEBUG) << __func__ << ": requested program list updates to stop...";
Weilin Xu764fe0d2023-07-26 18:07:05 +0000428 lock_guard<mutex> lk(mMutex);
429 cancelProgramListUpdateLocked();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000430 return ScopedAStatus::ok();
431}
432
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000433ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000434 LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
435
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000436 int flagBit = static_cast<int>(flag);
437 lock_guard<mutex> lk(mMutex);
438 *returnIsSet = ((mConfigFlagValues >> flagBit) & 1) == 1;
439 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000440}
441
442ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
443 LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
444
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000445 int flagBitMask = 1 << (static_cast<int>(flag));
446 lock_guard<mutex> lk(mMutex);
447 if (value) {
448 mConfigFlagValues |= flagBitMask;
449 } else {
450 mConfigFlagValues &= ~flagBitMask;
451 }
452 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000453}
454
455ScopedAStatus BroadcastRadio::setParameters(
456 [[maybe_unused]] const vector<VendorKeyValue>& parameters,
457 vector<VendorKeyValue>* returnParameters) {
458 // TODO(b/243682330) Support vendor parameter functionality
459 *returnParameters = {};
460 return ScopedAStatus::ok();
461}
462
463ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
464 vector<VendorKeyValue>* returnParameters) {
465 // TODO(b/243682330) Support vendor parameter functionality
466 *returnParameters = {};
467 return ScopedAStatus::ok();
468}
469
470std::optional<AmFmBandRange> BroadcastRadio::getAmFmRangeLocked() const {
471 if (!mIsTuneCompleted) {
472 LOG(WARNING) << __func__ << ": tune operation is in process";
473 return {};
474 }
475 if (!utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
476 LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
477 return {};
478 }
479
480 int64_t freq = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
481 for (const auto& range : mAmFmConfig.ranges) {
482 if (range.lowerBound <= freq && range.upperBound >= freq) {
483 return range;
484 }
485 }
486
487 return {};
488}
489
490ScopedAStatus BroadcastRadio::registerAnnouncementListener(
491 [[maybe_unused]] const std::shared_ptr<IAnnouncementListener>& listener,
492 const vector<AnnouncementType>& enabled, std::shared_ptr<ICloseHandle>* returnCloseHandle) {
493 LOG(DEBUG) << __func__ << ": registering announcement listener for "
494 << utils::vectorToString(enabled);
495
496 // TODO(b/243683842) Support announcement listener
497 *returnCloseHandle = nullptr;
498 LOG(INFO) << __func__ << ": registering announcementListener is not supported";
499 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
500 resultToInt(Result::NOT_SUPPORTED),
501 "registering announcementListener is not supported");
502}
503
Weilin Xu97203b02022-06-23 00:19:03 +0000504binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
505 if (numArgs == 0) {
506 return dumpsys(fd);
507 }
508
509 string option = string(args[0]);
510 if (EqualsIgnoreCase(option, "--help")) {
511 return cmdHelp(fd);
512 } else if (EqualsIgnoreCase(option, "--tune")) {
513 return cmdTune(fd, args, numArgs);
514 } else if (EqualsIgnoreCase(option, "--seek")) {
515 return cmdSeek(fd, args, numArgs);
516 } else if (EqualsIgnoreCase(option, "--step")) {
517 return cmdStep(fd, args, numArgs);
518 } else if (EqualsIgnoreCase(option, "--cancel")) {
519 return cmdCancel(fd, numArgs);
520 } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
521 return cmdStartProgramListUpdates(fd, args, numArgs);
522 } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
523 return cmdStopProgramListUpdates(fd, numArgs);
524 }
525 dprintf(fd, "Invalid option: %s\n", option.c_str());
526 return STATUS_BAD_VALUE;
527}
528
529binder_status_t BroadcastRadio::dumpsys(int fd) {
530 if (!checkDumpCallerHasWritePermissions(fd)) {
531 return STATUS_PERMISSION_DENIED;
532 }
533 lock_guard<mutex> lk(mMutex);
534 dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
535 dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
536 if (mIsTuneCompleted) {
537 dprintf(fd, "Tune completed\n");
538 } else {
539 dprintf(fd, "Tune not completed\n");
540 }
541 if (mCallback == nullptr) {
542 dprintf(fd, "No ITunerCallback registered\n");
543 } else {
544 dprintf(fd, "ITunerCallback registered\n");
545 }
546 dprintf(fd, "CurrentProgram: %s \n", mCurrentProgram.toString().c_str());
547 return STATUS_OK;
548}
549
550binder_status_t BroadcastRadio::cmdHelp(int fd) const {
551 dprintf(fd, "Usage: \n\n");
552 dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
553 dprintf(fd, "--help: shows this help\n");
554 dprintf(fd,
555 "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
556 "frequency (int) \n"
557 "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
558 "sidExt (int), ensemble (int) \n");
559 dprintf(fd,
560 "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
561 "option whether skipping sub channel: "
562 "skipSubChannel (string, should be either \"true\" or \"false\")\n");
563 dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
564 dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
565 dprintf(fd,
566 "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
567 "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
568 "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
569 "where TYPE is int), "
570 "program identifiers (string, in format "
571 "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
572 "where TYPE is int and VALUE is long), "
573 "includeCategories (string, should be either \"true\" or \"false\"), "
574 "excludeModifications (string, should be either \"true\" or \"false\")\n");
575 dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
576 dprintf(fd,
577 "Note on <TYPE> for --startProgramList command: it is int for identifier type. "
578 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
579 "for its definition.\n");
580 dprintf(fd,
581 "Note on <VALUE> for --startProgramList command: it is long type for identifier value. "
582 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
583 "for its value.\n");
584
585 return STATUS_OK;
586}
587
588binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
589 if (!checkDumpCallerHasWritePermissions(fd)) {
590 return STATUS_PERMISSION_DENIED;
591 }
592 if (numArgs != 3 && numArgs != 4) {
593 dprintf(fd,
594 "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
595 "or --tune dab <SID> <ENSEMBLE>\n");
596 return STATUS_BAD_VALUE;
597 }
598 bool isDab = false;
599 if (EqualsIgnoreCase(string(args[1]), "dab")) {
600 isDab = true;
601 } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
602 dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
603 return STATUS_BAD_VALUE;
604 }
605 ProgramSelector sel = {};
606 if (isDab) {
Weilin Xu64cb9632023-03-15 23:46:43 +0000607 if (numArgs != 5 && numArgs != 3) {
Weilin Xu97203b02022-06-23 00:19:03 +0000608 dprintf(fd,
Weilin Xu0d4207d2022-12-09 00:37:44 +0000609 "Invalid number of arguments: please provide "
Weilin Xu64cb9632023-03-15 23:46:43 +0000610 "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
611 "--tune dab <SID>\n");
Weilin Xu97203b02022-06-23 00:19:03 +0000612 return STATUS_BAD_VALUE;
613 }
614 int sid;
615 if (!utils::parseArgInt(string(args[2]), &sid)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000616 dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000617 return STATUS_BAD_VALUE;
618 }
Weilin Xu64cb9632023-03-15 23:46:43 +0000619 if (numArgs == 3) {
620 sel = utils::makeSelectorDab(sid);
621 } else {
622 int ensemble;
623 if (!utils::parseArgInt(string(args[3]), &ensemble)) {
624 dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
625 return STATUS_BAD_VALUE;
626 }
627 int freq;
628 if (!utils::parseArgInt(string(args[4]), &freq)) {
629 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
630 return STATUS_BAD_VALUE;
631 }
632 sel = utils::makeSelectorDab(sid, ensemble, freq);
Weilin Xu97203b02022-06-23 00:19:03 +0000633 }
Weilin Xu97203b02022-06-23 00:19:03 +0000634 } else {
635 if (numArgs != 3) {
636 dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
637 return STATUS_BAD_VALUE;
638 }
639 int freq;
640 if (!utils::parseArgInt(string(args[2]), &freq)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000641 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000642 return STATUS_BAD_VALUE;
643 }
644 sel = utils::makeSelectorAmfm(freq);
645 }
646
647 auto tuneResult = tune(sel);
648 if (!tuneResult.isOk()) {
649 dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
650 return STATUS_BAD_VALUE;
651 }
652 dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
653 return STATUS_OK;
654}
655
656binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
657 if (!checkDumpCallerHasWritePermissions(fd)) {
658 return STATUS_PERMISSION_DENIED;
659 }
660 if (numArgs != 3) {
661 dprintf(fd,
662 "Invalid number of arguments: please provide --seek <DIRECTION> "
663 "<SKIP_SUB_CHANNEL>\n");
664 return STATUS_BAD_VALUE;
665 }
666 string seekDirectionIn = string(args[1]);
667 bool seekDirectionUp;
668 if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
669 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
670 seekDirectionIn.c_str());
671 return STATUS_BAD_VALUE;
672 }
673 string skipSubChannelIn = string(args[2]);
674 bool skipSubChannel;
675 if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
676 dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
677 skipSubChannelIn.c_str());
678 return STATUS_BAD_VALUE;
679 }
680
681 auto seekResult = seek(seekDirectionUp, skipSubChannel);
682 if (!seekResult.isOk()) {
683 dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
684 return STATUS_BAD_VALUE;
685 }
686 dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
687 return STATUS_OK;
688}
689
690binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
691 if (!checkDumpCallerHasWritePermissions(fd)) {
692 return STATUS_PERMISSION_DENIED;
693 }
694 if (numArgs != 2) {
695 dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
696 return STATUS_BAD_VALUE;
697 }
698 string stepDirectionIn = string(args[1]);
699 bool stepDirectionUp;
700 if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
701 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
702 stepDirectionIn.c_str());
703 return STATUS_BAD_VALUE;
704 }
705
706 auto stepResult = step(stepDirectionUp);
707 if (!stepResult.isOk()) {
708 dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
709 return STATUS_BAD_VALUE;
710 }
711 dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
712 return STATUS_OK;
713}
714
715binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
716 if (!checkDumpCallerHasWritePermissions(fd)) {
717 return STATUS_PERMISSION_DENIED;
718 }
719 if (numArgs != 1) {
720 dprintf(fd,
721 "Invalid number of arguments: please provide --cancel "
722 "only and no more arguments\n");
723 return STATUS_BAD_VALUE;
724 }
725
726 auto cancelResult = cancel();
727 if (!cancelResult.isOk()) {
728 dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
729 return STATUS_BAD_VALUE;
730 }
731 dprintf(fd, "Canceled pending tune, seek, and step\n");
732 return STATUS_OK;
733}
734
735binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
736 uint32_t numArgs) {
737 if (!checkDumpCallerHasWritePermissions(fd)) {
738 return STATUS_PERMISSION_DENIED;
739 }
740 if (numArgs != 5) {
741 dprintf(fd,
742 "Invalid number of arguments: please provide --startProgramListUpdates "
743 "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
744 "<EXCLUDE_MODIFICATIONS>\n");
745 return STATUS_BAD_VALUE;
746 }
747 string filterTypesStr = string(args[1]);
748 std::vector<IdentifierType> filterTypeList;
749 if (!EqualsIgnoreCase(filterTypesStr, "null") &&
750 !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
751 dprintf(fd,
752 "Invalid identifier types provided with startProgramListUpdates: %s, "
753 "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
754 filterTypesStr.c_str());
755 return STATUS_BAD_VALUE;
756 }
757 string filtersStr = string(args[2]);
758 std::vector<ProgramIdentifier> filterList;
759 if (!EqualsIgnoreCase(filtersStr, "null") &&
760 !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
761 dprintf(fd,
762 "Invalid program identifiers provided with startProgramListUpdates: %s, "
763 "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
764 filtersStr.c_str());
765 return STATUS_BAD_VALUE;
766 }
767 string includeCategoriesStr = string(args[3]);
768 bool includeCategories;
769 if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
770 dprintf(fd,
771 "Invalid includeCategories (\"true\" or \"false\") "
772 "provided with startProgramListUpdates : %s\n",
773 includeCategoriesStr.c_str());
774 return STATUS_BAD_VALUE;
775 }
776 string excludeModificationsStr = string(args[4]);
777 bool excludeModifications;
778 if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
779 dprintf(fd,
780 "Invalid excludeModifications(\"true\" or \"false\") "
781 "provided with startProgramListUpdates : %s\n",
782 excludeModificationsStr.c_str());
783 return STATUS_BAD_VALUE;
784 }
785 ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
786
787 auto updateResult = startProgramListUpdates(filter);
788 if (!updateResult.isOk()) {
789 dprintf(fd, "Unable to start program list update for filter %s \n",
790 filter.toString().c_str());
791 return STATUS_BAD_VALUE;
792 }
793 dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
794 return STATUS_OK;
795}
796
797binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
798 if (!checkDumpCallerHasWritePermissions(fd)) {
799 return STATUS_PERMISSION_DENIED;
800 }
801 if (numArgs != 1) {
802 dprintf(fd,
803 "Invalid number of arguments: please provide --stopProgramListUpdates "
804 "only and no more arguments\n");
805 return STATUS_BAD_VALUE;
806 }
807
808 auto stopResult = stopProgramListUpdates();
809 if (!stopResult.isOk()) {
810 dprintf(fd, "Unable to stop pending program list update\n");
811 return STATUS_BAD_VALUE;
812 }
813 dprintf(fd, "Stop pending program list update\n");
814 return STATUS_OK;
815}
816
Weilin Xub2a6ca62022-05-08 23:47:04 +0000817} // namespace aidl::android::hardware::broadcastradio