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