File indexing completed on 2024-04-28 16:42:53
0001 // SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru> 0002 // 0003 // SPDX-License-Identifier: LicenseRef-KDE-Accepted-GPL 0004 0005 #include "mm-modem-controller.h" 0006 0007 ModemManagerController::ModemManagerController(QObject *parent) 0008 : ModemController(parent) 0009 { 0010 _init(); 0011 connect(ModemManager::notifier(), &ModemManager::Notifier::modemAdded, this, [this](const QString &udi) { 0012 Q_UNUSED(udi); 0013 _init(); 0014 }); 0015 connect(ModemManager::notifier(), &ModemManager::Notifier::modemRemoved, this, [this](const QString &udi) { 0016 Q_UNUSED(udi); 0017 _init(); 0018 }); 0019 } 0020 0021 QString ModemManagerController::subsystem() 0022 { 0023 return QStringLiteral("mm"); 0024 } 0025 0026 QString ModemManagerController::equipmentIdentifier(const QString &deviceUni) 0027 { 0028 QString equipmentIdentifier; 0029 const auto modem = ModemManager::findModemDevice(deviceUni); 0030 if (modem.isNull()) { 0031 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0032 return equipmentIdentifier; 0033 } 0034 const auto modemIface = modem->modemInterface(); 0035 if (modemIface.isNull()) { 0036 qDebug() << Q_FUNC_INFO << "modemIface not found"; 0037 return equipmentIdentifier; 0038 } 0039 return modemIface->equipmentIdentifier(); 0040 } 0041 0042 void ModemManagerController::ussdInitiate(const QString &deviceUni, const QString &command) 0043 { 0044 const auto modem = ModemManager::findModemDevice(deviceUni); 0045 if (modem.isNull()) { 0046 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0047 Q_EMIT ussdErrorReceived(deviceUni, tr("deviceUni not found")); 0048 return; 0049 } 0050 const auto modem3gppUssdInterface = _modem3gppUssdInterface(modem); 0051 QDBusPendingReply<QString> reply = modem3gppUssdInterface->initiate(command); 0052 /* 0053 During the following call to waitForFinished(), the DBus client waits for the DBus server to send a reply. 0054 In the meantime, the ModemManager instance will send the USSD request and wait for an USSD response. 0055 Once the ModemManager instance has received an USSD response, it will send a DBus response as well as a state update, in a possibly undefined order. 0056 Even if the state update would be sent before the DBus response, it will be received from this code after the DBus response, as waitForFinished() waits for the correct DBus response and queues any other DBus activity (such as the state update). 0057 Hence, the state update will always be received later than waitForFinished() returns. 0058 Hence, the ussdInitiateComplete() call will contain the correct message, but the corresponding state update will only happen (milliseconds) later. 0059 */ 0060 reply.waitForFinished(); 0061 if (reply.isError()) { 0062 qDebug() << Q_FUNC_INFO << reply.error(); 0063 Q_EMIT ussdErrorReceived(deviceUni, reply.error().message()); 0064 return; 0065 } 0066 Q_EMIT ussdInitiateComplete(deviceUni, reply.value()); 0067 } 0068 0069 void ModemManagerController::ussdRespond(const QString &deviceUni, const QString &reply) 0070 { 0071 const auto modem = ModemManager::findModemDevice(deviceUni); 0072 if (modem.isNull()) { 0073 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0074 Q_EMIT ussdErrorReceived(deviceUni, tr("deviceUni not found")); 0075 return; 0076 } 0077 const auto modem3gppUssdInterface = _modem3gppUssdInterface(modem); 0078 QDBusPendingReply<QString> dbusReply = modem3gppUssdInterface->respond(reply); 0079 dbusReply.waitForFinished(); 0080 if (dbusReply.isError()) { 0081 qDebug() << Q_FUNC_INFO << dbusReply.error(); 0082 Q_EMIT ussdErrorReceived(deviceUni, dbusReply.error().message()); 0083 return; 0084 } 0085 } 0086 0087 void ModemManagerController::ussdCancel(const QString &deviceUni) 0088 { 0089 const auto modem = ModemManager::findModemDevice(deviceUni); 0090 if (modem.isNull()) { 0091 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0092 Q_EMIT ussdErrorReceived(deviceUni, tr("deviceUni not found")); 0093 return; 0094 } 0095 const auto modem3gppUssdInterface = _modem3gppUssdInterface(modem); 0096 modem3gppUssdInterface->cancel(); 0097 } 0098 0099 QString ModemManagerController::callNumber(const QString &deviceUni, const QString &callUni) 0100 { 0101 QString callNumber; 0102 const auto modem = ModemManager::findModemDevice(deviceUni); 0103 if (modem.isNull()) { 0104 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0105 return callNumber; 0106 } 0107 const auto voiceInterface = _voiceInterface(modem); 0108 if (voiceInterface.isNull()) { 0109 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0110 return callNumber; 0111 } 0112 const auto call = voiceInterface->findCall(callUni); 0113 if (call.isNull()) { 0114 qDebug() << Q_FUNC_INFO << "call not found" << callUni; 0115 return callNumber; 0116 } 0117 return call->number(); 0118 } 0119 0120 void ModemManagerController::createCall(const QString &deviceUni, const QString &callUni) 0121 { 0122 qDebug() << Q_FUNC_INFO << "creating call"; 0123 const auto modem = ModemManager::findModemDevice(deviceUni); 0124 if (modem.isNull()) { 0125 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0126 return; 0127 } 0128 const auto voiceInterface = _voiceInterface(modem); 0129 if (voiceInterface.isNull()) { 0130 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0131 return; 0132 } 0133 QDBusPendingReply<QDBusObjectPath> reply = voiceInterface->createCall(callUni); 0134 reply.waitForFinished(); 0135 if (reply.isError()) { 0136 qDebug() << Q_FUNC_INFO << reply.error(); 0137 return; 0138 } 0139 } 0140 0141 void ModemManagerController::acceptCall(const QString &deviceUni, const QString &callUni) 0142 { 0143 const auto modem = ModemManager::findModemDevice(deviceUni); 0144 if (modem.isNull()) { 0145 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0146 return; 0147 } 0148 const auto voiceInterface = _voiceInterface(modem); 0149 if (voiceInterface.isNull()) { 0150 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0151 return; 0152 } 0153 const auto call = voiceInterface->findCall(callUni); 0154 if (call.isNull()) { 0155 qDebug() << Q_FUNC_INFO << "call not found" << callUni; 0156 return; 0157 } 0158 call->accept(); 0159 } 0160 0161 void ModemManagerController::hangUp(const QString &deviceUni, const QString &callUni) 0162 { 0163 const auto modem = ModemManager::findModemDevice(deviceUni); 0164 if (modem.isNull()) { 0165 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0166 return; 0167 } 0168 const auto voiceInterface = _voiceInterface(modem); 0169 if (voiceInterface.isNull()) { 0170 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0171 return; 0172 } 0173 const auto call = voiceInterface->findCall(callUni); 0174 if (call.isNull()) { 0175 qDebug() << Q_FUNC_INFO << "call not found" << callUni; 0176 return; 0177 } 0178 call->hangup(); 0179 } 0180 0181 void ModemManagerController::sendDtmf(const QString &deviceUni, const QString &callUni, const QString &tones) 0182 { 0183 const auto modem = ModemManager::findModemDevice(deviceUni); 0184 if (modem.isNull()) { 0185 qDebug() << Q_FUNC_INFO << "deviceUni not found:" << deviceUni; 0186 return; 0187 } 0188 const auto voiceInterface = _voiceInterface(modem); 0189 if (voiceInterface.isNull()) { 0190 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0191 return; 0192 } 0193 // never try to send + as a DTMF tone 0194 QString safeTones = tones; 0195 safeTones.replace(QStringLiteral("+"), QStringLiteral("0")); 0196 const auto call = voiceInterface->findCall(callUni); 0197 if (call.isNull()) { 0198 qDebug() << Q_FUNC_INFO << "call not found" << callUni; 0199 return; 0200 } 0201 call->sendDtmf(safeTones); 0202 } 0203 0204 DialerTypes::CallDataVector ModemManagerController::fetchCalls() 0205 { 0206 DialerTypes::CallDataVector callDataVector; 0207 auto calls = _calls; 0208 for (auto callObject : calls) { 0209 auto callData = _voiceCallData(callObject); 0210 callDataVector.append(callData); 0211 } 0212 return callDataVector; 0213 } 0214 0215 DialerTypes::CallData ModemManagerController::getCall(const QString &deviceUni, const QString &callUni) 0216 { 0217 DialerTypes::CallData callData; 0218 auto callObject = _getVoiceCallObject(deviceUni, callUni); 0219 if (callObject) { 0220 callData = _voiceCallData(callObject); 0221 } 0222 return callData; 0223 } 0224 0225 void ModemManagerController::deleteCall(const QString &deviceUni, const QString &callUni) 0226 { 0227 Q_UNUSED(deviceUni) // TODO: improve deviceUni getter 0228 for (const QSharedPointer<ModemManager::ModemDevice> &modemDevice : ModemManager::modemDevices()) { 0229 const auto voiceInterface = _voiceInterface(modemDevice); 0230 if (voiceInterface.isNull()) { 0231 qDebug() << Q_FUNC_INFO << "voiceInterface not found"; 0232 continue; 0233 } 0234 qDebug() << Q_FUNC_INFO << "deleting voice call" << callUni << "via" << modemDevice->uni(); 0235 voiceInterface->deleteCall(callUni); 0236 } 0237 } 0238 0239 void ModemManagerController::onServiceAppeared() 0240 { 0241 } 0242 0243 void ModemManagerController::onServiceDisappeared() 0244 { 0245 } 0246 0247 void ModemManagerController::onModemAdded(const QString &udi) 0248 { 0249 Q_UNUSED(udi); 0250 } 0251 0252 void ModemManagerController::onModemRemoved(const QString &udi) 0253 { 0254 Q_UNUSED(udi); 0255 } 0256 0257 void ModemManagerController::_init() 0258 { 0259 setDeviceUniList(QStringList()); 0260 const auto modemDevices = ModemManager::modemDevices(); 0261 if (modemDevices.isEmpty()) { 0262 qWarning() << "Could not find modem devices"; 0263 return; 0264 } 0265 for (const auto &device : modemDevices) { 0266 appendDeviceUni(device->uni()); 0267 // 3GPP 0268 const auto modem3gppInterface = _modem3gppInterface(device); 0269 if (modem3gppInterface.isNull()) { 0270 qDebug() << "Skipping 3GPP-related connections"; 0271 continue; 0272 } 0273 connect(modem3gppInterface.get(), &ModemManager::Modem3gpp::countryCodeChanged, this, [this](const QString &countryCode) { 0274 Q_EMIT countryCodeChanged(countryCode); 0275 }); 0276 0277 // 3GPPUSSD 0278 const auto modem3gppUssdInterface = _modem3gppUssdInterface(device); 0279 if (modem3gppInterface.isNull()) { 0280 qDebug() << "Skipping USSD-related connections"; 0281 continue; 0282 } 0283 connect(modem3gppUssdInterface.get(), 0284 &ModemManager::Modem3gppUssd::networkNotificationChanged, 0285 this, 0286 [this, device](const QString &networkNotification) { 0287 Q_EMIT ussdNotificationReceived(device->uni(), networkNotification); 0288 }); 0289 connect(modem3gppUssdInterface.get(), &ModemManager::Modem3gppUssd::networkRequestChanged, this, [this, device](const QString &networkRequest) { 0290 Q_EMIT ussdRequestReceived(device->uni(), networkRequest); 0291 }); 0292 connect(modem3gppUssdInterface.get(), &ModemManager::Modem3gppUssd::stateChanged, this, [this, device](MMModem3gppUssdSessionState state) { 0293 QString stateString = QStringLiteral("unknown"); 0294 switch (state) { 0295 case (MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE): 0296 stateString = QStringLiteral("idle"); 0297 break; 0298 case (MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE): 0299 stateString = QStringLiteral("active"); 0300 break; 0301 case (MM_MODEM_3GPP_USSD_SESSION_STATE_USER_RESPONSE): 0302 stateString = QStringLiteral("user-response"); 0303 break; 0304 default: 0305 break; 0306 } 0307 Q_EMIT ussdStateChanged(device->uni(), stateString); 0308 }); 0309 0310 // Voice 0311 const auto voiceInterface = _voiceInterface(device); 0312 if (voiceInterface.isNull()) { 0313 qDebug() << "Skipping Voice-related connections"; 0314 continue; 0315 } 0316 connect(voiceInterface.get(), &ModemManager::ModemVoice::callAdded, this, [this, device, voiceInterface](const QString &callUni) { 0317 qDebug() << Q_FUNC_INFO << "call added, initiating"; 0318 ModemManager::Call::Ptr call = voiceInterface->findCall(callUni); 0319 _initAddedCall(device, call); 0320 }); 0321 connect(voiceInterface.get(), &ModemManager::ModemVoice::callDeleted, this, [this, device](const QString &callUni) { 0322 _removeCall(callUni); 0323 Q_EMIT callDeleted(device->uni(), callUni); 0324 }); 0325 0326 // SMS/MMS 0327 const auto msgManager = device->messagingInterface(); 0328 connect(msgManager.get(), &ModemManager::ModemMessaging::messageAdded, this, [this, device, msgManager](const QString &uni, bool received) { 0329 ModemManager::Sms::Ptr msg = msgManager->findMessage(uni); 0330 Q_ASSERT(msg); 0331 QVariantMap message; 0332 message[QStringLiteral("number")] = msg->number(); 0333 message[QStringLiteral("text")] = msg->text(); 0334 message[QStringLiteral("data")] = msg->data(); 0335 Q_EMIT messageAdded(device->uni(), message, received); 0336 }); 0337 } 0338 0339 _fetchModemCalls(); 0340 } 0341 0342 void ModemManagerController::_removeCall(const QString &callId) 0343 { 0344 _calls.erase(std::remove_if(_calls.begin(), 0345 _calls.end(), 0346 [callId](CallObject *callObject) { 0347 return (callObject->id() == callId); 0348 }), 0349 _calls.end()); 0350 } 0351 0352 QSharedPointer<ModemManager::Modem3gpp> ModemManagerController::_modem3gppInterface(const QSharedPointer<ModemManager::ModemDevice> modemDevice) 0353 { 0354 return modemDevice->interface(ModemManager::ModemDevice::GsmInterface).objectCast<ModemManager::Modem3gpp>(); 0355 } 0356 0357 QSharedPointer<ModemManager::Modem3gppUssd> ModemManagerController::_modem3gppUssdInterface(const QSharedPointer<ModemManager::ModemDevice> modemDevice) 0358 { 0359 return modemDevice->interface(ModemManager::ModemDevice::GsmUssdInterface).objectCast<ModemManager::Modem3gppUssd>(); 0360 } 0361 0362 QSharedPointer<ModemManager::ModemVoice> ModemManagerController::_voiceInterface(const QSharedPointer<ModemManager::ModemDevice> modemDevice) 0363 { 0364 return modemDevice->interface(ModemManager::ModemDevice::VoiceInterface).objectCast<ModemManager::ModemVoice>(); 0365 } 0366 0367 CallObject *ModemManagerController::_voiceCallObject(const QSharedPointer<ModemManager::ModemDevice> &device, 0368 const QSharedPointer<ModemManager::Call> &call, 0369 QObject *parent) 0370 { 0371 auto callObject = new CallObject(parent); 0372 callObject->setId(call->uni()); 0373 callObject->setProtocol(QStringLiteral("tel")); 0374 callObject->setProvider(device->sim()->operatorIdentifier()); 0375 callObject->setAccount(device->sim()->simIdentifier()); 0376 callObject->setCommunicationWith(call->number()); 0377 callObject->setDirection(static_cast<DialerTypes::CallDirection>(call->direction())); 0378 callObject->setState(static_cast<DialerTypes::CallState>(call->state())); 0379 callObject->setStateReason(static_cast<DialerTypes::CallStateReason>(call->stateReason())); 0380 callObject->setCallAttemptDuration(0); 0381 callObject->setStartedAt(QDateTime::currentDateTime()); 0382 callObject->setDuration(0); 0383 return callObject; 0384 } 0385 0386 DialerTypes::CallData ModemManagerController::_voiceCallData(CallObject *callObject) 0387 { 0388 DialerTypes::CallData callData; 0389 callData.id = callObject->id(); 0390 0391 callData.protocol = callObject->protocol(); 0392 callData.provider = callObject->provider(); 0393 callData.account = callObject->account(); 0394 0395 callData.communicationWith = callObject->communicationWith(); 0396 callData.direction = callObject->direction(); 0397 callData.state = callObject->state(); 0398 callData.stateReason = callObject->stateReason(); 0399 0400 callData.callAttemptDuration = callObject->callAttemptDuration(); 0401 callData.startedAt = callObject->startedAt(); 0402 callData.duration = callObject->duration(); 0403 return callData; 0404 } 0405 0406 void ModemManagerController::_fetchModemCalls() 0407 { 0408 _calls.clear(); 0409 const auto modemDevices = ModemManager::modemDevices(); 0410 if (modemDevices.isEmpty()) { 0411 qWarning() << "Could not find modem devices"; 0412 return; 0413 } 0414 for (const auto &device : modemDevices) { 0415 const auto voiceInterface = this->_voiceInterface(device); 0416 if (voiceInterface.isNull()) { 0417 qDebug() << "Skipping device without voice interface"; 0418 continue; 0419 } 0420 const auto voiceCalls = voiceInterface->calls(); 0421 for (const auto &call : voiceCalls) { 0422 _initAddedCall(device, call); 0423 }; 0424 }; 0425 } 0426 0427 CallObject *ModemManagerController::_getVoiceCallObject(const QString &deviceUni, const QString &callUni) 0428 { 0429 Q_UNUSED(deviceUni); 0430 DialerTypes::CallData callData; 0431 auto calls = _calls; 0432 for (auto callObject : calls) { 0433 if (callObject->id() == callUni) { 0434 return callObject; 0435 } 0436 } 0437 return nullptr; 0438 } 0439 0440 void ModemManagerController::_initAddedCall(const QSharedPointer<ModemManager::ModemDevice> &device, const QSharedPointer<ModemManager::Call> &call) 0441 { 0442 qDebug() << Q_FUNC_INFO << "call details:" << call->direction() << call->state() << call->stateReason(); 0443 auto voiceCallObject = _voiceCallObject(device, call, this); 0444 _calls.append(voiceCallObject); 0445 connect(call.get(), 0446 &ModemManager::Call::stateChanged, 0447 this, 0448 [this, device, call, voiceCallObject](MMCallState oldState, MMCallState newState, MMCallStateReason reason) { 0449 Q_UNUSED(oldState); 0450 qDebug() << Q_FUNC_INFO << "call state changed, initiating"; 0451 auto callDirection = static_cast<DialerTypes::CallDirection>(call->direction()); 0452 auto callState = static_cast<DialerTypes::CallState>(newState); 0453 auto callReason = static_cast<DialerTypes::CallStateReason>(reason); 0454 if (voiceCallObject) { 0455 voiceCallObject->onCallStateChanged(device->uni(), call->uni(), callDirection, callState, callReason); 0456 } 0457 Q_EMIT callStateChanged(_voiceCallData(voiceCallObject)); 0458 }); 0459 Q_EMIT callAdded(device->uni(), 0460 call->uni(), 0461 static_cast<DialerTypes::CallDirection>(call->direction()), 0462 static_cast<DialerTypes::CallState>(call->state()), 0463 static_cast<DialerTypes::CallStateReason>(call->stateReason()), 0464 call->number()); 0465 if ((call->state() == MM_CALL_STATE_UNKNOWN) && call->direction() == MM_CALL_DIRECTION_OUTGOING) { 0466 qDebug() << Q_FUNC_INFO << "automatically starting outgoing call"; 0467 call->start(); 0468 } 0469 }