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