xref: /aosp_15_r20/hardware/interfaces/broadcastradio/aidl/default/BroadcastRadio.cpp (revision 4d7e907c777eeecc4c5bd7cf640a754fac206ff7)
1 /*
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 <broadcastradio-utils-aidl/UtilsV2.h>
20 #include <broadcastradio-utils-aidl/UtilsV3.h>
21 #include "resources.h"
22 
23 #include <android-base/logging.h>
24 #include <android-base/strings.h>
25 
26 #include <private/android_filesystem_config.h>
27 
28 namespace aidl::android::hardware::broadcastradio {
29 
30 using ::aidl::android::hardware::broadcastradio::utils::resultToInt;
31 using ::aidl::android::hardware::broadcastradio::utils::tunesTo;
32 using ::android::base::EqualsIgnoreCase;
33 using ::ndk::ScopedAStatus;
34 using ::std::literals::chrono_literals::operator""ms;
35 using ::std::literals::chrono_literals::operator""s;
36 using ::std::lock_guard;
37 using ::std::mutex;
38 using ::std::string;
39 using ::std::vector;
40 
41 namespace {
42 
43 inline constexpr std::chrono::milliseconds kSeekDelayTimeMs = 200ms;
44 inline constexpr std::chrono::milliseconds kStepDelayTimeMs = 100ms;
45 inline constexpr std::chrono::milliseconds kTuneDelayTimeMs = 150ms;
46 inline constexpr std::chrono::seconds kListDelayTimeS = 1s;
47 
48 const string kAlertAreaDelimiter = "+";
49 const string kAlertCoordinateGeocodeDelimiter = ",";
50 // clang-format off
51 const AmFmBandRange kFmFullBandRange = {65000, 108000, 10, 0};
52 const AmFmBandRange kAmFullBandRange = {150, 30000, 1, 0};
53 const AmFmRegionConfig kDefaultAmFmConfig = {
54         {
55                 {87500, 108000, 100, 100},  // FM
56                 {153, 282, 3, 9},           // AM LW
57                 {531, 1620, 9, 9},          // AM MW
58                 {1600, 30000, 1, 5},        // AM SW
59         },
60         AmFmRegionConfig::DEEMPHASIS_D50,
61         AmFmRegionConfig::RDS};
62 // clang-format on
63 
initProperties(const VirtualRadio & virtualRadio)64 Properties initProperties(const VirtualRadio& virtualRadio) {
65     Properties prop = {};
66 
67     prop.maker = "Android";
68     prop.product = virtualRadio.getName();
69     prop.supportedIdentifierTypes = virtualRadio.getSupportedIdentifierTypes();
70     prop.vendorInfo = vector<VendorKeyValue>({
71             {"com.android.sample", "sample"},
72     });
73 
74     return prop;
75 }
76 
isDigitalProgramAllowed(const ProgramSelector & sel,bool forceAnalogFm,bool forceAnalogAm)77 bool isDigitalProgramAllowed(const ProgramSelector& sel, bool forceAnalogFm, bool forceAnalogAm) {
78     if (sel.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
79         return true;
80     }
81     int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
82     bool isFm = freq >= kFmFullBandRange.lowerBound && freq <= kFmFullBandRange.upperBound;
83     return isFm ? !forceAnalogFm : !forceAnalogAm;
84 }
85 
86 /**
87  * Checks whether a program selector is in the current band.
88  *
89  * <p>For an AM/FM program, this method checks whether it is in the current AM/FM band. For a
90  * program selector is also an HD program, it is also checked whether HD radio is enabled in the
91  * current AM/FM band. For a non-AM/FM program, the method will returns {@code true} directly.
92  * @param sel Program selector to be checked
93  * @param currentAmFmBandRange the current AM/FM band
94  * @param forceAnalogFm whether FM band is forced to be analog
95  * @param forceAnalogAm  whether AM band is forced to be analog
96  * @return whether the program selector is in the current band if it is an AM/FM (including HD)
97  * selector, {@code true} otherwise
98  */
isProgramInBand(const ProgramSelector & sel,const std::optional<AmFmBandRange> & currentAmFmBandRange,bool forceAnalogFm,bool forceAnalogAm)99 bool isProgramInBand(const ProgramSelector& sel,
100                      const std::optional<AmFmBandRange>& currentAmFmBandRange, bool forceAnalogFm,
101                      bool forceAnalogAm) {
102     if (!utils::hasAmFmFrequency(sel)) {
103         return true;
104     }
105     if (!currentAmFmBandRange.has_value()) {
106         return false;
107     }
108     int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(sel));
109     if (freq < currentAmFmBandRange->lowerBound || freq > currentAmFmBandRange->upperBound) {
110         return false;
111     }
112     return isDigitalProgramAllowed(sel, forceAnalogFm, forceAnalogAm);
113 }
114 
115 // Makes ProgramInfo that does not point to any particular program
makeSampleProgramInfo(const ProgramSelector & selector)116 ProgramInfo makeSampleProgramInfo(const ProgramSelector& selector) {
117     ProgramInfo info = {};
118     info.selector = selector;
119     switch (info.selector.primaryId.type) {
120         case IdentifierType::AMFM_FREQUENCY_KHZ:
121             info.logicallyTunedTo = utils::makeIdentifier(
122                     IdentifierType::AMFM_FREQUENCY_KHZ,
123                     utils::getId(selector, IdentifierType::AMFM_FREQUENCY_KHZ));
124             info.physicallyTunedTo = info.logicallyTunedTo;
125             break;
126         case IdentifierType::HD_STATION_ID_EXT:
127             info.logicallyTunedTo = utils::makeIdentifier(IdentifierType::AMFM_FREQUENCY_KHZ,
128                                                           utils::getAmFmFrequency(info.selector));
129             info.physicallyTunedTo = info.logicallyTunedTo;
130             break;
131         case IdentifierType::DAB_SID_EXT:
132             info.logicallyTunedTo = info.selector.primaryId;
133             info.physicallyTunedTo = utils::makeIdentifier(
134                     IdentifierType::DAB_FREQUENCY_KHZ,
135                     utils::getId(selector, IdentifierType::DAB_FREQUENCY_KHZ));
136             break;
137         default:
138             info.logicallyTunedTo = info.selector.primaryId;
139             info.physicallyTunedTo = info.logicallyTunedTo;
140             break;
141     }
142     return info;
143 }
144 
createSampleAlert()145 static Alert createSampleAlert() {
146     Polygon polygon = {{{-38.47, -120.14},
147                         {38.34, -119.95},
148                         {38.52, -119.74},
149                         {38.62, -119.89},
150                         {-38.47, -120.14}}};
151     AlertArea alertArea1 = {{polygon}, {{"SAME", "006109"}, {"SAME", "006209"}}};
152     AlertArea alertArea2 = {{}, {{"SAME", "006009"}}};
153     AlertInfo alertInfo;
154     alertInfo.categoryArray = {AlertCategory::GEO, AlertCategory::TRANSPORT};
155     alertInfo.urgency = AlertUrgency::FUTURE;
156     alertInfo.severity = AlertSeverity::SEVERE;
157     alertInfo.certainty = AlertCertainty::POSSIBLE;
158     alertInfo.description = "Sample radio alert.";
159     alertInfo.language = "en-US";
160     alertInfo.areas.push_back(alertArea1);
161     alertInfo.areas.push_back(alertArea2);
162     Alert alert;
163     alert.status = AlertStatus::ACTUAL;
164     alert.messageType = AlertMessageType::ALERT;
165     alert.infoArray.push_back(alertInfo);
166     return alert;
167 }
168 
checkDumpCallerHasWritePermissions(int fd)169 static bool checkDumpCallerHasWritePermissions(int fd) {
170     uid_t uid = AIBinder_getCallingUid();
171     if (uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM) {
172         return true;
173     }
174     dprintf(fd, "BroadcastRadio HAL dump must be root, shell or system\n");
175     return false;
176 }
177 
parseGeocode(int fd,const string & geocodeString,Geocode & parsedGeocode)178 static bool parseGeocode(int fd, const string& geocodeString, Geocode& parsedGeocode) {
179     vector<string> geocodeStringPair =
180             ::android::base::Split(geocodeString, kAlertCoordinateGeocodeDelimiter);
181     if (geocodeStringPair.size() != 2) {
182         dprintf(fd, "Geocode is not of \"VALUE_NAME,VALUE\" format: %s\n", geocodeString.c_str());
183         return false;
184     }
185     parsedGeocode.valueName = geocodeStringPair[0];
186     parsedGeocode.value = geocodeStringPair[1];
187     return true;
188 }
189 
parsePolygon(int fd,const string & polygonString,Polygon & parsedPolygon)190 static bool parsePolygon(int fd, const string& polygonString, Polygon& parsedPolygon) {
191     vector<Coordinate> coordinates;
192     vector<string> coordinateStrings =
193             ::android::base::Split(polygonString, kAlertCoordinateGeocodeDelimiter);
194     if (coordinateStrings.size() % 2) {
195         dprintf(fd, "Incomplete \"LATITUDE,LONGITUDE\" coordinate pairs separated by \",\": %s\n",
196                 polygonString.c_str());
197         return false;
198     }
199     for (size_t i = 0; i < coordinateStrings.size(); i += 2) {
200         double latitude;
201         double longitude;
202         if (!utils::parseArgDouble(coordinateStrings[i], &latitude) ||
203             !utils::parseArgDouble(coordinateStrings[i + 1], &longitude)) {
204             dprintf(fd, "Value of \"LATITUDE,LONGITUDE\" coordinate pair is not double-type: %s\n",
205                     coordinateStrings[i].c_str());
206             return false;
207         }
208         coordinates.push_back(Coordinate(latitude, longitude));
209     }
210     parsedPolygon.coordinates = coordinates;
211     return true;
212 }
213 
parseAreaString(int fd,const string & areaString,AlertArea & parsedAlertArea)214 static bool parseAreaString(int fd, const string& areaString, AlertArea& parsedAlertArea) {
215     vector<string> areaEntryStrings = ::android::base::Split(areaString, "_");
216     for (const auto& areaEntryString : areaEntryStrings) {
217         vector<string> areaTypeValuePair = ::android::base::Split(areaEntryString, ":");
218         if (areaTypeValuePair.size() != 2) {
219             dprintf(fd, "Area is not of \"<TYPE>:<VALUE>\" format: %s\n", areaEntryString.c_str());
220             return false;
221         }
222         if (EqualsIgnoreCase(areaTypeValuePair[0], "polygon")) {
223             Polygon parsedPolygon;
224             if (!parsePolygon(fd, areaTypeValuePair[1], parsedPolygon)) {
225                 return false;
226             }
227             parsedAlertArea.polygons.push_back(parsedPolygon);
228         } else if (EqualsIgnoreCase(areaTypeValuePair[0], "geocode")) {
229             Geocode parsedGeocode;
230             if (!parseGeocode(fd, areaTypeValuePair[1], parsedGeocode)) {
231                 return false;
232             }
233             parsedAlertArea.geocodes.push_back(parsedGeocode);
234         } else {
235             dprintf(fd, "Invalid area <TYPE> other than \"polygon\" and \"geocode\": %s\n",
236                     areaTypeValuePair[0].c_str());
237             return false;
238         }
239     }
240     return true;
241 }
242 
parseAreaListString(int fd,const string & areaListString,vector<AlertArea> & parsedAlertAreas)243 static bool parseAreaListString(int fd, const string& areaListString,
244                                 vector<AlertArea>& parsedAlertAreas) {
245     if (EqualsIgnoreCase(areaListString, kAlertAreaDelimiter)) {
246         return true;
247     }
248     vector<string> areaStrings = ::android::base::Split(areaListString, kAlertAreaDelimiter);
249     for (const auto& areaString : areaStrings) {
250         AlertArea parsedArea;
251         if (!parseAreaString(fd, areaString, parsedArea)) {
252             return false;
253         }
254         parsedAlertAreas.push_back(parsedArea);
255     }
256     return true;
257 }
258 
259 }  // namespace
260 
BroadcastRadio(const VirtualRadio & virtualRadio)261 BroadcastRadio::BroadcastRadio(const VirtualRadio& virtualRadio)
262     : mVirtualRadio(virtualRadio),
263       mAmFmConfig(kDefaultAmFmConfig),
264       mProperties(initProperties(virtualRadio)) {
265     const auto& ranges = kDefaultAmFmConfig.ranges;
266     if (ranges.size() > 0) {
267         ProgramSelector sel = utils::makeSelectorAmfm(ranges[0].lowerBound);
268         VirtualProgram virtualProgram = {};
269         if (mVirtualRadio.getProgram(sel, &virtualProgram)) {
270             mCurrentProgramSelector = virtualProgram.selector;
271         } else {
272             mCurrentProgramSelector = sel;
273         }
274         adjustAmFmRangeLocked();
275     }
276 }
277 
~BroadcastRadio()278 BroadcastRadio::~BroadcastRadio() {
279     mTuningThread.reset();
280     mProgramListThread.reset();
281 }
282 
getAmFmRegionConfig(bool full,AmFmRegionConfig * returnConfigs)283 ScopedAStatus BroadcastRadio::getAmFmRegionConfig(bool full, AmFmRegionConfig* returnConfigs) {
284     if (full) {
285         *returnConfigs = {};
286         returnConfigs->ranges = vector<AmFmBandRange>({
287                 kFmFullBandRange,
288                 kAmFullBandRange,
289         });
290         returnConfigs->fmDeemphasis =
291                 AmFmRegionConfig::DEEMPHASIS_D50 | AmFmRegionConfig::DEEMPHASIS_D75;
292         returnConfigs->fmRds = AmFmRegionConfig::RDS | AmFmRegionConfig::RBDS;
293         return ScopedAStatus::ok();
294     }
295     lock_guard<mutex> lk(mMutex);
296     *returnConfigs = mAmFmConfig;
297     return ScopedAStatus::ok();
298 }
299 
getDabRegionConfig(vector<DabTableEntry> * returnConfigs)300 ScopedAStatus BroadcastRadio::getDabRegionConfig(vector<DabTableEntry>* returnConfigs) {
301     *returnConfigs = {
302             {"5A", 174928},  {"7D", 194064},  {"8A", 195936},  {"8B", 197648},  {"9A", 202928},
303             {"9B", 204640},  {"9C", 206352},  {"10B", 211648}, {"10C", 213360}, {"10D", 215072},
304             {"11A", 216928}, {"11B", 218640}, {"11C", 220352}, {"11D", 222064}, {"12A", 223936},
305             {"12B", 225648}, {"12C", 227360}, {"12D", 229072},
306     };
307     return ScopedAStatus::ok();
308 }
309 
getImage(int32_t id,vector<uint8_t> * returnImage)310 ScopedAStatus BroadcastRadio::getImage(int32_t id, vector<uint8_t>* returnImage) {
311     LOG(DEBUG) << __func__ << ": fetching image " << std::hex << id;
312 
313     if (id == resources::kDemoPngId) {
314         *returnImage = vector<uint8_t>(resources::kDemoPng, std::end(resources::kDemoPng));
315         return ScopedAStatus::ok();
316     }
317 
318     LOG(WARNING) << __func__ << ": image of id " << std::hex << id << " doesn't exist";
319     *returnImage = {};
320     return ScopedAStatus::ok();
321 }
322 
getProperties(Properties * returnProperties)323 ScopedAStatus BroadcastRadio::getProperties(Properties* returnProperties) {
324     lock_guard<mutex> lk(mMutex);
325     *returnProperties = mProperties;
326     return ScopedAStatus::ok();
327 }
328 
tuneInternalLocked(const ProgramSelector & sel)329 ProgramInfo BroadcastRadio::tuneInternalLocked(const ProgramSelector& sel) {
330     LOG(DEBUG) << __func__ << ": tune (internal) to " << sel.toString();
331 
332     VirtualProgram virtualProgram = {};
333     ProgramInfo programInfo;
334     bool isProgramAllowed =
335             isDigitalProgramAllowed(sel, isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
336                                     isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
337     if (isProgramAllowed && mVirtualRadio.getProgram(sel, &virtualProgram)) {
338         mCurrentProgramSelector = virtualProgram.selector;
339         programInfo = virtualProgram;
340     } else {
341         if (!isProgramAllowed) {
342             mCurrentProgramSelector = utils::makeSelectorAmfm(utils::getAmFmFrequency(sel));
343         } else {
344             mCurrentProgramSelector = sel;
345         }
346         programInfo = makeSampleProgramInfo(sel);
347     }
348     programInfo.infoFlags |= ProgramInfo::FLAG_SIGNAL_ACQUISITION;
349     if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
350         mIsTuneCompleted = true;
351     }
352     if (adjustAmFmRangeLocked()) {
353         startProgramListUpdatesLocked({});
354     }
355 
356     return programInfo;
357 }
358 
setTunerCallback(const std::shared_ptr<ITunerCallback> & callback)359 ScopedAStatus BroadcastRadio::setTunerCallback(const std::shared_ptr<ITunerCallback>& callback) {
360     LOG(DEBUG) << __func__ << ": setTunerCallback";
361 
362     if (callback == nullptr) {
363         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
364                 resultToInt(Result::INVALID_ARGUMENTS), "cannot set tuner callback to null");
365     }
366 
367     lock_guard<mutex> lk(mMutex);
368     mCallback = callback;
369 
370     return ScopedAStatus::ok();
371 }
372 
unsetTunerCallback()373 ScopedAStatus BroadcastRadio::unsetTunerCallback() {
374     LOG(DEBUG) << __func__ << ": unsetTunerCallback";
375 
376     lock_guard<mutex> lk(mMutex);
377     mCallback = nullptr;
378 
379     return ScopedAStatus::ok();
380 }
381 
handleProgramInfoUpdateRadioCallback(ProgramInfo programInfo,const std::shared_ptr<ITunerCallback> & callback)382 void BroadcastRadio::handleProgramInfoUpdateRadioCallback(
383         ProgramInfo programInfo, const std::shared_ptr<ITunerCallback>& callback) {
384     callback->onCurrentProgramInfoChanged(programInfo);
385     {
386         lock_guard<mutex> lk(mMutex);
387         mCurrentProgramInfo = programInfo;
388     }
389     if (programInfo.selector.primaryId.type != IdentifierType::HD_STATION_ID_EXT) {
390         return;
391     }
392     ProgramSelector sel = programInfo.selector;
393     auto cancelTask = [sel, callback]() { callback->onTuneFailed(Result::CANCELED, sel); };
394     programInfo.infoFlags |= ProgramInfo::FLAG_HD_SIS_ACQUISITION;
395     auto sisAcquiredTask = [this, callback, programInfo, cancelTask]() {
396         callback->onCurrentProgramInfoChanged(programInfo);
397         mCurrentProgramInfo = programInfo;
398         auto audioAcquiredTask = [this, callback, programInfo]() {
399             ProgramInfo hdProgramInfoWithAudio = programInfo;
400             hdProgramInfoWithAudio.infoFlags |= ProgramInfo::FLAG_HD_AUDIO_ACQUISITION;
401             callback->onCurrentProgramInfoChanged(hdProgramInfoWithAudio);
402             lock_guard<mutex> lk(mMutex);
403             mIsTuneCompleted = true;
404             mCurrentProgramInfo = hdProgramInfoWithAudio;
405         };
406         lock_guard<mutex> lk(mMutex);
407         mTuningThread->schedule(audioAcquiredTask, cancelTask, kTuneDelayTimeMs);
408     };
409 
410     lock_guard<mutex> lk(mMutex);
411     mTuningThread->schedule(sisAcquiredTask, cancelTask, kTuneDelayTimeMs);
412 }
413 
tune(const ProgramSelector & program)414 ScopedAStatus BroadcastRadio::tune(const ProgramSelector& program) {
415     LOG(DEBUG) << __func__ << ": tune to " << program.toString() << "...";
416 
417     lock_guard<mutex> lk(mMutex);
418     if (mCallback == nullptr) {
419         LOG(ERROR) << __func__ << ": callback is not registered.";
420         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
421                 resultToInt(Result::INVALID_STATE), "callback is not registered");
422     }
423 
424     if (!utils::isSupported(mProperties, program)) {
425         LOG(WARNING) << __func__ << ": selector not supported: " << program.toString();
426         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
427                 resultToInt(Result::NOT_SUPPORTED), "selector is not supported");
428     }
429 
430     if (!utils::isValidV2(program)) {
431         LOG(ERROR) << __func__ << ": selector is not valid: " << program.toString();
432         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
433                 resultToInt(Result::INVALID_ARGUMENTS), "selector is not valid");
434     }
435 
436     cancelLocked();
437 
438     mIsTuneCompleted = false;
439     std::shared_ptr<ITunerCallback> callback = mCallback;
440     auto task = [this, program, callback]() {
441         ProgramInfo programInfo = {};
442         {
443             lock_guard<mutex> lk(mMutex);
444             programInfo = tuneInternalLocked(program);
445         }
446         handleProgramInfoUpdateRadioCallback(programInfo, callback);
447     };
448     auto cancelTask = [program, callback]() { callback->onTuneFailed(Result::CANCELED, program); };
449     mTuningThread->schedule(task, cancelTask, kTuneDelayTimeMs);
450 
451     return ScopedAStatus::ok();
452 }
453 
findNextLocked(const ProgramSelector & current,bool directionUp,bool skipSubChannel,VirtualProgram * nextProgram) const454 bool BroadcastRadio::findNextLocked(const ProgramSelector& current, bool directionUp,
455                                     bool skipSubChannel, VirtualProgram* nextProgram) const {
456     if (mProgramList.empty()) {
457         return false;
458     }
459     // The list is not sorted here since it has already stored in VirtualRadio.
460     bool hasAmFmFrequency = utils::hasAmFmFrequency(current);
461     bool hasDabSId = utils::hasId(current, IdentifierType::DAB_SID_EXT);
462     uint32_t currentChannel =
463             hasAmFmFrequency ? utils::getAmFmFrequency(current) : utils::getDabSId(current);
464     auto found =
465             std::lower_bound(mProgramList.begin(), mProgramList.end(), VirtualProgram({current}));
466     if (directionUp) {
467         if (found < mProgramList.end() - 1) {
468             // When seeking up, tuner will jump to the first selector which is main program service
469             // greater than and of the same band as the current program selector in the program
470             // list (if not exist, jump to the first selector in the same band) for skipping
471             // sub-channels case or AM/FM without HD radio enabled case. Otherwise, the tuner will
472             // jump to the first selector which is greater than and of the same band as the current
473             // program selector.
474             if (utils::tunesTo(current, found->selector)) found++;
475             if (skipSubChannel) {
476                 if (hasAmFmFrequency || hasDabSId) {
477                     auto firstFound = found;
478                     while ((hasAmFmFrequency &&
479                             utils::getAmFmFrequency(found->selector) == currentChannel) ||
480                            (hasDabSId && utils::getDabSId(found->selector) == currentChannel)) {
481                         if (found < mProgramList.end() - 1) {
482                             found++;
483                         } else {
484                             found = mProgramList.begin();
485                         }
486                         if (found == firstFound) {
487                             // Only one main channel exists in the program list, the tuner cannot
488                             // skip sub-channel to the next program selector.
489                             return false;
490                         }
491                     }
492                 }
493             }
494         } else {
495             // If the selector of current program is no less than all selectors of the same band or
496             // not found in the program list, seeking up should wrap the tuner to the first program
497             // selector of the same band in the program list.
498             found = mProgramList.begin();
499         }
500     } else {
501         if (found > mProgramList.begin() && found != mProgramList.end()) {
502             // When seeking down, tuner will jump to the first selector which is main program
503             // service less than and of the same band as the current program selector in the
504             // program list (if not exist, jump to the last main program service selector of the
505             // same band) for skipping sub-channels case or AM/FM without HD radio enabled case.
506             // Otherwise, the tuner will jump to the first selector less than and of the same band
507             // as the current program selector.
508             found--;
509             if ((hasAmFmFrequency && utils::hasAmFmFrequency(found->selector)) ||
510                 (hasDabSId && utils::hasId(found->selector, IdentifierType::DAB_SID_EXT))) {
511                 uint32_t nextChannel = hasAmFmFrequency ? utils::getAmFmFrequency(found->selector)
512                                                         : utils::getDabSId(found->selector);
513                 if (nextChannel != currentChannel) {
514                     jumpToFirstSubChannelLocked(found);
515                 } else if (skipSubChannel) {
516                     jumpToFirstSubChannelLocked(found);
517                     auto firstFound = found;
518                     if (found > mProgramList.begin()) {
519                         found--;
520                     } else {
521                         found = mProgramList.end() - 1;
522                     }
523                     jumpToFirstSubChannelLocked(found);
524                     if (found == firstFound) {
525                         // Only one main channel exists in the program list, the tuner cannot skip
526                         // sub-channel to the next program selector.
527                         return false;
528                     }
529                 }
530             }
531         } else {
532             // If the selector of current program is no greater than all selectors of the same band
533             // or not found in the program list, seeking down should wrap the tuner to the last
534             // selector of the same band in the program list. If the last program selector in the
535             // program list is sub-channel and skipping sub-channels is needed, the tuner will jump
536             // to the last main program service of the same band in the program list.
537             found = mProgramList.end() - 1;
538             jumpToFirstSubChannelLocked(found);
539         }
540     }
541     *nextProgram = *found;
542     return true;
543 }
544 
jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator & it) const545 void BroadcastRadio::jumpToFirstSubChannelLocked(vector<VirtualProgram>::const_iterator& it) const {
546     if (it == mProgramList.begin()) {
547         return;
548     }
549     bool hasAmFmFrequency = utils::hasAmFmFrequency(it->selector);
550     bool hasDabSId = utils::hasId(it->selector, IdentifierType::DAB_SID_EXT);
551     if (hasAmFmFrequency || hasDabSId) {
552         uint32_t currentChannel = hasAmFmFrequency ? utils::getAmFmFrequency(it->selector)
553                                                    : utils::getDabSId(it->selector);
554         it--;
555         while (it != mProgramList.begin()) {
556             if (hasAmFmFrequency && utils::hasAmFmFrequency(it->selector) &&
557                 utils::getAmFmFrequency(it->selector) == currentChannel) {
558                 it--;
559             } else if (hasDabSId && utils::hasId(it->selector, IdentifierType::DAB_SID_EXT) &&
560                        utils::getDabSId(it->selector) == currentChannel) {
561                 it--;
562             } else {
563                 break;
564             }
565         }
566         it++;
567     }
568 }
569 
seek(bool directionUp,bool skipSubChannel)570 ScopedAStatus BroadcastRadio::seek(bool directionUp, bool skipSubChannel) {
571     LOG(DEBUG) << __func__ << ": seek " << (directionUp ? "up" : "down") << " with skipSubChannel? "
572                << (skipSubChannel ? "yes" : "no") << "...";
573 
574     lock_guard<mutex> lk(mMutex);
575     if (mCallback == nullptr) {
576         LOG(ERROR) << __func__ << ": callback is not registered.";
577         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
578                 resultToInt(Result::INVALID_STATE), "callback is not registered");
579     }
580 
581     cancelLocked();
582 
583     auto filterCb = [this](const VirtualProgram& program) {
584         return isProgramInBand(program.selector, mCurrentAmFmBandRange,
585                                isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
586                                isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
587     };
588     const auto& list = mVirtualRadio.getProgramList();
589     mProgramList.clear();
590     std::copy_if(list.begin(), list.end(), std::back_inserter(mProgramList), filterCb);
591     std::shared_ptr<ITunerCallback> callback = mCallback;
592     auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
593 
594     VirtualProgram nextProgram = {};
595     bool foundNext =
596             findNextLocked(mCurrentProgramSelector, directionUp, skipSubChannel, &nextProgram);
597     mIsTuneCompleted = false;
598     if (!foundNext) {
599         auto task = [callback]() {
600             LOG(DEBUG) << "seek: program list is empty, seek couldn't stop";
601 
602             callback->onTuneFailed(Result::TIMEOUT, {});
603         };
604         mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
605 
606         return ScopedAStatus::ok();
607     }
608 
609     auto task = [this, nextProgram, callback]() {
610         ProgramInfo programInfo = {};
611         {
612             lock_guard<mutex> lk(mMutex);
613             programInfo = tuneInternalLocked(nextProgram.selector);
614         }
615         handleProgramInfoUpdateRadioCallback(programInfo, callback);
616     };
617     mTuningThread->schedule(task, cancelTask, kSeekDelayTimeMs);
618 
619     return ScopedAStatus::ok();
620 }
621 
step(bool directionUp)622 ScopedAStatus BroadcastRadio::step(bool directionUp) {
623     LOG(DEBUG) << __func__ << ": step " << (directionUp ? "up" : "down") << "...";
624 
625     lock_guard<mutex> lk(mMutex);
626     if (mCallback == nullptr) {
627         LOG(ERROR) << __func__ << ": callback is not registered.";
628         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
629                 resultToInt(Result::INVALID_STATE), "callback is not registered");
630     }
631 
632     cancelLocked();
633 
634     int64_t stepTo;
635     if (utils::hasId(mCurrentProgramSelector, IdentifierType::AMFM_FREQUENCY_KHZ)) {
636         stepTo = utils::getId(mCurrentProgramSelector, IdentifierType::AMFM_FREQUENCY_KHZ);
637     } else if (mCurrentProgramSelector.primaryId.type == IdentifierType::HD_STATION_ID_EXT) {
638         stepTo = utils::getHdFrequency(mCurrentProgramSelector);
639     } else {
640         LOG(WARNING) << __func__ << ": can't step in anything else than AM/FM";
641         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
642                 resultToInt(Result::NOT_SUPPORTED), "cannot step in anything else than AM/FM");
643     }
644 
645     if (!mCurrentAmFmBandRange.has_value()) {
646         LOG(ERROR) << __func__ << ": can't find current band";
647         return ScopedAStatus::fromServiceSpecificErrorWithMessage(
648                 resultToInt(Result::INTERNAL_ERROR), "can't find current band");
649     }
650 
651     if (directionUp) {
652         stepTo += mCurrentAmFmBandRange->spacing;
653     } else {
654         stepTo -= mCurrentAmFmBandRange->spacing;
655     }
656     if (stepTo > mCurrentAmFmBandRange->upperBound) {
657         stepTo = mCurrentAmFmBandRange->lowerBound;
658     }
659     if (stepTo < mCurrentAmFmBandRange->lowerBound) {
660         stepTo = mCurrentAmFmBandRange->upperBound;
661     }
662 
663     mIsTuneCompleted = false;
664     std::shared_ptr<ITunerCallback> callback = mCallback;
665     auto task = [this, stepTo, callback]() {
666         ProgramInfo programInfo;
667         {
668             lock_guard<mutex> lk(mMutex);
669             programInfo = tuneInternalLocked(utils::makeSelectorAmfm(stepTo));
670         }
671         handleProgramInfoUpdateRadioCallback(programInfo, callback);
672     };
673     auto cancelTask = [callback]() { callback->onTuneFailed(Result::CANCELED, {}); };
674     mTuningThread->schedule(task, cancelTask, kStepDelayTimeMs);
675 
676     return ScopedAStatus::ok();
677 }
678 
cancelLocked()679 void BroadcastRadio::cancelLocked() {
680     LOG(DEBUG) << __func__ << ": cancelling current tuning operations...";
681 
682     mTuningThread->cancelAll();
683     if (mCurrentProgramSelector.primaryId.type != IdentifierType::INVALID) {
684         mIsTuneCompleted = true;
685     }
686 }
687 
cancel()688 ScopedAStatus BroadcastRadio::cancel() {
689     LOG(DEBUG) << __func__ << ": cancel pending tune, seek and step...";
690 
691     lock_guard<mutex> lk(mMutex);
692     cancelLocked();
693 
694     return ScopedAStatus::ok();
695 }
696 
startProgramListUpdatesLocked(const ProgramFilter & filter)697 void BroadcastRadio::startProgramListUpdatesLocked(const ProgramFilter& filter) {
698     auto filterCb = [&filter, this](const VirtualProgram& program) {
699         return utils::satisfies(filter, program.selector) &&
700                isProgramInBand(program.selector, mCurrentAmFmBandRange,
701                                isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_FM),
702                                isConfigFlagSetLocked(ConfigFlag::FORCE_ANALOG_AM));
703     };
704 
705     cancelProgramListUpdateLocked();
706 
707     const auto& list = mVirtualRadio.getProgramList();
708     vector<VirtualProgram> filteredList;
709     std::copy_if(list.begin(), list.end(), std::back_inserter(filteredList), filterCb);
710 
711     auto task = [this, filteredList]() {
712         std::shared_ptr<ITunerCallback> callback;
713         {
714             lock_guard<mutex> lk(mMutex);
715             if (mCallback == nullptr) {
716                 LOG(WARNING) << "Callback is null when updating program List";
717                 return;
718             }
719             callback = mCallback;
720         }
721 
722         ProgramListChunk chunk = {};
723         chunk.purge = true;
724         chunk.complete = true;
725         chunk.modified = vector<ProgramInfo>(filteredList.begin(), filteredList.end());
726 
727         callback->onProgramListUpdated(chunk);
728     };
729     mProgramListThread->schedule(task, kListDelayTimeS);
730 }
731 
startProgramListUpdates(const ProgramFilter & filter)732 ScopedAStatus BroadcastRadio::startProgramListUpdates(const ProgramFilter& filter) {
733     LOG(DEBUG) << __func__ << ": requested program list updates, filter = " << filter.toString()
734                << "...";
735 
736     lock_guard<mutex> lk(mMutex);
737 
738     startProgramListUpdatesLocked(filter);
739 
740     return ScopedAStatus::ok();
741 }
742 
cancelProgramListUpdateLocked()743 void BroadcastRadio::cancelProgramListUpdateLocked() {
744     LOG(DEBUG) << __func__ << ": cancelling current program list update operations...";
745     mProgramListThread->cancelAll();
746 }
747 
stopProgramListUpdates()748 ScopedAStatus BroadcastRadio::stopProgramListUpdates() {
749     LOG(DEBUG) << __func__ << ": requested program list updates to stop...";
750     lock_guard<mutex> lk(mMutex);
751     cancelProgramListUpdateLocked();
752     return ScopedAStatus::ok();
753 }
754 
isConfigFlagSetLocked(ConfigFlag flag) const755 bool BroadcastRadio::isConfigFlagSetLocked(ConfigFlag flag) const {
756     int flagBit = static_cast<int>(flag);
757     return ((mConfigFlagValues >> flagBit) & 1) == 1;
758 }
759 
isConfigFlagSet(ConfigFlag flag,bool * returnIsSet)760 ScopedAStatus BroadcastRadio::isConfigFlagSet(ConfigFlag flag, bool* returnIsSet) {
761     LOG(DEBUG) << __func__ << ": flag = " << toString(flag);
762 
763     if (flag == ConfigFlag::FORCE_ANALOG) {
764         flag = ConfigFlag::FORCE_ANALOG_FM;
765     }
766     lock_guard<mutex> lk(mMutex);
767     *returnIsSet = isConfigFlagSetLocked(flag);
768     return ScopedAStatus::ok();
769 }
770 
setConfigFlag(ConfigFlag flag,bool value)771 ScopedAStatus BroadcastRadio::setConfigFlag(ConfigFlag flag, bool value) {
772     LOG(DEBUG) << __func__ << ": flag = " << toString(flag) << ", value = " << value;
773 
774     if (flag == ConfigFlag::FORCE_ANALOG) {
775         flag = ConfigFlag::FORCE_ANALOG_FM;
776     }
777     int flagBitMask = 1 << (static_cast<int>(flag));
778     lock_guard<mutex> lk(mMutex);
779     if (value) {
780         mConfigFlagValues |= flagBitMask;
781     } else {
782         mConfigFlagValues &= ~flagBitMask;
783     }
784     if (flag == ConfigFlag::FORCE_ANALOG_AM || flag == ConfigFlag::FORCE_ANALOG_FM) {
785         startProgramListUpdatesLocked({});
786     }
787     return ScopedAStatus::ok();
788 }
789 
setParameters(const vector<VendorKeyValue> & parameters,vector<VendorKeyValue> * returnParameters)790 ScopedAStatus BroadcastRadio::setParameters(
791         [[maybe_unused]] const vector<VendorKeyValue>& parameters,
792         vector<VendorKeyValue>* returnParameters) {
793     // TODO(b/243682330) Support vendor parameter functionality
794     *returnParameters = {};
795     return ScopedAStatus::ok();
796 }
797 
getParameters(const vector<string> & keys,vector<VendorKeyValue> * returnParameters)798 ScopedAStatus BroadcastRadio::getParameters([[maybe_unused]] const vector<string>& keys,
799                                             vector<VendorKeyValue>* returnParameters) {
800     // TODO(b/243682330) Support vendor parameter functionality
801     *returnParameters = {};
802     return ScopedAStatus::ok();
803 }
804 
adjustAmFmRangeLocked()805 bool BroadcastRadio::adjustAmFmRangeLocked() {
806     bool hasBandBefore = mCurrentAmFmBandRange.has_value();
807     if (!utils::hasAmFmFrequency(mCurrentProgramSelector)) {
808         LOG(WARNING) << __func__ << ": current program does not has AMFM_FREQUENCY_KHZ identifier";
809         mCurrentAmFmBandRange.reset();
810         return hasBandBefore;
811     }
812 
813     int32_t freq = static_cast<int32_t>(utils::getAmFmFrequency(mCurrentProgramSelector));
814     for (const auto& range : mAmFmConfig.ranges) {
815         if (range.lowerBound <= freq && range.upperBound >= freq) {
816             bool isBandChanged = hasBandBefore ? *mCurrentAmFmBandRange != range : true;
817             mCurrentAmFmBandRange = range;
818             return isBandChanged;
819         }
820     }
821 
822     mCurrentAmFmBandRange.reset();
823     return !hasBandBefore;
824 }
825 
updateCurrentProgramInfoWithAlert(std::optional<Alert> & alert)826 void BroadcastRadio::updateCurrentProgramInfoWithAlert(std::optional<Alert>& alert) {
827     std::shared_ptr<ITunerCallback> callback;
828     ProgramInfo currentProgramInfo;
829     {
830         lock_guard<mutex> lk(mMutex);
831         if (mCallback == nullptr) {
832             return;
833         }
834         if (mCurrentProgramInfo.selector.primaryId.type == IdentifierType::INVALID) {
835             return;
836         }
837         callback = mCallback;
838         currentProgramInfo = mCurrentProgramInfo;
839     }
840     currentProgramInfo.emergencyAlert = alert.value();
841     callback->onCurrentProgramInfoChanged(currentProgramInfo);
842 }
843 
registerAnnouncementListener(const std::shared_ptr<IAnnouncementListener> & listener,const vector<AnnouncementType> & enabled,std::shared_ptr<ICloseHandle> * returnCloseHandle)844 ScopedAStatus BroadcastRadio::registerAnnouncementListener(
845         [[maybe_unused]] const std::shared_ptr<IAnnouncementListener>& listener,
846         const vector<AnnouncementType>& enabled, std::shared_ptr<ICloseHandle>* returnCloseHandle) {
847     LOG(DEBUG) << __func__ << ": registering announcement listener for "
848                << utils::vectorToString(enabled);
849 
850     // TODO(b/243683842) Support announcement listener
851     *returnCloseHandle = nullptr;
852     LOG(INFO) << __func__ << ": registering announcementListener is not supported";
853     return ScopedAStatus::fromServiceSpecificErrorWithMessage(
854             resultToInt(Result::NOT_SUPPORTED),
855             "registering announcementListener is not supported");
856 }
857 
dump(int fd,const char ** args,uint32_t numArgs)858 binder_status_t BroadcastRadio::dump(int fd, const char** args, uint32_t numArgs) {
859     if (numArgs == 0) {
860         return dumpsys(fd);
861     }
862 
863     string option = string(args[0]);
864     if (EqualsIgnoreCase(option, "--help")) {
865         return cmdHelp(fd);
866     } else if (EqualsIgnoreCase(option, "--tune")) {
867         return cmdTune(fd, args, numArgs);
868     } else if (EqualsIgnoreCase(option, "--seek")) {
869         return cmdSeek(fd, args, numArgs);
870     } else if (EqualsIgnoreCase(option, "--step")) {
871         return cmdStep(fd, args, numArgs);
872     } else if (EqualsIgnoreCase(option, "--cancel")) {
873         return cmdCancel(fd, numArgs);
874     } else if (EqualsIgnoreCase(option, "--startProgramListUpdates")) {
875         return cmdStartProgramListUpdates(fd, args, numArgs);
876     } else if (EqualsIgnoreCase(option, "--stopProgramListUpdates")) {
877         return cmdStopProgramListUpdates(fd, numArgs);
878     } else if (EqualsIgnoreCase(option, "--simulateAlert")) {
879         return cmdSimulateAlert(fd, args, numArgs);
880     }
881     dprintf(fd, "Invalid option: %s\n", option.c_str());
882     return STATUS_BAD_VALUE;
883 }
884 
dumpsys(int fd)885 binder_status_t BroadcastRadio::dumpsys(int fd) {
886     if (!checkDumpCallerHasWritePermissions(fd)) {
887         return STATUS_PERMISSION_DENIED;
888     }
889     lock_guard<mutex> lk(mMutex);
890     dprintf(fd, "AmFmRegionConfig: %s\n", mAmFmConfig.toString().c_str());
891     dprintf(fd, "Properties: %s \n", mProperties.toString().c_str());
892     if (mIsTuneCompleted) {
893         dprintf(fd, "Tune completed\n");
894     } else {
895         dprintf(fd, "Tune not completed\n");
896     }
897     if (mCallback == nullptr) {
898         dprintf(fd, "No ITunerCallback registered\n");
899     } else {
900         dprintf(fd, "ITunerCallback registered\n");
901     }
902     dprintf(fd, "CurrentProgram: %s \n", mCurrentProgramSelector.toString().c_str());
903     return STATUS_OK;
904 }
905 
cmdHelp(int fd) const906 binder_status_t BroadcastRadio::cmdHelp(int fd) const {
907     dprintf(fd, "Usage: \n\n");
908     dprintf(fd, "[no args]: dumps focus listener / gain callback registered status\n");
909     dprintf(fd, "--help: shows this help\n");
910     dprintf(fd,
911             "--tune amfm <FREQUENCY>: tunes amfm radio to frequency (in Hz) specified: "
912             "frequency (int) \n"
913             "--tune dab <SID> <ENSEMBLE>: tunes dab radio to sid and ensemble specified: "
914             "sidExt (int), ensemble (int) \n");
915     dprintf(fd,
916             "--seek [up|down] <SKIP_SUB_CHANNEL>: seek with direction (up or down) and "
917             "option whether skipping sub channel: "
918             "skipSubChannel (string, should be either \"true\" or \"false\")\n");
919     dprintf(fd, "--step [up|down]: step in direction (up or down) specified\n");
920     dprintf(fd, "--cancel: cancel current pending tune, step, and seek\n");
921     dprintf(fd,
922             "--startProgramListUpdates <IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
923             "<EXCLUDE_MODIFICATIONS>: start update program list with the filter specified: "
924             "identifier types (string, in format <TYPE>,<TYPE>,...,<TYPE> or \"null\" (if empty), "
925             "where TYPE is int), "
926             "program identifiers (string, in format "
927             "<TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE> or \"null\" (if empty), "
928             "where TYPE is int and VALUE is long), "
929             "includeCategories (string, should be either \"true\" or \"false\"), "
930             "excludeModifications (string, should be either \"true\" or \"false\")\n");
931     dprintf(fd, "--stopProgramListUpdates: stop current pending program list updates\n");
932     dprintf(fd,
933             "\t<TYPE>: it is int for identifier type. "
934             "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
935             "for its definition.\n");
936     dprintf(fd,
937             "\t<VALUE>: it is long type for identifier value. "
938             "Please see broadcastradio/aidl/android/hardware/broadcastradio/IdentifierType.aidl "
939             "for its value.\n");
940     dprintf(fd,
941             "--simulateAlert <STATUS> <MESSAGE_TYPE> <CATEGORIES> <URGENCY> <SEVERITY> "
942             "<CERTAINTY> <DESCRIPTION> <LANGUAGE> <AREAS>: simulate emergency alert on current "
943             "program; if no arguments following \"--simulateAlert\", the default alert message"
944             "is applied.\n");
945     dprintf(fd, "\t<STATUS>: string representation of alert scope.\n");
946     dprintf(fd, "\t<MESSAGE_TYPE>: string representation of alert message type.\n");
947     dprintf(fd,
948             "\t<CATEGORIES>: string representation of alert categories separated by "
949             "\",\".\n");
950     dprintf(fd, "\t<URGENCY>: string representation of alert urgency type.\n");
951     dprintf(fd, "\t<SEVERITY>: string representation of alert severity type.\n");
952     dprintf(fd, "\t<CERTAINTY>: string representation of alert certainty type.\n");
953     dprintf(fd, "\t<DESCRIPTION>: description of alert message within quotation mark(\"\").\n");
954     dprintf(fd, "\t<LANGUAGE>: language code of alert message, \"null\" if unspecified.\n");
955     dprintf(fd,
956             "\t<AREAS>: <TYPE>:<VALUE>_<TYPE>:<VALUE>_...+<TYPE>:<VALUE>_<TYPE>:<VALUE>_... "
957             "which represents list of affected areas of the alert separated by \"|\". "
958             "If no area, this field should be: |\n"
959             "Each area may contains multiple entries separated by \";\" where "
960             "<TYPE> can be either \"polygon\" or \"geocode\". If <TYPE> is polygon, <VALUE> is a "
961             "series of coordinates of \"LATITUDE,LONGITUDE\" format separated by \",\"; if "
962             "<TYPE> is geocode, <VALUE> is of \"VALUE_NAME,VALUE\" format.\n");
963     dprintf(fd,
964             "Example: --simulateAlert actual alert geo,transport future severe"
965             " possible \"alert message for testing\" en-US geocode:SAME,006109_geocode:SAME,006209"
966             "_polygon:-38.47,-120.14,38.34,-119.95,38.52,-119.74,38.62,-119.89,-38.47,-120.14"
967             "+geocode:SAME,006009\n");
968 
969     return STATUS_OK;
970 }
971 
cmdTune(int fd,const char ** args,uint32_t numArgs)972 binder_status_t BroadcastRadio::cmdTune(int fd, const char** args, uint32_t numArgs) {
973     if (!checkDumpCallerHasWritePermissions(fd)) {
974         return STATUS_PERMISSION_DENIED;
975     }
976     if (numArgs != 3 && numArgs != 4) {
977         dprintf(fd,
978                 "Invalid number of arguments: please provide --tune amfm <FREQUENCY> "
979                 "or --tune dab <SID> <ENSEMBLE>\n");
980         return STATUS_BAD_VALUE;
981     }
982     bool isDab = false;
983     if (EqualsIgnoreCase(string(args[1]), "dab")) {
984         isDab = true;
985     } else if (!EqualsIgnoreCase(string(args[1]), "amfm")) {
986         dprintf(fd, "Unknown radio type provided with tune: %s\n", args[1]);
987         return STATUS_BAD_VALUE;
988     }
989     ProgramSelector sel = {};
990     if (isDab) {
991         if (numArgs != 5 && numArgs != 3) {
992             dprintf(fd,
993                     "Invalid number of arguments: please provide "
994                     "--tune dab <SID> <ENSEMBLE> <FREQUENCY> or "
995                     "--tune dab <SID>\n");
996             return STATUS_BAD_VALUE;
997         }
998         int sid;
999         if (!utils::parseArgInt(string(args[2]), &sid)) {
1000             dprintf(fd, "Non-integer sid provided with tune: %s\n", args[2]);
1001             return STATUS_BAD_VALUE;
1002         }
1003         if (numArgs == 3) {
1004             sel = utils::makeSelectorDab(sid);
1005         } else {
1006             int ensemble;
1007             if (!utils::parseArgInt(string(args[3]), &ensemble)) {
1008                 dprintf(fd, "Non-integer ensemble provided with tune: %s\n", args[3]);
1009                 return STATUS_BAD_VALUE;
1010             }
1011             int freq;
1012             if (!utils::parseArgInt(string(args[4]), &freq)) {
1013                 dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[4]);
1014                 return STATUS_BAD_VALUE;
1015             }
1016             sel = utils::makeSelectorDab(sid, ensemble, freq);
1017         }
1018     } else {
1019         if (numArgs != 3) {
1020             dprintf(fd, "Invalid number of arguments: please provide --tune amfm <FREQUENCY>\n");
1021             return STATUS_BAD_VALUE;
1022         }
1023         int freq;
1024         if (!utils::parseArgInt(string(args[2]), &freq)) {
1025             dprintf(fd, "Non-integer frequency provided with tune: %s\n", args[2]);
1026             return STATUS_BAD_VALUE;
1027         }
1028         sel = utils::makeSelectorAmfm(freq);
1029     }
1030 
1031     auto tuneResult = tune(sel);
1032     if (!tuneResult.isOk()) {
1033         dprintf(fd, "Unable to tune %s radio to %s\n", args[1], sel.toString().c_str());
1034         return STATUS_BAD_VALUE;
1035     }
1036     dprintf(fd, "Tune %s radio to %s \n", args[1], sel.toString().c_str());
1037     return STATUS_OK;
1038 }
1039 
cmdSeek(int fd,const char ** args,uint32_t numArgs)1040 binder_status_t BroadcastRadio::cmdSeek(int fd, const char** args, uint32_t numArgs) {
1041     if (!checkDumpCallerHasWritePermissions(fd)) {
1042         return STATUS_PERMISSION_DENIED;
1043     }
1044     if (numArgs != 3) {
1045         dprintf(fd,
1046                 "Invalid number of arguments: please provide --seek <DIRECTION> "
1047                 "<SKIP_SUB_CHANNEL>\n");
1048         return STATUS_BAD_VALUE;
1049     }
1050     string seekDirectionIn = string(args[1]);
1051     bool seekDirectionUp;
1052     if (!utils::parseArgDirection(seekDirectionIn, &seekDirectionUp)) {
1053         dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with seek: %s\n",
1054                 seekDirectionIn.c_str());
1055         return STATUS_BAD_VALUE;
1056     }
1057     string skipSubChannelIn = string(args[2]);
1058     bool skipSubChannel;
1059     if (!utils::parseArgBool(skipSubChannelIn, &skipSubChannel)) {
1060         dprintf(fd, "Invalid skipSubChannel (\"true\" or \"false\") provided with seek: %s\n",
1061                 skipSubChannelIn.c_str());
1062         return STATUS_BAD_VALUE;
1063     }
1064 
1065     auto seekResult = seek(seekDirectionUp, skipSubChannel);
1066     if (!seekResult.isOk()) {
1067         dprintf(fd, "Unable to seek in %s direction\n", seekDirectionIn.c_str());
1068         return STATUS_BAD_VALUE;
1069     }
1070     dprintf(fd, "Seek in %s direction\n", seekDirectionIn.c_str());
1071     return STATUS_OK;
1072 }
1073 
cmdStep(int fd,const char ** args,uint32_t numArgs)1074 binder_status_t BroadcastRadio::cmdStep(int fd, const char** args, uint32_t numArgs) {
1075     if (!checkDumpCallerHasWritePermissions(fd)) {
1076         return STATUS_PERMISSION_DENIED;
1077     }
1078     if (numArgs != 2) {
1079         dprintf(fd, "Invalid number of arguments: please provide --step <DIRECTION>\n");
1080         return STATUS_BAD_VALUE;
1081     }
1082     string stepDirectionIn = string(args[1]);
1083     bool stepDirectionUp;
1084     if (!utils::parseArgDirection(stepDirectionIn, &stepDirectionUp)) {
1085         dprintf(fd, "Invalid direction (\"up\" or \"down\") provided with step: %s\n",
1086                 stepDirectionIn.c_str());
1087         return STATUS_BAD_VALUE;
1088     }
1089 
1090     auto stepResult = step(stepDirectionUp);
1091     if (!stepResult.isOk()) {
1092         dprintf(fd, "Unable to step in %s direction\n", stepDirectionIn.c_str());
1093         return STATUS_BAD_VALUE;
1094     }
1095     dprintf(fd, "Step in %s direction\n", stepDirectionIn.c_str());
1096     return STATUS_OK;
1097 }
1098 
cmdCancel(int fd,uint32_t numArgs)1099 binder_status_t BroadcastRadio::cmdCancel(int fd, uint32_t numArgs) {
1100     if (!checkDumpCallerHasWritePermissions(fd)) {
1101         return STATUS_PERMISSION_DENIED;
1102     }
1103     if (numArgs != 1) {
1104         dprintf(fd,
1105                 "Invalid number of arguments: please provide --cancel "
1106                 "only and no more arguments\n");
1107         return STATUS_BAD_VALUE;
1108     }
1109 
1110     auto cancelResult = cancel();
1111     if (!cancelResult.isOk()) {
1112         dprintf(fd, "Unable to cancel pending tune, seek, and step\n");
1113         return STATUS_BAD_VALUE;
1114     }
1115     dprintf(fd, "Canceled pending tune, seek, and step\n");
1116     return STATUS_OK;
1117 }
1118 
cmdStartProgramListUpdates(int fd,const char ** args,uint32_t numArgs)1119 binder_status_t BroadcastRadio::cmdStartProgramListUpdates(int fd, const char** args,
1120                                                            uint32_t numArgs) {
1121     if (!checkDumpCallerHasWritePermissions(fd)) {
1122         return STATUS_PERMISSION_DENIED;
1123     }
1124     if (numArgs != 5) {
1125         dprintf(fd,
1126                 "Invalid number of arguments: please provide --startProgramListUpdates "
1127                 "<IDENTIFIER_TYPES> <IDENTIFIERS> <INCLUDE_CATEGORIES> "
1128                 "<EXCLUDE_MODIFICATIONS>\n");
1129         return STATUS_BAD_VALUE;
1130     }
1131     string filterTypesStr = string(args[1]);
1132     std::vector<IdentifierType> filterTypeList;
1133     if (!EqualsIgnoreCase(filterTypesStr, "null") &&
1134         !utils::parseArgIdentifierTypeArray(filterTypesStr, &filterTypeList)) {
1135         dprintf(fd,
1136                 "Invalid identifier types provided with startProgramListUpdates: %s, "
1137                 "should be: <TYPE>,<TYPE>,...,<TYPE>\n",
1138                 filterTypesStr.c_str());
1139         return STATUS_BAD_VALUE;
1140     }
1141     string filtersStr = string(args[2]);
1142     std::vector<ProgramIdentifier> filterList;
1143     if (!EqualsIgnoreCase(filtersStr, "null") &&
1144         !utils::parseProgramIdentifierList(filtersStr, &filterList)) {
1145         dprintf(fd,
1146                 "Invalid program identifiers provided with startProgramListUpdates: %s, "
1147                 "should be: <TYPE>:<VALUE>,<TYPE>:<VALUE>,...,<TYPE>:<VALUE>\n",
1148                 filtersStr.c_str());
1149         return STATUS_BAD_VALUE;
1150     }
1151     string includeCategoriesStr = string(args[3]);
1152     bool includeCategories;
1153     if (!utils::parseArgBool(includeCategoriesStr, &includeCategories)) {
1154         dprintf(fd,
1155                 "Invalid includeCategories (\"true\" or \"false\") "
1156                 "provided with startProgramListUpdates : %s\n",
1157                 includeCategoriesStr.c_str());
1158         return STATUS_BAD_VALUE;
1159     }
1160     string excludeModificationsStr = string(args[4]);
1161     bool excludeModifications;
1162     if (!utils::parseArgBool(excludeModificationsStr, &excludeModifications)) {
1163         dprintf(fd,
1164                 "Invalid excludeModifications(\"true\" or \"false\") "
1165                 "provided with startProgramListUpdates : %s\n",
1166                 excludeModificationsStr.c_str());
1167         return STATUS_BAD_VALUE;
1168     }
1169     ProgramFilter filter = {filterTypeList, filterList, includeCategories, excludeModifications};
1170 
1171     auto updateResult = startProgramListUpdates(filter);
1172     if (!updateResult.isOk()) {
1173         dprintf(fd, "Unable to start program list update for filter %s \n",
1174                 filter.toString().c_str());
1175         return STATUS_BAD_VALUE;
1176     }
1177     dprintf(fd, "Start program list update for filter %s\n", filter.toString().c_str());
1178     return STATUS_OK;
1179 }
1180 
cmdStopProgramListUpdates(int fd,uint32_t numArgs)1181 binder_status_t BroadcastRadio::cmdStopProgramListUpdates(int fd, uint32_t numArgs) {
1182     if (!checkDumpCallerHasWritePermissions(fd)) {
1183         return STATUS_PERMISSION_DENIED;
1184     }
1185     if (numArgs != 1) {
1186         dprintf(fd,
1187                 "Invalid number of arguments: please provide --stopProgramListUpdates "
1188                 "only and no more arguments\n");
1189         return STATUS_BAD_VALUE;
1190     }
1191 
1192     auto stopResult = stopProgramListUpdates();
1193     if (!stopResult.isOk()) {
1194         dprintf(fd, "Unable to stop pending program list update\n");
1195         return STATUS_BAD_VALUE;
1196     }
1197     dprintf(fd, "Stop pending program list update\n");
1198     return STATUS_OK;
1199 }
1200 
cmdSimulateAlert(int fd,const char ** args,uint32_t numArgs)1201 binder_status_t BroadcastRadio::cmdSimulateAlert(int fd, const char** args, uint32_t numArgs) {
1202     if (!checkDumpCallerHasWritePermissions(fd)) {
1203         return STATUS_PERMISSION_DENIED;
1204     }
1205     std::optional<Alert> alertOpt;
1206     if (numArgs == 1) {
1207         alertOpt.emplace(createSampleAlert());
1208         updateCurrentProgramInfoWithAlert(alertOpt);
1209         return STATUS_OK;
1210     }
1211     if (numArgs != 10) {
1212         dprintf(fd,
1213                 "Invalid number of arguments: please provide --simulateAlert "
1214                 "<STATUS> <MESSAGE_TYPE> <CATEGORIES> <URGENCY> "
1215                 "<SEVERITY> <CERTAINTY> <DESCRIPTION> <LANGUAGE> <AREAS>, provided: %d\n",
1216                 numArgs);
1217         return STATUS_BAD_VALUE;
1218     }
1219     Alert parsedAlert;
1220     if (!utils::parseAlertStatus(args[1], parsedAlert.status)) {
1221         dprintf(fd, "Unknown alert status type: %s\n", args[2]);
1222         return STATUS_BAD_VALUE;
1223     }
1224     if (!utils::parseAlertMessageType(args[2], parsedAlert.messageType)) {
1225         dprintf(fd, "Unknown alert message type: %s\n", args[3]);
1226         return STATUS_BAD_VALUE;
1227     }
1228     AlertInfo parsedAlertInfo;
1229     vector<string> categoryStrings = ::android::base::Split(args[3], ",");
1230     for (const auto& categoryString : categoryStrings) {
1231         AlertCategory category;
1232         if (!utils::parseAlertCategory(categoryString, category)) {
1233             dprintf(fd, "Unknown alert category type: %s\n", args[3]);
1234             return STATUS_BAD_VALUE;
1235         }
1236         parsedAlertInfo.categoryArray.push_back(category);
1237     }
1238     if (!utils::parseAlertUrgency(args[4], parsedAlertInfo.urgency)) {
1239         dprintf(fd, "Unknown alert urgency type: %s\n", args[4]);
1240         return STATUS_BAD_VALUE;
1241     }
1242     if (!utils::parseAlertSeverity(args[5], parsedAlertInfo.severity)) {
1243         dprintf(fd, "Unknown alert severity type: %s\n", args[5]);
1244         return STATUS_BAD_VALUE;
1245     }
1246     if (!utils::parseAlertCertainty(args[6], parsedAlertInfo.certainty)) {
1247         dprintf(fd, "Unknown alert certainty type: %s\n", args[6]);
1248         return STATUS_BAD_VALUE;
1249     }
1250     parsedAlertInfo.description = string(args[7]);
1251     string languageStr = string(args[8]);
1252     if (!EqualsIgnoreCase(languageStr, "null")) {
1253         parsedAlertInfo.language.emplace(languageStr);
1254     }
1255     string areaListString = string(args[9]);
1256     vector<AlertArea> areaList;
1257     if (!parseAreaListString(fd, areaListString, areaList)) {
1258         return STATUS_BAD_VALUE;
1259     }
1260     parsedAlertInfo.areas = areaList;
1261     parsedAlert.infoArray = {parsedAlertInfo};
1262     LOG(INFO) << "Simulate alert: " << parsedAlert.toString().c_str();
1263     alertOpt.emplace(parsedAlert);
1264     updateCurrentProgramInfoWithAlert(alertOpt);
1265     return STATUS_OK;
1266 }
1267 
1268 }  // namespace aidl::android::hardware::broadcastradio
1269