• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define FAILURE_DEBUG_PREFIX "RadioModem"
18 
19 #include <charconv>
20 #include <format>
21 
22 #include "RadioModem.h"
23 
24 #include "atCmds.h"
25 #include "debug.h"
26 #include "makeRadioResponseInfo.h"
27 #include "ratUtils.h"
28 
29 namespace aidl {
30 namespace android {
31 namespace hardware {
32 namespace radio {
33 namespace implementation {
34 namespace {
35 constexpr char kBasebandversion[] = "1.0.0.0";
36 constexpr char kModemUuid[] = "com.android.modem.simulator";
37 constexpr char kSimUuid[] = "com.android.modem.simcard";
38 }  // namespace
39 
RadioModem(std::shared_ptr<AtChannel> atChannel)40 RadioModem::RadioModem(std::shared_ptr<AtChannel> atChannel) : mAtChannel(std::move(atChannel)) {
41 }
42 
enableModem(const int32_t serial,const bool)43 ScopedAStatus RadioModem::enableModem(const int32_t serial, const bool /*on*/) {
44     NOT_NULL(mRadioModemResponse)->enableModemResponse(
45         makeRadioResponseInfo(serial));
46     return ScopedAStatus::ok();
47 }
48 
getBasebandVersion(const int32_t serial)49 ScopedAStatus RadioModem::getBasebandVersion(const int32_t serial) {
50     NOT_NULL(mRadioModemResponse)->getBasebandVersionResponse(
51             makeRadioResponseInfo(serial), kBasebandversion);
52     return ScopedAStatus::ok();
53 }
54 
getImei(const int32_t serial)55 ScopedAStatus RadioModem::getImei(const int32_t serial) {
56     static const char* const kFunc = __func__;
57     mAtChannel->queueRequester([this, serial](const AtChannel::RequestPipe requestPipe) -> bool {
58         using namespace std::literals;
59 
60         const AtResponsePtr response =
61             mAtConversation(requestPipe, atCmds::getIMEI,
62                             [](const AtResponse& response) -> bool {
63                                 return response.holds<std::string>();
64                             });
65         if (!response) {
66             NOT_NULL(mRadioModemResponse)->getImeiResponse(
67                     makeRadioResponseInfo(serial, FAILURE(RadioError::INTERNAL_ERR)), {});
68             return false;
69         } else if (const std::string* imeiSvn = response->get_if<std::string>()) {
70             using modem::ImeiInfo;
71 
72             ImeiInfo imeiInfo = {
73                 .type = ImeiInfo::ImeiType::PRIMARY,
74                 .imei = imeiSvn->substr(0, 15),
75                 .svn = imeiSvn->substr(15, 2),
76             };
77 
78             NOT_NULL(mRadioModemResponse)->getImeiResponse(
79                 makeRadioResponseInfo(serial), std::move(imeiInfo));
80            return true;
81         } else {
82             response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
83         }
84     });
85 
86     return ScopedAStatus::ok();
87 }
88 
getHardwareConfig(const int32_t serial)89 ScopedAStatus RadioModem::getHardwareConfig(const int32_t serial) {
90     static const char* const kFunc = __func__;
91     mAtChannel->queueRequester([this, serial](const AtChannel::RequestPipe requestPipe) -> bool {
92         using modem::HardwareConfig;
93         using modem::HardwareConfigModem;
94         using modem::HardwareConfigSim;
95 
96         std::vector<HardwareConfig> config;
97 
98         const auto [status, rafBitmask] =
99             getSupportedRadioTechs(requestPipe, mAtConversation);
100         if (status == RadioError::NONE) {
101             const HardwareConfigModem modemHwConfig = {
102                 .rilModel = 0,  // 0 - single: one-to-one relationship between a modem hardware and a ril daemon.
103                 .rat = static_cast<RadioTechnology>(rafBitmask),
104                 .maxVoiceCalls = 1,
105                 .maxDataCalls = 1,
106                 .maxStandby = 1,
107             };
108 
109             HardwareConfig modemConfig = {
110                 .type = HardwareConfig::TYPE_MODEM,
111                 .uuid = kModemUuid,
112                 .state = HardwareConfig::STATE_ENABLED,
113             };
114 
115             modemConfig.modem.push_back(modemHwConfig);
116 
117             HardwareConfig simConfig = {
118                 .type = HardwareConfig::TYPE_SIM,
119                 .uuid = kSimUuid,
120                 .state = HardwareConfig::STATE_ENABLED,
121             };
122 
123             HardwareConfigSim simHwConfig = {
124                 .modemUuid = modemConfig.uuid,
125             };
126             simConfig.sim.push_back(simHwConfig);
127 
128             config.push_back(std::move(modemConfig));
129             config.push_back(std::move(simConfig));
130         }
131 
132         NOT_NULL(mRadioModemResponse)->getHardwareConfigResponse(
133                 makeRadioResponseInfo(serial, status), std::move(config));
134         return status != RadioError::INTERNAL_ERR;
135     });
136 
137     return ScopedAStatus::ok();
138 }
139 
getModemActivityInfo(const int32_t serial)140 ScopedAStatus RadioModem::getModemActivityInfo(const int32_t serial) {
141     using modem::ActivityStatsInfo;
142     using modem::ActivityStatsTechSpecificInfo;
143 
144     ActivityStatsInfo activityStatsInfo = {
145         .sleepModeTimeMs = 42,
146         .idleModeTimeMs = 14,
147         .techSpecificInfo = {
148             {
149                 .frequencyRange = ActivityStatsTechSpecificInfo::FREQUENCY_RANGE_UNKNOWN,
150                 .txmModetimeMs = { 1, 3, 6, 8, 9 },
151                 .rxModeTimeMs = 9,
152             },
153         },
154     };
155 
156     NOT_NULL(mRadioModemResponse)->getModemActivityInfoResponse(
157             makeRadioResponseInfo(serial), std::move(activityStatsInfo));
158     return ScopedAStatus::ok();
159 }
160 
getModemStackStatus(const int32_t serial)161 ScopedAStatus RadioModem::getModemStackStatus(const int32_t serial) {
162     NOT_NULL(mRadioModemResponse)->getModemStackStatusResponse(
163             makeRadioResponseInfo(serial), true);
164     return ScopedAStatus::ok();
165 }
166 
getRadioCapability(const int32_t serial)167 ScopedAStatus RadioModem::getRadioCapability(const int32_t serial) {
168     static const char* const kFunc = __func__;
169     mAtChannel->queueRequester([this, serial](const AtChannel::RequestPipe requestPipe) -> bool {
170         using modem::RadioCapability;
171         RadioCapability cap;
172 
173         const auto [status, rafBitmask] =
174             getSupportedRadioTechs(requestPipe, mAtConversation);
175         if (status == RadioError::NONE) {
176             cap.session = serial;
177             cap.phase = RadioCapability::PHASE_CONFIGURED;
178             cap.raf = rafBitmask;
179             cap.logicalModemUuid = kModemUuid;
180             cap.status = RadioCapability::STATUS_SUCCESS;
181         }
182 
183         NOT_NULL(mRadioModemResponse)->getRadioCapabilityResponse(
184                 makeRadioResponseInfo(serial, status), std::move(cap));
185         return status != RadioError::INTERNAL_ERR;
186     });
187 
188     return ScopedAStatus::ok();
189 }
190 
requestShutdown(const int32_t serial)191 ScopedAStatus RadioModem::requestShutdown(const int32_t serial) {
192     mAtChannel->queueRequester([this, serial]
193                                (const AtChannel::RequestPipe requestPipe) -> bool {
194         if (setRadioPowerImpl(requestPipe, false)) {
195             NOT_NULL(mRadioModemResponse)->requestShutdownResponse(
196                 makeRadioResponseInfo(serial));
197             return true;
198         } else {
199             return false;
200         }
201     });
202 
203     return ScopedAStatus::ok();
204 }
205 
sendDeviceState(const int32_t serial,const modem::DeviceStateType,const bool)206 ScopedAStatus RadioModem::sendDeviceState(const int32_t serial,
207                                           const modem::DeviceStateType /*stateType*/,
208                                           const bool /*state*/) {
209     // matches reference-ril.c
210     NOT_NULL(mRadioModemResponse)->sendDeviceStateResponse(
211         makeRadioResponseInfoNOP(serial));
212     return ScopedAStatus::ok();
213 }
214 
setRadioCapability(const int32_t serial,const modem::RadioCapability &)215 ScopedAStatus RadioModem::setRadioCapability(const int32_t serial,
216                                              const modem::RadioCapability& /*rc*/) {
217     // matches reference-ril.c
218     NOT_NULL(mRadioModemResponse)->setRadioCapabilityResponse(
219         makeRadioResponseInfoNOP(serial), {});
220     return ScopedAStatus::ok();
221 }
222 
setRadioPower(const int32_t serial,const bool powerOn,const bool forEmergencyCall,const bool preferredForEmergencyCall)223 ScopedAStatus RadioModem::setRadioPower(const int32_t serial, const bool powerOn,
224                                         const bool forEmergencyCall,
225                                         const bool preferredForEmergencyCall) {
226     mAtChannel->queueRequester([this, serial, powerOn]
227                                (const AtChannel::RequestPipe requestPipe) -> bool {
228         if (setRadioPowerImpl(requestPipe, powerOn)) {
229             NOT_NULL(mRadioModemResponse)->setRadioPowerResponse(
230                     makeRadioResponseInfo(serial));
231             return true;
232         } else {
233             return false;
234         }
235     });
236 
237     return ScopedAStatus::ok();
238 }
239 
responseAcknowledgement()240 ScopedAStatus RadioModem::responseAcknowledgement() {
241     return ScopedAStatus::ok();
242 }
243 
atResponseSink(const AtResponsePtr & response)244 void RadioModem::atResponseSink(const AtResponsePtr& response) {
245     if (!mAtConversation.send(response)) {
246         response->visit([this](const auto& msg){ handleUnsolicited(msg); });
247     }
248 }
249 
handleUnsolicited(const AtResponse::CFUN & cfun)250 void RadioModem::handleUnsolicited(const AtResponse::CFUN& cfun) {
251     bool changed;
252     {
253         std::lock_guard<std::mutex> lock(mMtx);
254         changed = (mRadioState != cfun.state);
255         mRadioState = cfun.state;
256     }
257 
258     if (changed && mRadioModemIndication) {
259         mRadioModemIndication->radioStateChanged(
260             RadioIndicationType::UNSOLICITED, cfun.state);
261     }
262 }
263 
setResponseFunctions(const std::shared_ptr<modem::IRadioModemResponse> & radioModemResponse,const std::shared_ptr<modem::IRadioModemIndication> & radioModemIndication)264 ScopedAStatus RadioModem::setResponseFunctions(
265         const std::shared_ptr<modem::IRadioModemResponse>& radioModemResponse,
266         const std::shared_ptr<modem::IRadioModemIndication>& radioModemIndication) {
267     mRadioModemResponse = NOT_NULL(radioModemResponse);
268     mRadioModemIndication = NOT_NULL(radioModemIndication);
269 
270     modem::RadioState radioState;
271 
272     {
273         std::lock_guard<std::mutex> lock(mMtx);
274         radioState = mRadioState;
275     }
276 
277     radioModemIndication->rilConnected(RadioIndicationType::UNSOLICITED);
278 
279     radioModemIndication->radioStateChanged(
280             RadioIndicationType::UNSOLICITED, radioState);
281 
282     return ScopedAStatus::ok();
283 }
284 
getSupportedRadioTechs(const AtChannel::RequestPipe requestPipe,AtChannel::Conversation & atConversation)285 std::pair<RadioError, uint32_t> RadioModem::getSupportedRadioTechs(
286             const AtChannel::RequestPipe requestPipe,
287             AtChannel::Conversation& atConversation) {
288     using ParseError = AtResponse::ParseError;
289     using CTEC = AtResponse::CTEC;
290     using ratUtils::ModemTechnology;
291 
292     AtResponsePtr response =
293         atConversation(requestPipe, atCmds::getSupportedRadioTechs,
294                         [](const AtResponse& response) -> bool {
295                             return response.holds<CTEC>();
296                         });
297     if (!response || response->isParseError()) {
298         return {FAILURE(RadioError::INTERNAL_ERR), 0};
299     } else if (const CTEC* ctec = response->get_if<CTEC>()) {
300         uint32_t rafBitmask = 0;
301 
302         for (const std::string& mtechStr : ctec->values) {
303             int mtech;
304             std::from_chars(&*mtechStr.begin(), &*mtechStr.end(), mtech, 10);
305 
306             rafBitmask |= ratUtils::supportedRadioTechBitmask(
307                 static_cast<ModemTechnology>(mtech));
308         }
309 
310         return {RadioError::NONE, rafBitmask};
311     } else {
312         response->unexpected(FAILURE_DEBUG_PREFIX, __func__);
313     }
314 }
315 
setRadioPowerImpl(const AtChannel::RequestPipe requestPipe,const bool powerOn)316 bool RadioModem::setRadioPowerImpl(const AtChannel::RequestPipe requestPipe,
317                                    const bool powerOn) {
318     {
319         std::lock_guard<std::mutex> lock(mMtx);
320         if (powerOn == (mRadioState == modem::RadioState::ON)) {
321             return true;
322         }
323     }
324 
325     const std::string request = std::format("AT+CFUN={0:d}", powerOn ? 1 : 0);
326     if (!requestPipe(request)) {
327         return FAILURE(false);
328     }
329 
330     // to broadcast CFUN from the listening thread
331     if (!requestPipe(atCmds::getModemPowerState)) {
332         return FAILURE(false);
333     }
334 
335     using modem::RadioState;
336 
337     const modem::RadioState newState =
338         powerOn ? RadioState::ON : RadioState::OFF;
339 
340     {
341         std::lock_guard<std::mutex> lock(mMtx);
342         mRadioState = newState;
343     }
344 
345     NOT_NULL(mRadioModemIndication)->radioStateChanged(
346             RadioIndicationType::UNSOLICITED, newState);
347 
348     return true;
349 }
350 
351 /************************* deprecated *************************/
getDeviceIdentity(const int32_t serial)352 ScopedAStatus RadioModem::getDeviceIdentity(const int32_t serial) {
353     NOT_NULL(mRadioModemResponse)->getDeviceIdentityResponse(
354         makeRadioResponseInfoDeprecated(serial),
355         "", "", "", "");
356     return ScopedAStatus::ok();
357 }
358 
nvReadItem(const int32_t serial,modem::NvItem)359 ScopedAStatus RadioModem::nvReadItem(const int32_t serial, modem::NvItem) {
360     NOT_NULL(mRadioModemResponse)->nvReadItemResponse(
361         makeRadioResponseInfoDeprecated(serial), "");
362     return ScopedAStatus::ok();
363 }
364 
nvResetConfig(const int32_t serial,modem::ResetNvType)365 ScopedAStatus RadioModem::nvResetConfig(const int32_t serial, modem::ResetNvType) {
366     NOT_NULL(mRadioModemResponse)->nvResetConfigResponse(
367         makeRadioResponseInfoDeprecated(serial));
368     return ScopedAStatus::ok();
369 }
370 
nvWriteCdmaPrl(const int32_t serial,const std::vector<uint8_t> &)371 ScopedAStatus RadioModem::nvWriteCdmaPrl(const int32_t serial, const std::vector<uint8_t>&) {
372     NOT_NULL(mRadioModemResponse)->nvWriteCdmaPrlResponse(
373         makeRadioResponseInfoDeprecated(serial));
374     return ScopedAStatus::ok();
375 }
376 
nvWriteItem(const int32_t serial,const modem::NvWriteItem &)377 ScopedAStatus RadioModem::nvWriteItem(const int32_t serial, const modem::NvWriteItem&) {
378     NOT_NULL(mRadioModemResponse)->nvWriteItemResponse(
379         makeRadioResponseInfoDeprecated(serial));
380     return ScopedAStatus::ok();
381 }
382 
383 }  // namespace implementation
384 }  // namespace radio
385 }  // namespace hardware
386 }  // namespace android
387 }  // namespace aidl
388