diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp index f76f41db976..838e04de954 100644 --- a/src/plugins/ios/iosdevice.cpp +++ b/src/plugins/ios/iosdevice.cpp @@ -16,8 +16,13 @@ #include #include +#include + +#include #include +#include +#include #include #include @@ -72,7 +77,14 @@ static QString CFStringRef2QString(CFStringRef s) namespace Ios::Internal { const char kDeviceName[] = "deviceName"; +const char kDeveloperStatus[] = "developerStatus"; +const char kDeviceConnected[] = "deviceConnected"; +const char kOsVersion[] = "osVersion"; +const char kCpuArchitecture[] = "cpuArchitecture"; const char kUniqueDeviceId[] = "uniqueDeviceId"; +const char vOff[] = "*off*"; +const char vDevelopment[] = "Development"; +const char vYes[] = "YES"; class IosDeviceInfoWidget : public IDeviceWidget { @@ -168,12 +180,12 @@ QString IosDevice::name() QString IosDevice::osVersion() const { - return m_extraInfo.value(QLatin1String("osVersion")); + return m_extraInfo.value(kOsVersion); } QString IosDevice::cpuArchitecture() const { - return m_extraInfo.value("cpuArchitecture"); + return m_extraInfo.value(kCpuArchitecture); } Utils::Port IosDevice::nextPort() const @@ -194,13 +206,12 @@ IosDeviceManager::TranslationMap IosDeviceManager::translationMap() TranslationMap &tMap = *new TranslationMap; tMap[kDeviceName] = Tr::tr("Device name"); //: Whether the device is in developer mode. - tMap[QLatin1String("developerStatus")] = Tr::tr("Developer status"); - tMap[QLatin1String("deviceConnected")] = Tr::tr("Connected"); - tMap[QLatin1String("YES")] = Tr::tr("yes"); + tMap[kDeveloperStatus] = Tr::tr("Developer status"); + tMap[kDeviceConnected] = Tr::tr("Connected"); + tMap[vYes] = Tr::tr("yes"); tMap[QLatin1String("NO")] = Tr::tr("no"); - tMap[QLatin1String("YES")] = Tr::tr("yes"); tMap[QLatin1String("*unknown*")] = Tr::tr("unknown"); - tMap[QLatin1String("osVersion")] = Tr::tr("OS version"); + tMap[kOsVersion] = Tr::tr("OS version"); translationMap = &tMap; return tMap; } @@ -253,12 +264,61 @@ void IosDeviceManager::deviceDisconnected(const QString &uid) void IosDeviceManager::updateInfo(const QString &devId) { - IosToolHandler *requester = new IosToolHandler(IosDeviceType(IosDeviceType::IosDevice), this); - connect(requester, &IosToolHandler::deviceInfo, - this, &IosDeviceManager::deviceInfo, Qt::QueuedConnection); - connect(requester, &IosToolHandler::finished, - this, &IosDeviceManager::infoGathererFinished); - requester->requestDeviceInfo(devId); + using namespace Tasking; + + const auto infoFromDeviceCtl = ProcessTask( + [](Process &process) { + process.setCommand({FilePath::fromString("/usr/bin/xcrun"), + {"devicectl", "list", "devices", "--quiet", "--json-output", "-"}}); + }, + [this, devId](const Process &process, DoneWith) -> DoneResult { + auto jsonOutput = QJsonDocument::fromJson(process.rawStdOut()); + // find device + const QJsonArray deviceList = jsonOutput["result"]["devices"].toArray(); + for (const QJsonValue &device : deviceList) { + const QString udid = device["hardwareProperties"]["udid"].toString(); + // USB identifiers don't have dashes, but iOS device udids can. Remove. + if (QString(udid).remove('-') == devId) { + // fill in the map that we use for the iostool data + QMap info; + info[kDeviceName] = device["deviceProperties"]["name"].toString(); + info[kDeveloperStatus] = QLatin1String( + device["deviceProperties"]["developerModeStatus"] == "enabled" + ? vDevelopment + : vOff); + info[kDeviceConnected] = vYes; // that's the assumption + info[kOsVersion] + = QLatin1String("%1 (%2)") + .arg(device["deviceProperties"]["osVersionNumber"].toString(), + device["deviceProperties"]["osBuildUpdate"].toString()); + info[kCpuArchitecture] + = device["hardwareProperties"]["cpuType"]["name"].toString(); + info[kUniqueDeviceId] = udid; + deviceInfo(nullptr, devId, info); + return DoneResult::Success; + } + } + // device not found, not handled by devicectl + return DoneResult::Error; + }, + CallDoneIf::Success); + + const auto infoFromIosTool = IosToolTask([this, devId](IosToolRunner &runner) { + runner.setDeviceType(IosDeviceType::IosDevice); + runner.setStartHandler([this, devId](IosToolHandler *handler) { + connect(handler, + &IosToolHandler::deviceInfo, + this, + &IosDeviceManager::deviceInfo, + Qt::QueuedConnection); + handler->requestDeviceInfo(devId); + }); + }); + + const Group root{sequential, stopOnSuccess, infoFromDeviceCtl, infoFromIosTool}; + auto task = new TaskTree(root); + connect(task, &TaskTree::done, task, [task] { task->deleteLater(); }); + task->start(); } void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid, @@ -294,14 +354,14 @@ void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid, QLatin1String devStatusKey = QLatin1String("developerStatus"); if (info.contains(devStatusKey)) { QString devStatus = info.value(devStatusKey); - if (devStatus == QLatin1String("Development")) { + if (devStatus == vDevelopment) { devManager->setDeviceState(newDev->id(), IDevice::DeviceReadyToUse); m_userModeDeviceIds.removeOne(uid); } else { devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected); bool shouldIgnore = newDev->m_ignoreDevice; newDev->m_ignoreDevice = true; - if (devStatus == QLatin1String("*off*")) { + if (devStatus == vOff) { if (!shouldIgnore && !IosConfigurations::ignoreAllDevices()) { QMessageBox mBox; mBox.setText(Tr::tr("An iOS device in user mode has been detected.")); @@ -331,11 +391,6 @@ void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid, } } -void IosDeviceManager::infoGathererFinished(IosToolHandler *gatherer) -{ - gatherer->deleteLater(); -} - #ifdef Q_OS_MAC namespace { io_iterator_t gAddedIter; diff --git a/src/plugins/ios/iosdevice.h b/src/plugins/ios/iosdevice.h index 24bee023de7..bdbd1d43a2c 100644 --- a/src/plugins/ios/iosdevice.h +++ b/src/plugins/ios/iosdevice.h @@ -75,7 +75,6 @@ public: void updateInfo(const QString &devId); void deviceInfo(Ios::IosToolHandler *gatherer, const QString &deviceId, const Ios::IosToolHandler::Dict &info); - void infoGathererFinished(Ios::IosToolHandler *gatherer); void monitorAvailableDevices(); private: diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index 88a15290f20..c182d780ea2 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -973,6 +973,28 @@ bool IosToolHandler::isRunning() const return d->isRunning(); } +void IosToolRunner::setStartHandler(const StartHandler &startHandler) +{ + m_startHandler = startHandler; +} + +void IosToolRunner::setDeviceType(const Internal::IosDeviceType &type) +{ + m_deviceType = type; +} + +IosToolTaskAdapter::IosToolTaskAdapter() {} + +void IosToolTaskAdapter::start() +{ + task()->m_iosToolHandler = new IosToolHandler(Internal::IosDeviceType(task()->m_deviceType)); + connect(task()->m_iosToolHandler, &IosToolHandler::finished, this, [this] { + task()->m_iosToolHandler->deleteLater(); + emit done(Tasking::DoneResult::Success); + }); + task()->m_startHandler(task()->m_iosToolHandler); +} + } // namespace Ios #include "iostoolhandler.moc" diff --git a/src/plugins/ios/iostoolhandler.h b/src/plugins/ios/iostoolhandler.h index a85e3250b40..33d413e3cfc 100644 --- a/src/plugins/ios/iostoolhandler.h +++ b/src/plugins/ios/iostoolhandler.h @@ -3,9 +3,13 @@ #pragma once +#include "iossimulator.h" + #include #include +#include + #include #include #include @@ -67,4 +71,29 @@ private: Ios::Internal::IosToolHandlerPrivate *d; }; +// for Tasking: + +class IosToolRunner +{ +public: + using StartHandler = std::function; + void setStartHandler(const StartHandler &startHandler); + void setDeviceType(const Internal::IosDeviceType &type); + +private: + friend class IosToolTaskAdapter; + IosToolHandler *m_iosToolHandler = nullptr; + StartHandler m_startHandler; + Internal::IosDeviceType m_deviceType = Internal::IosDeviceType::IosDevice; +}; + +class IosToolTaskAdapter : public Tasking::TaskAdapter +{ +public: + IosToolTaskAdapter(); + void start() final; +}; + +using IosToolTask = Tasking::CustomTask; + } // namespace Ios diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index 891e2300ec8..d492e6f78dc 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include @@ -105,20 +104,6 @@ static bool findXcodePath(QString *xcodePath) return (process.exitStatus() == QProcess::NormalExit && QFile::exists(*xcodePath)); } -static bool checkDevelopmentStatusViaDeviceCtl(const QString &deviceId) -{ - QProcess process; - process.start("/usr/bin/xcrun", QStringList({"devicectl", - "device", "info", "details", "--quiet", "--device", deviceId, "-j", "-"})); - if (!process.waitForFinished(3000)) { - qCWarning(loggingCategory) << "Failed to launch devicectl:" << process.errorString(); - return false; - } - - auto jsonOutput = QJsonDocument::fromJson(process.readAllStandardOutput()); - return jsonOutput["result"]["deviceProperties"]["developerModeStatus"] == "enabled"; -} - /*! * \brief Finds the \e DeveloperDiskImage.dmg path corresponding to \a versionStr and \a buildStr. * @@ -1685,17 +1670,10 @@ void DevInfoSession::deviceCallbackReturned() res[deviceNameKey] = getStringValue(device, nullptr, CFSTR("DeviceName")); res[uniqueDeviceId] = getStringValue(device, nullptr, CFSTR("UniqueDeviceID")); const QString productVersion = getStringValue(device, nullptr, CFSTR("ProductVersion")); - - if (productVersion.startsWith("17.")) { - res[developerStatusKey] = checkDevelopmentStatusViaDeviceCtl(res[uniqueDeviceId]) - ? QLatin1String("Development") : QLatin1String("*off*"); - } else { - res[developerStatusKey] = getStringValue(device, + res[developerStatusKey] = getStringValue(device, CFSTR("com.apple.xcode.developerdomain"), CFSTR("DeveloperStatus"), "*off*"); - } - res[cpuArchitectureKey] = getStringValue(device, nullptr, CFSTR("CPUArchitecture")); const QString buildVersion = getStringValue(device, nullptr, CFSTR("BuildVersion")); if (!productVersion.isEmpty() && !buildVersion.isEmpty())