blob: c7e1134255766a44dd397079e7a1f871832d9b6b [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;
Weilin Xu39dd0f82023-09-07 17:00:57 -070084 switch (info.selector.primaryId.type) {
85 case IdentifierType::AMFM_FREQUENCY_KHZ:
86 info.logicallyTunedTo = utils::makeIdentifier(
87 IdentifierType::AMFM_FREQUENCY_KHZ,
88 utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
89 info.physicallyTunedTo = info.logicallyTunedTo;
90 break;
91 case IdentifierType::HD_STATION_ID_EXT:
92 info.logicallyTunedTo = utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
93 utils::getAmFmFrequency(info.selector));
94 info.physicallyTunedTo = info.logicallyTunedTo;
95 break;
96 case IdentifierType::DAB_SID_EXT:
97 info.logicallyTunedTo = info.selector.primaryId;
98 info.physicallyTunedTo = utils::makeIdentifier(
99 IdentifierType::DAB_FREQUENCY_KHZ,
100 utils::getId(selector, IdentifierType::DAB_FREQUENCY_KHZ));
101 break;
102 default:
103 info.logicallyTunedTo = info.selector.primaryId;
104 info.physicallyTunedTo = info.logicallyTunedTo;
105 break;
106 }
Weilin Xub2a6ca62022-05-08 23:47:04 +0000107 return info;
108}
109
Weilin Xu97203b02022-06-23 00:19:03 +0000110static bool checkDumpCallerHasWritePermissions(int fd) {
111 uid_t uid = AIBinder_getCallingUid();
112 if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
113 return true;
114 }
115 dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
116 return false;
117}
118
Weilin Xub2a6ca62022-05-08 23:47:04 +0000119} // namespace
120
121BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
122 : mVirtualRadio(virtualRadio),
123 mAmFmConfig(kDefaultAmFmConfig),
124 mProperties(initProperties(virtualRadio)) {
125 const auto& ranges = kDefaultAmFmConfig.ranges;
126 if (ranges.size() > 0) {
127 ProgramSelector sel = utils::makeSelectorAmfm(ranges[0].lowerBound);
128 VirtualProgram virtualProgram = {};
129 if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
130 mCurrentProgram = virtualProgram.selector;
131 } else {
132 mCurrentProgram = sel;
133 }
134 }
135}
136
137BroadcastRadio::~BroadcastRadio() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000138 mTuningThread.reset();
139 mProgramListThread.reset();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000140}
141
142ScopedAStatus BroadcastRadio::getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) {
143 if (full) {
144 *returnConfigs = {};
145 returnConfigs->ranges = vector<AmFmBandRange>({
146 {65000, 108000, 10, 0}, // FM
147 {150, 30000, 1, 0}, // AM
148 });
149 returnConfigs->fmDeemphasis =
150 AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
151 returnConfigs->fmRds = AmFmRegionConfig::RDS | AmFmRegionConfig::RBDS;
152 return ScopedAStatus::ok();
153 }
154 lock_guard<mutex> lk(mMutex);
155 *returnConfigs = mAmFmConfig;
156 return ScopedAStatus::ok();
157}
158
159ScopedAStatus BroadcastRadio::getDabRegionConfig(vector<DabTableEntry>* returnConfigs) {
160 *returnConfigs = {
161 {"5A", 174928}, {"7D", 194064}, {"8A", 195936}, {"8B", 197648}, {"9A", 202928},
162 {"9B", 204640}, {"9C", 206352}, {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
163 {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
164 {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
165 };
166 return ScopedAStatus::ok();
167}
168
169ScopedAStatus BroadcastRadio::getImage(int32_t id, vector<uint8_t>* returnImage) {
170 LOG(DEBUG) << __func__ << ": fetching image " << std::hex << id;
171
172 if (id == resources::kDemoPngId) {
173 *returnImage = vector<uint8_t>(resources::kDemoPng, std::end(resources::kDemoPng));
174 return ScopedAStatus::ok();
175 }
176
177 LOG(WARNING) << __func__ << ": image of id " << std::hex << id << " doesn't exist";
178 *returnImage = {};
179 return ScopedAStatus::ok();
180}
181
182ScopedAStatus BroadcastRadio::getProperties(Properties* returnProperties) {
183 lock_guard<mutex> lk(mMutex);
184 *returnProperties = mProperties;
185 return ScopedAStatus::ok();
186}
187
188ProgramInfo BroadcastRadio::tuneInternalLocked(const ProgramSelector& sel) {
189 LOG(DEBUG) << __func__ << ": tune (internal) to " << sel.toString();
190
191 VirtualProgram virtualProgram = {};
192 ProgramInfo programInfo;
193 if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
194 mCurrentProgram = virtualProgram.selector;
195 programInfo = virtualProgram;
196 } else {
197 mCurrentProgram = sel;
198 programInfo = makeSampleProgramInfo(sel);
199 }
200 mIsTuneCompleted = true;
201
202 return programInfo;
203}
204
205ScopedAStatus BroadcastRadio::setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) {
206 LOG(DEBUG) << __func__ << ": setTunerCallback";
207
208 if (callback == nullptr) {
209 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
210 resultToInt(Result::INVALID_ARGUMENTS), "cannot set tuner callback to null");
211 }
212
213 lock_guard<mutex> lk(mMutex);
214 mCallback = callback;
215
216 return ScopedAStatus::ok();
217}
218
219ScopedAStatus BroadcastRadio::unsetTunerCallback() {
220 LOG(DEBUG) << __func__ << ": unsetTunerCallback";
221
222 lock_guard<mutex> lk(mMutex);
223 mCallback = nullptr;
224
225 return ScopedAStatus::ok();
226}
227
228ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
229 LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
230
231 lock_guard<mutex> lk(mMutex);
232 if (mCallback == nullptr) {
233 LOG(ERROR) << __func__ << ": callback is not registered.";
234 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
235 resultToInt(Result::INVALID_STATE), "callback is not registered");
236 }
237
238 if (!utils::isSupported(mProperties, program)) {
239 LOG(WARNING) << __func__ << ": selector not supported: " << program.toString();
240 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
241 resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
242 }
243
Weilin Xu31c541c2023-09-07 17:00:57 -0700244 if (!utils::isValidV2(program)) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000245 LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
246 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
247 resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
248 }
249
250 cancelLocked();
251
252 mIsTuneCompleted = false;
253 std::shared_ptr<ITunerCallback> callback = mCallback;
254 auto task = [this, program, callback]() {
255 ProgramInfo programInfo = {};
256 {
257 lock_guard<mutex> lk(mMutex);
258 programInfo = tuneInternalLocked(program);
259 }
260 callback->onCurrentProgramInfoChanged(programInfo);
261 };
Weilin Xud7483322022-11-29 01:12:36 +0000262 auto cancelTask = [program, callback]() { callback->onTuneFailed(Result::CANCELED, program); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000263 mTuningThread->schedule(task, cancelTask, kTuneDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000264
265 return ScopedAStatus::ok();
266}
267
Weilin Xu39dd0f82023-09-07 17:00:57 -0700268bool BroadcastRadio::findNextLocked(const ProgramSelector& current, bool directionUp,
269 bool skipSubChannel, VirtualProgram* nextProgram) const {
270 if (mProgramList.empty()) {
271 return false;
272 }
273 // The list is not sorted here since it has already stored in VirtualRadio.
274 bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
275 uint32_t currentFreq = hasAmFmFrequency ? utils::getAmFmFrequency(current) : 0;
276 auto found =
277 std::lower_bound(mProgramList.begin(), mProgramList.end(), VirtualProgram({current}));
278 if (directionUp) {
279 if (found < mProgramList.end() - 1) {
280 // When seeking up, tuner will jump to the first selector which is main program service
281 // greater than the current program selector in the program list (if not exist, jump
282 // to the first selector) for skipping sub-channels case or AM/FM without HD radio
283 // enabled case. Otherwise, the tuner will jump to the first selector greater than the
284 // current program selector.
285 if (utils::tunesTo(current, found->selector)) found++;
286 if (skipSubChannel && hasAmFmFrequency) {
287 auto firstFound = found;
288 while (utils::getAmFmFrequency(found->selector) == currentFreq) {
289 if (found < mProgramList.end() - 1) {
290 found++;
291 } else {
292 found = mProgramList.begin();
293 }
294 if (found == firstFound) {
295 // Only one main channel exists in the program list, the tuner cannot skip
296 // sub-channel to the next program selector.
297 return false;
298 }
299 }
300 }
301 } else {
302 // If the selector of current program is no less than all selectors or not found in the
303 // program list, seeking up should wrap the tuner to the beginning of the program list.
304 found = mProgramList.begin();
305 }
306 } else {
307 if (found > mProgramList.begin() && found != mProgramList.end()) {
308 // When seeking down, tuner will jump to the first selector which is main program
309 // service less than the current program selector in the program list (if not exist,
310 // jump to the last main program service selector) for skipping sub-channels case or
311 // AM/FM without HD radio enabled case. Otherwise, the tuner will jump to the first
312 // selector less than the current program selector.
313 found--;
314 if (hasAmFmFrequency && utils::hasAmFmFrequency(found->selector)) {
315 uint32_t nextFreq = utils::getAmFmFrequency(found->selector);
316 if (nextFreq != currentFreq) {
317 jumpToFirstSubChannelLocked(found);
318 } else if (skipSubChannel) {
319 jumpToFirstSubChannelLocked(found);
320 auto firstFound = found;
321 if (found > mProgramList.begin()) {
322 found--;
323 } else {
324 found = mProgramList.end() - 1;
325 }
326 jumpToFirstSubChannelLocked(found);
327 if (found == firstFound) {
328 // Only one main channel exists in the program list, the tuner cannot skip
329 // sub-channel to the next program selector.
330 return false;
331 }
332 }
333 }
334 } else {
335 // If the selector of current program is no greater than all selectors or not found
336 // in the program list, seeking down should wrap the tuner to the end of the program
337 // list. If the last program selector in the program list is sub-channel and skipping
338 // sub-channels is needed, the tuner will jump to the last main program service in
339 // the program list.
340 found = mProgramList.end() - 1;
341 jumpToFirstSubChannelLocked(found);
342 }
343 }
344 *nextProgram = *found;
345 return true;
346}
347
348void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
349 if (!utils::hasAmFmFrequency(it->selector) || it == mProgramList.begin()) {
350 return;
351 }
352 uint32_t currentFrequency = utils::getAmFmFrequency(it->selector);
353 it--;
354 while (it != mProgramList.begin() && utils::hasAmFmFrequency(it->selector) &&
355 utils::getAmFmFrequency(it->selector) == currentFrequency) {
356 it--;
357 }
358 it++;
359}
360
Weilin Xub2a6ca62022-05-08 23:47:04 +0000361ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
362 LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
363 << (skipSubChannel ? "yes" : "no") << "...";
364
365 lock_guard<mutex> lk(mMutex);
366 if (mCallback == nullptr) {
367 LOG(ERROR) << __func__ << ": callback is not registered.";
368 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
369 resultToInt(Result::INVALID_STATE), "callback is not registered");
370 }
371
372 cancelLocked();
373
Weilin Xu39dd0f82023-09-07 17:00:57 -0700374 mProgramList = mVirtualRadio.getProgramList();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000375 std::shared_ptr<ITunerCallback> callback = mCallback;
Weilin Xud7483322022-11-29 01:12:36 +0000376 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xu39dd0f82023-09-07 17:00:57 -0700377
378 VirtualProgram nextProgram = {};
379 bool foundNext = findNextLocked(mCurrentProgram, directionUp, skipSubChannel, &nextProgram);
380 mIsTuneCompleted = false;
381 if (!foundNext) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000382 auto task = [callback]() {
383 LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
384
385 callback->onTuneFailed(Result::TIMEOUT, {});
386 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000387 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000388
389 return ScopedAStatus::ok();
390 }
391
Weilin Xu39dd0f82023-09-07 17:00:57 -0700392 auto task = [this, nextProgram, callback]() {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000393 ProgramInfo programInfo = {};
394 {
395 lock_guard<mutex> lk(mMutex);
Weilin Xu39dd0f82023-09-07 17:00:57 -0700396 programInfo = tuneInternalLocked(nextProgram.selector);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000397 }
398 callback->onCurrentProgramInfoChanged(programInfo);
399 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000400 mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000401
402 return ScopedAStatus::ok();
403}
404
405ScopedAStatus BroadcastRadio::step(bool directionUp) {
406 LOG(DEBUG) << __func__ << ": step " << (directionUp ? "up" : "down") << "...";
407
408 lock_guard<mutex> lk(mMutex);
409 if (mCallback == nullptr) {
410 LOG(ERROR) << __func__ << ": callback is not registered.";
411 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
412 resultToInt(Result::INVALID_STATE), "callback is not registered");
413 }
414
415 cancelLocked();
416
Weilin Xu39dd0f82023-09-07 17:00:57 -0700417 int64_t stepTo;
418 if (utils::hasId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ)) {
419 stepTo = utils::getId(mCurrentProgram, IdentifierType::AMFM_FREQUENCY_KHZ);
420 } else if (mCurrentProgram.primaryId.type == IdentifierType::HD_STATION_ID_EXT) {
421 stepTo = utils::getHdFrequency(mCurrentProgram);
422 } else {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000423 LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
424 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
425 resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
426 }
427
Weilin Xub2a6ca62022-05-08 23:47:04 +0000428 std::optional<AmFmBandRange> range = getAmFmRangeLocked();
429 if (!range) {
430 LOG(ERROR) << __func__ << ": can't find current band or tune operation is in process";
Weilin Xuee546a62023-11-14 12:57:50 -0800431 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
Weilin Xub2a6ca62022-05-08 23:47:04 +0000432 resultToInt(Result::INTERNAL_ERROR),
433 "can't find current band or tune operation is in process");
434 }
435
436 if (directionUp) {
437 stepTo += range->spacing;
438 } else {
439 stepTo -= range->spacing;
440 }
441 if (stepTo > range->upperBound) {
442 stepTo = range->lowerBound;
443 }
444 if (stepTo < range->lowerBound) {
445 stepTo = range->upperBound;
446 }
447
448 mIsTuneCompleted = false;
449 std::shared_ptr<ITunerCallback> callback = mCallback;
450 auto task = [this, stepTo, callback]() {
451 ProgramInfo programInfo;
452 {
453 lock_guard<mutex> lk(mMutex);
454 programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
455 }
456 callback->onCurrentProgramInfoChanged(programInfo);
457 };
Weilin Xud7483322022-11-29 01:12:36 +0000458 auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000459 mTuningThread->schedule(task, cancelTask, kStepDelayTimeMs);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000460
461 return ScopedAStatus::ok();
462}
463
464void BroadcastRadio::cancelLocked() {
Weilin Xu764fe0d2023-07-26 18:07:05 +0000465 LOG(DEBUG) << __func__ << ": cancelling current tuning operations...";
Weilin Xub2a6ca62022-05-08 23:47:04 +0000466
Weilin Xu764fe0d2023-07-26 18:07:05 +0000467 mTuningThread->cancelAll();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000468 if (mCurrentProgram.primaryId.type != IdentifierType::INVALID) {
469 mIsTuneCompleted = true;
470 }
471}
472
473ScopedAStatus BroadcastRadio::cancel() {
474 LOG(DEBUG) << __func__ << ": cancel pending tune, seek and step...";
475
476 lock_guard<mutex> lk(mMutex);
477 cancelLocked();
478
479 return ScopedAStatus::ok();
480}
481
482ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
483 LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
484 << "...";
485
486 auto filterCb = [&filter](const VirtualProgram& program) {
487 return utils::satisfies(filter, program.selector);
488 };
489
490 lock_guard<mutex> lk(mMutex);
491
Weilin Xu764fe0d2023-07-26 18:07:05 +0000492 cancelProgramListUpdateLocked();
493
Weilin Xub2a6ca62022-05-08 23:47:04 +0000494 const auto& list = mVirtualRadio.getProgramList();
495 vector<VirtualProgram> filteredList;
496 std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
497
498 auto task = [this, filteredList]() {
499 std::shared_ptr<ITunerCallback> callback;
500 {
501 lock_guard<mutex> lk(mMutex);
502 if (mCallback == nullptr) {
503 LOG(WARNING) << "Callback is null when updating program List";
504 return;
505 }
506 callback = mCallback;
507 }
508
509 ProgramListChunk chunk = {};
510 chunk.purge = true;
511 chunk.complete = true;
512 chunk.modified = vector<ProgramInfo>(filteredList.begin(), filteredList.end());
513
514 callback->onProgramListUpdated(chunk);
515 };
Weilin Xu764fe0d2023-07-26 18:07:05 +0000516 mProgramListThread->schedule(task, kListDelayTimeS);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000517
518 return ScopedAStatus::ok();
519}
520
Weilin Xu764fe0d2023-07-26 18:07:05 +0000521void BroadcastRadio::cancelProgramListUpdateLocked() {
522 LOG(DEBUG) << __func__ << ": cancelling current program list update operations...";
523 mProgramListThread->cancelAll();
524}
525
Weilin Xub2a6ca62022-05-08 23:47:04 +0000526ScopedAStatus BroadcastRadio::stopProgramListUpdates() {
527 LOG(DEBUG) << __func__ << ": requested program list updates to stop...";
Weilin Xu764fe0d2023-07-26 18:07:05 +0000528 lock_guard<mutex> lk(mMutex);
529 cancelProgramListUpdateLocked();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000530 return ScopedAStatus::ok();
531}
532
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000533ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000534 LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
535
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000536 int flagBit = static_cast<int>(flag);
537 lock_guard<mutex> lk(mMutex);
538 *returnIsSet = ((mConfigFlagValues >> flagBit) & 1) == 1;
539 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000540}
541
542ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
543 LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
544
Weilin Xu3bd4d9b2023-07-19 00:38:57 +0000545 int flagBitMask = 1 << (static_cast<int>(flag));
546 lock_guard<mutex> lk(mMutex);
547 if (value) {
548 mConfigFlagValues |= flagBitMask;
549 } else {
550 mConfigFlagValues &= ~flagBitMask;
551 }
552 return ScopedAStatus::ok();
Weilin Xub2a6ca62022-05-08 23:47:04 +0000553}
554
555ScopedAStatus BroadcastRadio::setParameters(
556 [[maybe_unused]] const vector<VendorKeyValue>& parameters,
557 vector<VendorKeyValue>* returnParameters) {
558 // TODO(b/243682330) Support vendor parameter functionality
559 *returnParameters = {};
560 return ScopedAStatus::ok();
561}
562
563ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
564 vector<VendorKeyValue>* returnParameters) {
565 // TODO(b/243682330) Support vendor parameter functionality
566 *returnParameters = {};
567 return ScopedAStatus::ok();
568}
569
570std::optional<AmFmBandRange> BroadcastRadio::getAmFmRangeLocked() const {
571 if (!mIsTuneCompleted) {
572 LOG(WARNING) << __func__ << ": tune operation is in process";
573 return {};
574 }
Weilin Xu39dd0f82023-09-07 17:00:57 -0700575 if (!utils::hasAmFmFrequency(mCurrentProgram)) {
Weilin Xub2a6ca62022-05-08 23:47:04 +0000576 LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
577 return {};
578 }
579
Weilin Xu39dd0f82023-09-07 17:00:57 -0700580 int64_t freq = utils::getAmFmFrequency(mCurrentProgram);
Weilin Xub2a6ca62022-05-08 23:47:04 +0000581 for (const auto& range : mAmFmConfig.ranges) {
582 if (range.lowerBound <= freq && range.upperBound >= freq) {
583 return range;
584 }
585 }
586
587 return {};
588}
589
590ScopedAStatus BroadcastRadio::registerAnnouncementListener(
591 [[maybe_unused]] const std::shared_ptr<IAnnouncementListener>& listener,
592 const vector<AnnouncementType>& enabled, std::shared_ptr<ICloseHandle>* returnCloseHandle) {
593 LOG(DEBUG) << __func__ << ": registering announcement listener for "
594 << utils::vectorToString(enabled);
595
596 // TODO(b/243683842) Support announcement listener
597 *returnCloseHandle = nullptr;
598 LOG(INFO) << __func__ << ": registering announcementListener is not supported";
599 return ScopedAStatus::fromServiceSpecificErrorWithMessage(
600 resultToInt(Result::NOT_SUPPORTED),
601 "registering announcementListener is not supported");
602}
603
Weilin Xu97203b02022-06-23 00:19:03 +0000604binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
605 if (numArgs == 0) {
606 return dumpsys(fd);
607 }
608
609 string option = string(args[0]);
610 if (EqualsIgnoreCase(option, "--help")) {
611 return cmdHelp(fd);
612 } else if (EqualsIgnoreCase(option, "--tune")) {
613 return cmdTune(fd, args, numArgs);
614 } else if (EqualsIgnoreCase(option, "--seek")) {
615 return cmdSeek(fd, args, numArgs);
616 } else if (EqualsIgnoreCase(option, "--step")) {
617 return cmdStep(fd, args, numArgs);
618 } else if (EqualsIgnoreCase(option, "--cancel")) {
619 return cmdCancel(fd, numArgs);
620 } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
621 return cmdStartProgramListUpdates(fd, args, numArgs);
622 } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
623 return cmdStopProgramListUpdates(fd, numArgs);
624 }
625 dprintf(fd, "Invalid option: %s\n", option.c_str());
626 return STATUS_BAD_VALUE;
627}
628
629binder_status_t BroadcastRadio::dumpsys(int fd) {
630 if (!checkDumpCallerHasWritePermissions(fd)) {
631 return STATUS_PERMISSION_DENIED;
632 }
633 lock_guard<mutex> lk(mMutex);
634 dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
635 dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
636 if (mIsTuneCompleted) {
637 dprintf(fd, "Tune completed\n");
638 } else {
639 dprintf(fd, "Tune not completed\n");
640 }
641 if (mCallback == nullptr) {
642 dprintf(fd, "No ITunerCallback registered\n");
643 } else {
644 dprintf(fd, "ITunerCallback registered\n");
645 }
646 dprintf(fd, "CurrentProgram: %s \n", mCurrentProgram.toString().c_str());
647 return STATUS_OK;
648}
649
650binder_status_t BroadcastRadio::cmdHelp(int fd) const {
651 dprintf(fd, "Usage: \n\n");
652 dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
653 dprintf(fd, "--help: shows this help\n");
654 dprintf(fd,
655 "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
656 "frequency (int) \n"
657 "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
658 "sidExt (int), ensemble (int) \n");
659 dprintf(fd,
660 "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
661 "option whether skipping sub channel: "
662 "skipSubChannel (string, should be either \"true\" or \"false\")\n");
663 dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
664 dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
665 dprintf(fd,
666 "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
667 "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
668 "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
669 "where TYPE is int), "
670 "program identifiers (string, in format "
671 "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
672 "where TYPE is int and VALUE is long), "
673 "includeCategories (string, should be either \"true\" or \"false\"), "
674 "excludeModifications (string, should be either \"true\" or \"false\")\n");
675 dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
676 dprintf(fd,
677 "Note on <TYPE> for --startProgramList command: it is int for identifier type. "
678 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
679 "for its definition.\n");
680 dprintf(fd,
681 "Note on <VALUE> for --startProgramList command: it is long type for identifier value. "
682 "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
683 "for its value.\n");
684
685 return STATUS_OK;
686}
687
688binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
689 if (!checkDumpCallerHasWritePermissions(fd)) {
690 return STATUS_PERMISSION_DENIED;
691 }
692 if (numArgs != 3 && numArgs != 4) {
693 dprintf(fd,
694 "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
695 "or --tune dab <SID> <ENSEMBLE>\n");
696 return STATUS_BAD_VALUE;
697 }
698 bool isDab = false;
699 if (EqualsIgnoreCase(string(args[1]), "dab")) {
700 isDab = true;
701 } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
702 dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
703 return STATUS_BAD_VALUE;
704 }
705 ProgramSelector sel = {};
706 if (isDab) {
Weilin Xu64cb9632023-03-15 23:46:43 +0000707 if (numArgs != 5 && numArgs != 3) {
Weilin Xu97203b02022-06-23 00:19:03 +0000708 dprintf(fd,
Weilin Xu0d4207d2022-12-09 00:37:44 +0000709 "Invalid number of arguments: please provide "
Weilin Xu64cb9632023-03-15 23:46:43 +0000710 "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
711 "--tune dab <SID>\n");
Weilin Xu97203b02022-06-23 00:19:03 +0000712 return STATUS_BAD_VALUE;
713 }
714 int sid;
715 if (!utils::parseArgInt(string(args[2]), &sid)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000716 dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000717 return STATUS_BAD_VALUE;
718 }
Weilin Xu64cb9632023-03-15 23:46:43 +0000719 if (numArgs == 3) {
720 sel = utils::makeSelectorDab(sid);
721 } else {
722 int ensemble;
723 if (!utils::parseArgInt(string(args[3]), &ensemble)) {
724 dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
725 return STATUS_BAD_VALUE;
726 }
727 int freq;
728 if (!utils::parseArgInt(string(args[4]), &freq)) {
729 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
730 return STATUS_BAD_VALUE;
731 }
732 sel = utils::makeSelectorDab(sid, ensemble, freq);
Weilin Xu97203b02022-06-23 00:19:03 +0000733 }
Weilin Xu97203b02022-06-23 00:19:03 +0000734 } else {
735 if (numArgs != 3) {
736 dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
737 return STATUS_BAD_VALUE;
738 }
739 int freq;
740 if (!utils::parseArgInt(string(args[2]), &freq)) {
Weilin Xu0d4207d2022-12-09 00:37:44 +0000741 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[2]);
Weilin Xu97203b02022-06-23 00:19:03 +0000742 return STATUS_BAD_VALUE;
743 }
744 sel = utils::makeSelectorAmfm(freq);
745 }
746
747 auto tuneResult = tune(sel);
748 if (!tuneResult.isOk()) {
749 dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
750 return STATUS_BAD_VALUE;
751 }
752 dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
753 return STATUS_OK;
754}
755
756binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
757 if (!checkDumpCallerHasWritePermissions(fd)) {
758 return STATUS_PERMISSION_DENIED;
759 }
760 if (numArgs != 3) {
761 dprintf(fd,
762 "Invalid number of arguments: please provide --seek <DIRECTION> "
763 "<SKIP_SUB_CHANNEL>\n");
764 return STATUS_BAD_VALUE;
765 }
766 string seekDirectionIn = string(args[1]);
767 bool seekDirectionUp;
768 if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
769 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
770 seekDirectionIn.c_str());
771 return STATUS_BAD_VALUE;
772 }
773 string skipSubChannelIn = string(args[2]);
774 bool skipSubChannel;
775 if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
776 dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
777 skipSubChannelIn.c_str());
778 return STATUS_BAD_VALUE;
779 }
780
781 auto seekResult = seek(seekDirectionUp, skipSubChannel);
782 if (!seekResult.isOk()) {
783 dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
784 return STATUS_BAD_VALUE;
785 }
786 dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
787 return STATUS_OK;
788}
789
790binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
791 if (!checkDumpCallerHasWritePermissions(fd)) {
792 return STATUS_PERMISSION_DENIED;
793 }
794 if (numArgs != 2) {
795 dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
796 return STATUS_BAD_VALUE;
797 }
798 string stepDirectionIn = string(args[1]);
799 bool stepDirectionUp;
800 if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
801 dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
802 stepDirectionIn.c_str());
803 return STATUS_BAD_VALUE;
804 }
805
806 auto stepResult = step(stepDirectionUp);
807 if (!stepResult.isOk()) {
808 dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
809 return STATUS_BAD_VALUE;
810 }
811 dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
812 return STATUS_OK;
813}
814
815binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
816 if (!checkDumpCallerHasWritePermissions(fd)) {
817 return STATUS_PERMISSION_DENIED;
818 }
819 if (numArgs != 1) {
820 dprintf(fd,
821 "Invalid number of arguments: please provide --cancel "
822 "only and no more arguments\n");
823 return STATUS_BAD_VALUE;
824 }
825
826 auto cancelResult = cancel();
827 if (!cancelResult.isOk()) {
828 dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
829 return STATUS_BAD_VALUE;
830 }
831 dprintf(fd, "Canceled pending tune, seek, and step\n");
832 return STATUS_OK;
833}
834
835binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
836 uint32_t numArgs) {
837 if (!checkDumpCallerHasWritePermissions(fd)) {
838 return STATUS_PERMISSION_DENIED;
839 }
840 if (numArgs != 5) {
841 dprintf(fd,
842 "Invalid number of arguments: please provide --startProgramListUpdates "
843 "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
844 "<EXCLUDE_MODIFICATIONS>\n");
845 return STATUS_BAD_VALUE;
846 }
847 string filterTypesStr = string(args[1]);
848 std::vector<IdentifierType> filterTypeList;
849 if (!EqualsIgnoreCase(filterTypesStr, "null") &&
850 !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
851 dprintf(fd,
852 "Invalid identifier types provided with startProgramListUpdates: %s, "
853 "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
854 filterTypesStr.c_str());
855 return STATUS_BAD_VALUE;
856 }
857 string filtersStr = string(args[2]);
858 std::vector<ProgramIdentifier> filterList;
859 if (!EqualsIgnoreCase(filtersStr, "null") &&
860 !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
861 dprintf(fd,
862 "Invalid program identifiers provided with startProgramListUpdates: %s, "
863 "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
864 filtersStr.c_str());
865 return STATUS_BAD_VALUE;
866 }
867 string includeCategoriesStr = string(args[3]);
868 bool includeCategories;
869 if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
870 dprintf(fd,
871 "Invalid includeCategories (\"true\" or \"false\") "
872 "provided with startProgramListUpdates : %s\n",
873 includeCategoriesStr.c_str());
874 return STATUS_BAD_VALUE;
875 }
876 string excludeModificationsStr = string(args[4]);
877 bool excludeModifications;
878 if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
879 dprintf(fd,
880 "Invalid excludeModifications(\"true\" or \"false\") "
881 "provided with startProgramListUpdates : %s\n",
882 excludeModificationsStr.c_str());
883 return STATUS_BAD_VALUE;
884 }
885 ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
886
887 auto updateResult = startProgramListUpdates(filter);
888 if (!updateResult.isOk()) {
889 dprintf(fd, "Unable to start program list update for filter %s \n",
890 filter.toString().c_str());
891 return STATUS_BAD_VALUE;
892 }
893 dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
894 return STATUS_OK;
895}
896
897binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
898 if (!checkDumpCallerHasWritePermissions(fd)) {
899 return STATUS_PERMISSION_DENIED;
900 }
901 if (numArgs != 1) {
902 dprintf(fd,
903 "Invalid number of arguments: please provide --stopProgramListUpdates "
904 "only and no more arguments\n");
905 return STATUS_BAD_VALUE;
906 }
907
908 auto stopResult = stopProgramListUpdates();
909 if (!stopResult.isOk()) {
910 dprintf(fd, "Unable to stop pending program list update\n");
911 return STATUS_BAD_VALUE;
912 }
913 dprintf(fd, "Stop pending program list update\n");
914 return STATUS_OK;
915}
916
Weilin Xub2a6ca62022-05-08 23:47:04 +0000917} // namespace aidl::android::hardware::broadcastradio