From 6cd2c5e5baeec8354a31e29ea38c41ffbb02f740 Mon Sep 17 00:00:00 2001 From: uncor3 Date: Fri, 20 Mar 2026 20:26:36 +0300 Subject: [PATCH] cleanup pairing process and improve heartbeat management --- src/appcontext.cpp | 395 +++++++++++++++--------------- src/appcontext.h | 3 + src/core/services/init_device.cpp | 23 +- src/heartbeat.h | 4 + src/iDescriptor.h | 8 +- 5 files changed, 221 insertions(+), 212 deletions(-) diff --git a/src/appcontext.cpp b/src/appcontext.cpp index 99b3846..4c7bad2 100644 --- a/src/appcontext.cpp +++ b/src/appcontext.cpp @@ -21,7 +21,6 @@ #include "devicemonitor.h" #include "iDescriptor.h" #include "mainwindow.h" -#include "networkdevicemanager.h" #include "settingsmanager.h" #include #include @@ -256,198 +255,7 @@ void AppContext::addDevice(iDescriptor::Uniq uniq, << initResult->success; if (!initResult->success) { - qDebug() << "Failed to initialize device with" << uniq; - emit initFailed(uniq); - // TODO:it could also be password protected, so check for - // that Initialization failed, cleaning up resources. - // PasswordProtected - // Invalidhostid - if (initResult->error && initResult->error->code == - PairingDialogResponsePending) { - if (addType == AddType::Regular) { - m_pendingDevices.append(uniq); - emit devicePasswordProtected(uniq); - emit deviceChange(); - QTimer::singleShot( - SettingsManager::sharedInstance() - ->connectionTimeout() * - 1000, - this, [this, uniq]() { - if (m_pendingDevices.contains(uniq)) { - qDebug() << "Pairing expired for " - "device UDID:" - << uniq; - m_pendingDevices.removeAll(uniq); - emit devicePairingExpired(uniq); - emit deviceChange(); - } - }); - // FIXME: free properly and move to a better place - QThreadPool::globalInstance()->start([uniq, - this]() { - UsbmuxdConnectionHandle *usbmuxd_conn = nullptr; - UsbmuxdAddrHandle *addr_handle = nullptr; - IdeviceProviderHandle *provider = nullptr; - LockdowndClientHandle *lockdown = nullptr; - IdevicePairingFile *pairing_file = nullptr; - - IdeviceFfiError *err = - idevice_usbmuxd_new_default_connection( - 0, &usbmuxd_conn); - if (err) { - // if (!isWireless) { - qDebug() << "Failed to connect to usbmuxd"; - // goto cleanup; - return; - // } - } - - err = idevice_usbmuxd_default_addr_new( - &addr_handle); - if (err) { - qDebug() - << "Failed to create address handle"; - // goto cleanup; - return; - } - - UsbmuxdDeviceHandle **devices; - int device_count; - int actual_device_id = -1; - err = idevice_usbmuxd_get_devices( - usbmuxd_conn, &devices, &device_count); - - for (size_t i = 0; i < device_count; i++) { - const char *device_udid = - idevice_usbmuxd_device_get_udid( - devices[i]); - if (strcmp( - device_udid, - uniq.get().toUtf8().constData()) == - 0) { - actual_device_id = - idevice_usbmuxd_device_get_device_id( - devices[i]); - break; - } - } - - err = usbmuxd_provider_new( - addr_handle, 0, - uniq.get().toUtf8().constData(), - actual_device_id, APP_LABEL, &provider); - - err = lockdownd_connect(provider, &lockdown); - if (err) { - qDebug() << "Failed to connect to lockdown"; - return; - } - - QString hostId = QUuid::createUuid() - .toString() - .remove("{") - .remove("}") - .toUpper(); - char *buid = nullptr; - idevice_usbmuxd_get_buid(usbmuxd_conn, &buid); - - bool ok = false; - while (true) { - // if (const auto dev = - // AppContext::sharedInstance() - // ->getDevice( - // uniq.get().toStdString())) - // { - - if (ok) { - qDebug() << "Successfully paired with " - "device, "; - break; - }; - err = lockdownd_pair( - lockdown, hostId.toStdString().c_str(), - buid, nullptr, &pairing_file); - if (err) { - qDebug() << "Failed to pair with device" - << err->message; - - std::this_thread::sleep_for( - std::chrono::seconds(5)); - // return; - // goto cleanup; - } else { - - qDebug() - << "There was no error, pairing " - "successful"; - uint8_t *data = nullptr; - size_t size = 0; - - // Serialize pairing file to bytes - IdeviceFfiError *err = - idevice_pairing_file_serialize( - pairing_file, &data, &size); - if (err) { - qDebug() << "Failed to serialize " - "pairing file:" - << err->message; - // idevice_error_free(err); - // goto cleanup; - } - - err = idevice_usbmuxd_save_pair_record( - usbmuxd_conn, - uniq.get().toUtf8().constData(), - data, size); - - QMetaObject::invokeMethod( - AppContext::sharedInstance(), - "addDevice", Qt::QueuedConnection, - Q_ARG(iDescriptor::Uniq, uniq), - Q_ARG(DeviceMonitorThread:: - IdeviceConnectionType, - DeviceMonitorThread:: - IdeviceConnectionType:: - CONNECTION_NETWORK), - Q_ARG(AddType, AddType::Regular)); - ok = true; - } - } - }); - } - } - - // else if (initResult.error == - // LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING - // || - // initResult.error == - // LOCKDOWN_E_INVALID_HOST_ID) { - // m_pendingDevices.append(udid); - // emit devicePairPending(udid); - // emit deviceChange(); - // QTimer::singleShot( - // SettingsManager::sharedInstance()->connectionTimeout() - // * - // 1000, - // this, [this, udid]() { - // qDebug() - // << "Pairing timer fired for device - // UDID: " << udid; - // if (m_pendingDevices.contains(udid)) { - // qDebug() - // << "Pairing expired for device - // UDID: " << udid; - // m_pendingDevices.removeAll(udid); - // emit devicePairingExpired(udid); - // emit deviceChange(); - // } - // }); - // } else { - // qDebug() << "Unhandled error for device UDID: " - // << udid - // << " Error code: " << initResult.error; - // } - return; + return handlePairing(initResult.get(), addType, uniq); } qDebug() << "Device initialized: " << uniq; @@ -461,19 +269,27 @@ void AppContext::addDevice(iDescriptor::Uniq uniq, .lockdown = initResult->lockdown, .diagRelay = initResult->diagRelay, .heartbeatThread = initResult->heartbeatThread}; + + /* + sometimes heartbeat thread can fail before we even add + the device, in that case we should not add the device at all + this seems to happen on Windows for some reason + */ + if (initResult->deviceInfo.isWireless && + initResult->heartbeatThread && + initResult->heartbeatThread->exited()) { + qDebug() << "Heartbeat thread already exited for device" + << uniq << " Not adding device."; + freeDevice(device); + return; + } + m_devices[device->udid] = device; + if (addType == AddType::Wireless || addType == AddType::UpgradeToWireless || addType == AddType::Regular) { qDebug() << "Wireless device added: " << uniq; - SettingsManager::sharedInstance()->doIfEnabled( - SettingsManager::Setting::AutoRaiseWindow, []() { - if (MainWindow *mainWindow = - MainWindow::sharedInstance()) { - mainWindow->raise(); - mainWindow->activateWindow(); - } - }); emit deviceAdded(device); emit deviceChange(); @@ -736,4 +552,181 @@ void AppContext::freeDevice(iDescriptorDevice *device) lockdownd_client_free(device->lockdown); idevice_provider_free(device->provider); delete device; +} + +void AppContext::handlePairing(iDescriptorInitDeviceResult *initResult, + AddType addType, iDescriptor::Uniq uniq) +{ + qDebug() << "[handlePairing] for device" << uniq; + emit initFailed(uniq); + if (initResult->error && + initResult->error->code == PairingDialogResponsePending || + initResult->error->code == InvalidHostID || + initResult->error->code == PasswordProtected) { + if (addType == AddType::Regular) { + m_pendingDevices.append(uniq); + emit devicePasswordProtected(uniq); + emit deviceChange(); + QTimer::singleShot( + SettingsManager::sharedInstance()->connectionTimeout() * 1000, + this, [this, uniq]() { + if (m_pendingDevices.contains(uniq)) { + qDebug() << "Pairing expired for " + "device UDID:" + << uniq; + m_pendingDevices.removeAll(uniq); + emit devicePairingExpired(uniq); + emit deviceChange(); + } + }); + // FIXME: free properly and move to a better place + QThreadPool::globalInstance()->start([uniq, this]() { + UsbmuxdConnectionHandle *usbmuxd_conn = nullptr; + UsbmuxdAddrHandle *addr_handle = nullptr; + IdeviceProviderHandle *provider = nullptr; + LockdowndClientHandle *lockdown = nullptr; + IdevicePairingFile *pairing_file = nullptr; + + IdeviceFfiError *err = + idevice_usbmuxd_new_default_connection(0, &usbmuxd_conn); + if (err) { + // if (!isWireless) { + qDebug() << "Failed to connect to usbmuxd"; + // goto cleanup; + return; + // } + } + + err = idevice_usbmuxd_default_addr_new(&addr_handle); + if (err) { + qDebug() << "Failed to create address handle"; + // goto cleanup; + return; + } + + UsbmuxdDeviceHandle **devices; + int device_count; + int actual_device_id = -1; + err = idevice_usbmuxd_get_devices(usbmuxd_conn, &devices, + &device_count); + + for (size_t i = 0; i < device_count; i++) { + const char *device_udid = + idevice_usbmuxd_device_get_udid(devices[i]); + if (strcmp(device_udid, uniq.get().toUtf8().constData()) == + 0) { + actual_device_id = + idevice_usbmuxd_device_get_device_id(devices[i]); + break; + } + } + + err = usbmuxd_provider_new( + addr_handle, 0, uniq.get().toUtf8().constData(), + actual_device_id, APP_LABEL, &provider); + + err = lockdownd_connect(provider, &lockdown); + if (err) { + qDebug() << "Failed to connect to lockdown"; + return; + } + + QString hostId = QUuid::createUuid() + .toString() + .remove("{") + .remove("}") + .toUpper(); + char *buid = nullptr; + idevice_usbmuxd_get_buid(usbmuxd_conn, &buid); + + bool ok = false; + while (true) { + // if (const auto dev = + // AppContext::sharedInstance() + // ->getDevice( + // uniq.get().toStdString())) + // { + + if (ok) { + qDebug() << "Successfully paired with " + "device, "; + break; + }; + err = lockdownd_pair(lockdown, hostId.toStdString().c_str(), + buid, nullptr, &pairing_file); + if (err) { + qDebug() + << "Failed to pair with device" << err->message; + + std::this_thread::sleep_for(std::chrono::seconds(5)); + // return; + // goto cleanup; + } else { + + qDebug() << "There was no error, pairing " + "successful"; + uint8_t *data = nullptr; + size_t size = 0; + + // Serialize pairing file to bytes + IdeviceFfiError *err = idevice_pairing_file_serialize( + pairing_file, &data, &size); + if (err) { + qDebug() << "Failed to serialize " + "pairing file:" + << err->message; + // idevice_error_free(err); + // goto cleanup; + } + + err = idevice_usbmuxd_save_pair_record( + usbmuxd_conn, uniq.get().toUtf8().constData(), data, + size); + + QMetaObject::invokeMethod( + AppContext::sharedInstance(), "addDevice", + Qt::QueuedConnection, + Q_ARG(iDescriptor::Uniq, uniq), + Q_ARG(DeviceMonitorThread::IdeviceConnectionType, + DeviceMonitorThread::IdeviceConnectionType:: + CONNECTION_NETWORK), + Q_ARG(AddType, AddType::Regular)); + ok = true; + } + } + }); + } + } + + // else if (initResult.error == + // LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING + // || + // initResult.error == + // LOCKDOWN_E_INVALID_HOST_ID) { + // m_pendingDevices.append(udid); + // emit devicePairPending(udid); + // emit deviceChange(); + // QTimer::singleShot( + // SettingsManager::sharedInstance()->connectionTimeout() + // * + // 1000, + // this, [this, udid]() { + // qDebug() + // << "Pairing timer fired for device + // UDID: " << udid; + // if (m_pendingDevices.contains(udid)) { + // qDebug() + // << "Pairing expired for device + // UDID: " << udid; + // m_pendingDevices.removeAll(udid); + // emit devicePairingExpired(udid); + // emit deviceChange(); + // } + // }); + // } else { + // qDebug() << "Unhandled error for device UDID: " + // << udid + // << " Error code: " << initResult.error; + // } + return; } \ No newline at end of file diff --git a/src/appcontext.h b/src/appcontext.h index 88dda01..8567268 100644 --- a/src/appcontext.h +++ b/src/appcontext.h @@ -22,6 +22,7 @@ #include "devicemonitor.h" #include "devicesidebarwidget.h" +#include "heartbeat.h" #include "iDescriptor.h" #include @@ -60,6 +61,8 @@ private: void cachePairedDevices(); void emitNoPairingFileForWirelessDevice(const QString &udid); void freeDevice(iDescriptorDevice *device); + void handlePairing(iDescriptorInitDeviceResult *initResult, AddType addType, + iDescriptor::Uniq uniq); signals: void deviceAdded(const iDescriptorDevice *device); void deviceRemoved(const std::string &udid, const std::string &macAddress, diff --git a/src/core/services/init_device.cpp b/src/core/services/init_device.cpp index 480be0b..5f7fe61 100644 --- a/src/core/services/init_device.cpp +++ b/src/core/services/init_device.cpp @@ -153,10 +153,9 @@ void parseDeviceBattery(PlistNavigator &ioreg, DeviceInfo &d) d.batteryInfo.watts = ioreg["AppleRawAdapterDetails"][0]["Watts"].getUInt(); } -DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, - AfcClientHandle *afcClient, - DiagnosticsRelay *diagRelay, - iDescriptorInitDeviceResult &result) +void fullDeviceInfo(const pugi::xml_document &doc, AfcClientHandle *afcClient, + DiagnosticsRelay *diagRelay, + iDescriptorInitDeviceResult &result) { pugi::xml_node dict = doc.child("plist").child("dict"); auto safeGet = [&](const char *key) -> std::string { @@ -248,7 +247,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, IdeviceFfiError *err = afc_get_device_info(afcClient, info); if (err) { qDebug() << "AFC get device info error code: " << err->message; - return d; + return; } if (info) { @@ -309,7 +308,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, if (!diagnostics) { qDebug() << "Failed to get diagnostics plist."; - return d; + return; } try { PlistNavigator ioreg = PlistNavigator(diagnostics); @@ -320,7 +319,7 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, parseOldDevice(ioreg, d); plist_free(diagnostics); diagnostics = nullptr; - return d; + return; } bool newerThaniPhone8 = @@ -358,10 +357,10 @@ DeviceInfo fullDeviceInfo(const pugi::xml_document &doc, plist_free(diagnostics); diagnostics = nullptr; - return d; + return; } catch (const std::exception &e) { qDebug() << "Error occurred: " << e.what(); - return d; + return; } } @@ -567,6 +566,12 @@ cleanup: if (!result.success) { qDebug() << "Initialization failed, cleaning up resources." << err->message; + if (heartbeatThread) { + heartbeatThread->requestInterruption(); + heartbeatThread->wait(); + delete heartbeatThread; + heartbeatThread = nullptr; + } if (afc2_client) afc_client_free(afc2_client); if (afc_client) diff --git a/src/heartbeat.h b/src/heartbeat.h index 522034c..a74e1b7 100644 --- a/src/heartbeat.h +++ b/src/heartbeat.h @@ -3,6 +3,7 @@ #include "iDescriptor.h" #include #include +#include using namespace IdeviceFFI; @@ -62,6 +63,7 @@ public: "exiting for " "device" << m_macAddress; + m_exited = true; emit heartbeatThreadExited(m_macAddress); break; } @@ -84,12 +86,14 @@ public: } bool initialCompleted() const { return m_initialCompleted; } + bool exited() const { return m_exited.load(); } private: Heartbeat m_hb; bool m_initialCompleted = false; iDescriptor::Uniq m_macAddress; unsigned int m_tries = 0; + std::atomic m_exited{false}; signals: void heartbeatFailed(const QString &macAddress, unsigned int tries = 0); diff --git a/src/iDescriptor.h b/src/iDescriptor.h index 0b2f796..5700261 100644 --- a/src/iDescriptor.h +++ b/src/iDescriptor.h @@ -77,6 +77,8 @@ #define NotFoundErrorCode -14 #define ServiceNotFoundErrorCode -15 #define PairingDialogResponsePending -28 +#define InvalidHostID -10 +#define PasswordProtected -30 #define InvalidServiceErrorCode -59 #define TimeoutErrorCode -71 @@ -225,6 +227,8 @@ struct DeviceInfo { std::string UniqueDeviceID; }; +class HeartbeatThread; + struct iDescriptorDevice { std::string udid; DeviceMonitorThread::IdeviceConnectionType conn_type; @@ -237,7 +241,7 @@ struct iDescriptorDevice { mutable std::recursive_mutex mutex; std::shared_ptr diagRelay; // nullptr on USB devices - QThread *heartbeatThread; + HeartbeatThread *heartbeatThread; }; struct iDescriptorInitDeviceResult { @@ -249,7 +253,7 @@ struct iDescriptorInitDeviceResult { AfcClientHandle *afc2Client; LockdowndClientHandle *lockdown; std::shared_ptr diagRelay; - QThread *heartbeatThread; + HeartbeatThread *heartbeatThread; }; #ifdef ENABLE_RECOVERY_DEVICE_SUPPORT struct iDescriptorRecoveryDevice {