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 }