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