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 "RadioData"
18
19 #include <format>
20 #include <string_view>
21
22 #include <arpa/inet.h>
23 #include <net/if.h>
24 #include <netinet/in.h>
25 #include <sys/socket.h>
26
27 #include "RadioData.h"
28
29 #include "debug.h"
30 #include "makeRadioResponseInfo.h"
31
32 namespace aidl {
33 namespace android {
34 namespace hardware {
35 namespace radio {
36 namespace implementation {
37 using data::DataProfileInfo;
38 using data::PdpProtocolType;
39 using data::SetupDataCallResult;
40
41 namespace {
42 constexpr char kInterfaceName[] = "eth0";
43
getProtocolStr(const PdpProtocolType p)44 std::string_view getProtocolStr(const PdpProtocolType p) {
45 using namespace std::literals;
46
47 switch (p) {
48 case PdpProtocolType::IP: return "IP"sv;
49 case PdpProtocolType::IPV6: return "IPV6"sv;
50 case PdpProtocolType::IPV4V6: return "IPV4V6"sv;
51 case PdpProtocolType::PPP: return "PPP"sv;
52 case PdpProtocolType::NON_IP: return "NON_IP"sv;
53 case PdpProtocolType::UNSTRUCTURED: return "UNSTRUCTURED"sv;
54 default: return {};
55 }
56 }
57
formatCGDCONT(const int cid,const PdpProtocolType protocol,const std::string_view apn)58 std::string formatCGDCONT(const int cid,
59 const PdpProtocolType protocol,
60 const std::string_view apn) {
61 const std::string_view protocolStr = getProtocolStr(protocol);
62 if (protocolStr.empty()) {
63 return FAILURE_V("", "Unexpected protocol: %s", toString(protocol).c_str());
64 }
65
66 if (apn.empty()) {
67 return FAILURE_V("", "%s", "APN is empty");
68 }
69
70 return std::format("AT+CGDCONT={0:d},\"{1:s}\",\"{2:s}\",,0,0",
71 cid, protocolStr, apn);
72 }
73
setInterfaceState(const char * interfaceName,const bool on)74 bool setInterfaceState(const char* interfaceName, const bool on) {
75 const int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
76 if (sock < 0) {
77 return FAILURE_V(false, "Failed to open interface socket: %s (%d)",
78 strerror(errno), errno);
79 }
80
81 struct ifreq request;
82 memset(&request, 0, sizeof(request));
83 strncpy(request.ifr_name, interfaceName, sizeof(request.ifr_name));
84 request.ifr_name[sizeof(request.ifr_name) - 1] = '\0';
85
86 if (ioctl(sock, SIOCGIFFLAGS, &request)) {
87 ::close(sock);
88 return FAILURE_V(false, "Failed to get interface flags for %s: %s (%d)",
89 interfaceName, strerror(errno), errno);
90 }
91
92 if (((request.ifr_flags & IFF_UP) != 0) == on) {
93 ::close(sock);
94 return true; // Interface already in desired state
95 }
96
97 request.ifr_flags ^= IFF_UP;
98 if (ioctl(sock, SIOCSIFFLAGS, &request)) {
99 ::close(sock);
100 return FAILURE_V(false, "Failed to set interface flags for %s: %s (%d)",
101 interfaceName, strerror(errno), errno);
102 }
103
104 ::close(sock);
105 return true;
106 }
107
setIpAddr(const char * addr,const int addrSize,const char * radioInterfaceName)108 bool setIpAddr(const char *addr, const int addrSize,
109 const char* radioInterfaceName) {
110 const int family = strchr(addr, ':') ? AF_INET6 : AF_INET;
111 const int sock = socket(family, SOCK_DGRAM, 0);
112 if (sock == -1) {
113 return FAILURE_V(false, "Failed to open a %s socket: %s (%d)",
114 ((family == AF_INET) ? "INET" : "INET6"),
115 strerror(errno), errno);
116 }
117
118 struct ifreq req4;
119 memset(&req4, 0, sizeof(req4));
120
121 strncpy(req4.ifr_name, radioInterfaceName, sizeof(req4.ifr_name));
122 req4.ifr_name[sizeof(req4.ifr_name) - 1] = '\0';
123
124 if (family == AF_INET) {
125 struct sockaddr_in *sin = (struct sockaddr_in *)&req4.ifr_addr;
126 sin->sin_family = AF_INET;
127 sin->sin_addr.s_addr = inet_addr(addr);
128 if (ioctl(sock, SIOCSIFADDR, &req4) < 0) {
129 ::close(sock);
130 return FAILURE_V(false, "SIOCSIFADDR IPv4 failed: %s (%d)",
131 strerror(errno), errno);
132 }
133
134 sin->sin_addr.s_addr = htonl(0xFFFFFFFFu << (32 - (addrSize ? addrSize : 32)));
135 if (ioctl(sock, SIOCSIFNETMASK, &req4) < 0) {
136 ::close(sock);
137 return FAILURE_V(false, "SIOCSIFNETMASK IPv4 failed: %s (%d)",
138 strerror(errno), errno);
139 }
140 } else {
141 if (ioctl(sock, SIOCGIFINDEX, &req4) < 0) {
142 ::close(sock);
143 return FAILURE_V(false, "SIOCGIFINDEX IPv6 failed: %s (%d)",
144 strerror(errno), errno);
145 }
146
147 struct in6_ifreq req6 = {
148 .ifr6_prefixlen = static_cast<__u32>(addrSize ? addrSize : 128),
149 .ifr6_ifindex = req4.ifr_ifindex,
150 };
151
152 if (inet_pton(AF_INET6, addr, &req6.ifr6_addr) != 1) {
153 ::close(sock);
154 return FAILURE_V(false, "inet_pton(AF_INET6, '%s') failed: %s (%d)",
155 addr, strerror(errno), errno);
156 }
157
158 if (ioctl(sock, SIOCSIFADDR, &req6) < 0) {
159 ::close(sock);
160 return FAILURE_V(false, "SIOCSIFADDR failed: %s (%d)",
161 strerror(errno), errno);
162 }
163 }
164
165 ::close(sock);
166 return true;
167 }
168
169 } // namespace
170
RadioData(std::shared_ptr<AtChannel> atChannel)171 RadioData::RadioData(std::shared_ptr<AtChannel> atChannel) : mAtChannel(std::move(atChannel)) {
172 }
173
getSlicingConfig(const int32_t serial)174 ScopedAStatus RadioData::getSlicingConfig(const int32_t serial) {
175 // matches reference-ril.c
176 NOT_NULL(mRadioDataResponse)->getSlicingConfigResponse(
177 makeRadioResponseInfo(serial), {});
178 return ScopedAStatus::ok();
179 }
180
setDataAllowed(const int32_t serial,const bool)181 ScopedAStatus RadioData::setDataAllowed(const int32_t serial,
182 const bool /*allow*/) {
183 // matches reference-ril.c
184 NOT_NULL(mRadioDataResponse)->setDataAllowedResponse(
185 makeRadioResponseInfo(serial));
186 return ScopedAStatus::ok();
187 }
188
setDataProfile(const int32_t serial,const std::vector<DataProfileInfo> &)189 ScopedAStatus RadioData::setDataProfile(const int32_t serial,
190 const std::vector<DataProfileInfo>& /*profiles*/) {
191 // matches reference-ril.c
192 NOT_NULL(mRadioDataResponse)->setDataProfileResponse(
193 makeRadioResponseInfo(serial));
194 return ScopedAStatus::ok();
195 }
196
setDataThrottling(const int32_t serial,const data::DataThrottlingAction,const int64_t)197 ScopedAStatus RadioData::setDataThrottling(const int32_t serial,
198 const data::DataThrottlingAction /*dataThrottlingAction*/,
199 const int64_t /*completionDurationMillis*/) {
200 // matches reference-ril.c
201 NOT_NULL(mRadioDataResponse)->setDataThrottlingResponse(
202 makeRadioResponseInfo(serial));
203 return ScopedAStatus::ok();
204 }
205
setInitialAttachApn(const int32_t serial,const std::optional<DataProfileInfo> &)206 ScopedAStatus RadioData::setInitialAttachApn(const int32_t serial,
207 const std::optional<DataProfileInfo>& /*maybeDpInfo*/) {
208 // matches reference-ril.c
209 NOT_NULL(mRadioDataResponse)->setInitialAttachApnResponse(
210 makeRadioResponseInfo(serial));
211 return ScopedAStatus::ok();
212 }
213
allocatePduSessionId(const int32_t serial)214 ScopedAStatus RadioData::allocatePduSessionId(const int32_t serial) {
215 NOT_NULL(mRadioDataResponse)->allocatePduSessionIdResponse(
216 makeRadioResponseInfoUnsupported( // matches reference-ril.c
217 serial, FAILURE_DEBUG_PREFIX, __func__), 0);
218 return ScopedAStatus::ok();
219 }
220
releasePduSessionId(const int32_t serial,const int32_t)221 ScopedAStatus RadioData::releasePduSessionId(const int32_t serial,
222 const int32_t /*id*/) {
223 NOT_NULL(mRadioDataResponse)->releasePduSessionIdResponse(
224 makeRadioResponseInfo(serial));
225 return ScopedAStatus::ok();
226 }
227
setupDataCall(const int32_t serial,const AccessNetwork,const DataProfileInfo & dataProfileInfo,const bool,const data::DataRequestReason,const std::vector<data::LinkAddress> &,const std::vector<std::string> &,const int32_t pduSessionId,const std::optional<data::SliceInfo> &,const bool)228 ScopedAStatus RadioData::setupDataCall(const int32_t serial,
229 const AccessNetwork /*accessNetwork*/,
230 const DataProfileInfo& dataProfileInfo,
231 const bool /*roamingAllowed*/,
232 const data::DataRequestReason /*reason*/,
233 const std::vector<data::LinkAddress>& /*addresses*/,
234 const std::vector<std::string>& /*dnses*/,
235 const int32_t pduSessionId,
236 const std::optional<data::SliceInfo>& /*sliceInfo*/,
237 const bool /*matchAllRuleAllowed*/) {
238 if (!setInterfaceState(kInterfaceName, true)) {
239 NOT_NULL(mRadioDataResponse)->setupDataCallResponse(
240 makeRadioResponseInfo(serial, FAILURE(RadioError::GENERIC_FAILURE)), {});
241 return ScopedAStatus::ok();
242 }
243
244 static const char* const kFunc = __func__;
245 mAtChannel->queueRequester([this, serial, dataProfileInfo, pduSessionId]
246 (const AtChannel::RequestPipe requestPipe) -> bool {
247 using CmeError = AtResponse::CmeError;
248 using CGCONTRDP = AtResponse::CGCONTRDP;
249
250 RadioError status;
251 const int32_t cid = allocateId();
252
253 std::string request = formatCGDCONT(cid, dataProfileInfo.protocol,
254 dataProfileInfo.apn);
255 if (request.empty()) {
256 status = RadioError::INVALID_ARGUMENTS;
257
258 failed: releaseId(cid);
259 NOT_NULL(mRadioDataResponse)->setupDataCallResponse(
260 makeRadioResponseInfo(serial, FAILURE(status)), {});
261 return status != RadioError::INTERNAL_ERR;
262 }
263
264 AtResponsePtr response =
265 mAtConversation(requestPipe, request,
266 [](const AtResponse& response) -> bool {
267 return response.holds<AtResponse::OK>() ||
268 response.holds<CmeError>();
269 });
270 if (!response) {
271 status = FAILURE(RadioError::INTERNAL_ERR);
272 goto failed;
273 } else if (const CmeError* err = response->get_if<CmeError>()) {
274 status = FAILURE_V(err->error, "%s", toString(err->error).c_str());
275 goto failed;
276 } else if (!response->holds<AtResponse::OK>()) {
277 response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
278 }
279
280 SetupDataCallResult setupDataCallResult = {
281 .suggestedRetryTime = -1,
282 .cid = cid,
283 .active = SetupDataCallResult::DATA_CONNECTION_STATUS_ACTIVE,
284 .type = dataProfileInfo.protocol,
285 .ifname = kInterfaceName,
286 .mtuV4 = 1500,
287 .mtuV6 = 1500,
288 .handoverFailureMode = SetupDataCallResult::HANDOVER_FAILURE_MODE_LEGACY,
289 .pduSessionId = pduSessionId,
290 };
291
292 request = std::format("AT+CGCONTRDP={0:d}", cid);
293 response =
294 mAtConversation(requestPipe, request,
295 [](const AtResponse& response) -> bool {
296 return response.holds<CGCONTRDP>() ||
297 response.holds<CmeError>();
298 });
299 if (!response || response->isParseError()) {
300 status = FAILURE(RadioError::INTERNAL_ERR);
301 goto failed;
302 } else if (const CGCONTRDP* cgcontrdp = response->get_if<CGCONTRDP>()) {
303 if (!setIpAddr(cgcontrdp->localAddr.c_str(),
304 cgcontrdp->localAddrSize,
305 setupDataCallResult.ifname.c_str())) {
306 status = FAILURE(RadioError::GENERIC_FAILURE);
307 goto failed;
308 }
309
310 const auto makeLinkAddress = [](const std::string_view address,
311 const size_t addrSize) -> data::LinkAddress {
312 return {
313 .address = std::format("{0:s}/{1:d}", address, addrSize),
314 .addressProperties = 0,
315 .deprecationTime = -1,
316 .expirationTime = -1,
317 };
318 };
319
320 setupDataCallResult.addresses.push_back(
321 makeLinkAddress(cgcontrdp->localAddr, cgcontrdp->localAddrSize));
322 setupDataCallResult.gateways.push_back(cgcontrdp->gwAddr);
323 setupDataCallResult.dnses.push_back(cgcontrdp->dns1);
324 if (!cgcontrdp->dns2.empty()) {
325 setupDataCallResult.dnses.push_back(cgcontrdp->dns2);
326 }
327
328 std::lock_guard<std::mutex> lock(mMtx);
329 mDataCalls.insert({ cid, setupDataCallResult });
330 status = RadioError::NONE;
331 } else if (const CmeError* err = response->get_if<CmeError>()) {
332 status = FAILURE_V(err->error, "%s", toString(err->error).c_str());
333 goto failed;
334 } else {
335 response->unexpected(FAILURE_DEBUG_PREFIX, kFunc);
336 }
337
338 NOT_NULL(mRadioDataResponse)->setupDataCallResponse(
339 makeRadioResponseInfo(serial), std::move(setupDataCallResult));
340
341 NOT_NULL(mRadioDataIndication)->dataCallListChanged(
342 RadioIndicationType::UNSOLICITED, getDataCalls());
343 return true;
344 });
345
346 return ScopedAStatus::ok();
347 }
348
deactivateDataCall(const int32_t serial,const int32_t cid,const data::DataRequestReason)349 ScopedAStatus RadioData::deactivateDataCall(
350 const int32_t serial, const int32_t cid,
351 const data::DataRequestReason /*reason*/) {
352 bool removed;
353 bool empty;
354 {
355 std::lock_guard<std::mutex> lock(mMtx);
356 const auto i = mDataCalls.find(cid);
357 if (i != mDataCalls.end()) {
358 mDataCalls.erase(i);
359 mIdAllocator.put(cid);
360 removed = true;
361 } else {
362 removed = false;
363 }
364 empty = mDataCalls.empty();
365 }
366
367 if (empty) {
368 setInterfaceState(kInterfaceName, false);
369 }
370
371 if (removed) {
372 NOT_NULL(mRadioDataResponse)->deactivateDataCallResponse(
373 makeRadioResponseInfo(serial));
374
375 NOT_NULL(mRadioDataIndication)->dataCallListChanged(
376 RadioIndicationType::UNSOLICITED, getDataCalls());
377 } else {
378 NOT_NULL(mRadioDataResponse)->deactivateDataCallResponse(
379 makeRadioResponseInfo(serial, FAILURE(RadioError::INVALID_ARGUMENTS)));
380 }
381
382 return ScopedAStatus::ok();
383 }
384
getDataCallList(const int32_t serial)385 ScopedAStatus RadioData::getDataCallList(const int32_t serial) {
386 NOT_NULL(mRadioDataResponse)->getDataCallListResponse(
387 makeRadioResponseInfo(serial), getDataCalls());
388 return ScopedAStatus::ok();
389 }
390
startHandover(const int32_t serial,const int32_t)391 ScopedAStatus RadioData::startHandover(const int32_t serial,
392 const int32_t /*callId*/) {
393 NOT_NULL(mRadioDataResponse)->startHandoverResponse(
394 makeRadioResponseInfoUnsupported( // matches reference-ril.c
395 serial, FAILURE_DEBUG_PREFIX, __func__));
396 return ScopedAStatus::ok();
397 }
398
cancelHandover(const int32_t serial,const int32_t)399 ScopedAStatus RadioData::cancelHandover(const int32_t serial,
400 const int32_t /*callId*/) {
401 NOT_NULL(mRadioDataResponse)->cancelHandoverResponse(
402 makeRadioResponseInfoUnsupported( // matches reference-ril.c
403 serial, FAILURE_DEBUG_PREFIX, __func__));
404 return ScopedAStatus::ok();
405 }
406
startKeepalive(const int32_t serial,const data::KeepaliveRequest &)407 ScopedAStatus RadioData::startKeepalive(const int32_t serial,
408 const data::KeepaliveRequest& /*keepalive*/) {
409 const int32_t sessionHandle = allocateId();
410
411 {
412 std::lock_guard<std::mutex> lock(mMtx);
413 mKeepAliveSessions.insert(sessionHandle);
414 }
415
416 using data::KeepaliveStatus;
417
418 KeepaliveStatus keepaliveStatus = {
419 .sessionHandle = sessionHandle,
420 .code = KeepaliveStatus::CODE_ACTIVE,
421 };
422
423 NOT_NULL(mRadioDataResponse)->startKeepaliveResponse(
424 makeRadioResponseInfo(serial), std::move(keepaliveStatus));
425 return ScopedAStatus::ok();
426 }
427
stopKeepalive(const int32_t serial,const int32_t sessionHandle)428 ScopedAStatus RadioData::stopKeepalive(const int32_t serial,
429 const int32_t sessionHandle) {
430 bool removed;
431 {
432 std::lock_guard<std::mutex> lock(mMtx);
433 removed = mKeepAliveSessions.erase(sessionHandle) > 0;
434 }
435
436 if (removed) {
437 releaseId(sessionHandle);
438 }
439
440 NOT_NULL(mRadioDataResponse)->stopKeepaliveResponse(
441 makeRadioResponseInfo(serial, removed ?
442 RadioError::NONE : FAILURE(RadioError::INVALID_ARGUMENTS)));
443 return ScopedAStatus::ok();
444 }
445
responseAcknowledgement()446 ScopedAStatus RadioData::responseAcknowledgement() {
447 return ScopedAStatus::ok();
448 }
449
atResponseSink(const AtResponsePtr & response)450 void RadioData::atResponseSink(const AtResponsePtr& response) {
451 if (!mAtConversation.send(response)) {
452 response->visit([this](const auto& msg){ handleUnsolicited(msg); });
453 }
454 }
455
setResponseFunctions(const std::shared_ptr<data::IRadioDataResponse> & radioDataResponse,const std::shared_ptr<data::IRadioDataIndication> & radioDataIndication)456 ScopedAStatus RadioData::setResponseFunctions(
457 const std::shared_ptr<data::IRadioDataResponse>& radioDataResponse,
458 const std::shared_ptr<data::IRadioDataIndication>& radioDataIndication) {
459 mRadioDataResponse = NOT_NULL(radioDataResponse);
460 mRadioDataIndication = NOT_NULL(radioDataIndication);
461 return ScopedAStatus::ok();
462 }
463
allocateId()464 int32_t RadioData::allocateId() {
465 std::lock_guard<std::mutex> lock(mMtx);
466 return mIdAllocator.get();
467 }
468
releaseId(const int32_t cid)469 void RadioData::releaseId(const int32_t cid) {
470 std::lock_guard<std::mutex> lock(mMtx);
471 mIdAllocator.put(cid);
472 }
473
getDataCalls() const474 std::vector<SetupDataCallResult> RadioData::getDataCalls() const {
475 std::vector<SetupDataCallResult> dataCalls;
476
477 std::lock_guard<std::mutex> lock(mMtx);
478 for (const auto& kv : mDataCalls) {
479 dataCalls.push_back(kv.second);
480 }
481
482 return dataCalls;
483 }
484
485 } // namespace implementation
486 } // namespace radio
487 } // namespace hardware
488 } // namespace android
489 } // namespace aidl
490