cleanup pairing process and improve heartbeat management

This commit is contained in:
uncor3
2026-03-20 20:26:36 +03:00
parent 1023777642
commit 6cd2c5e5ba
5 changed files with 221 additions and 212 deletions
+194 -201
View File
@@ -21,7 +21,6 @@
#include "devicemonitor.h"
#include "iDescriptor.h"
#include "mainwindow.h"
#include "networkdevicemanager.h"
#include "settingsmanager.h"
#include <QDebug>
#include <QMessageBox>
@@ -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;
}
+3
View File
@@ -22,6 +22,7 @@
#include "devicemonitor.h"
#include "devicesidebarwidget.h"
#include "heartbeat.h"
#include "iDescriptor.h"
#include <QObject>
@@ -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,
+14 -9
View File
@@ -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)
+4
View File
@@ -3,6 +3,7 @@
#include "iDescriptor.h"
#include <QDebug>
#include <QThread>
#include <atomic>
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<bool> m_exited{false};
signals:
void heartbeatFailed(const QString &macAddress, unsigned int tries = 0);
+6 -2
View File
@@ -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<DiagnosticsRelay> diagRelay;
// nullptr on USB devices
QThread *heartbeatThread;
HeartbeatThread *heartbeatThread;
};
struct iDescriptorInitDeviceResult {
@@ -249,7 +253,7 @@ struct iDescriptorInitDeviceResult {
AfcClientHandle *afc2Client;
LockdowndClientHandle *lockdown;
std::shared_ptr<DiagnosticsRelay> diagRelay;
QThread *heartbeatThread;
HeartbeatThread *heartbeatThread;
};
#ifdef ENABLE_RECOVERY_DEVICE_SUPPORT
struct iDescriptorRecoveryDevice {