2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
2013-04-25 16:02:17 +02:00
|
|
|
|
|
|
|
|
#include "iosdevice.h"
|
2022-12-20 13:39:23 +01:00
|
|
|
|
2024-01-29 15:55:18 +01:00
|
|
|
#include "devicectlutils.h"
|
2013-04-25 16:02:17 +02:00
|
|
|
#include "iosconfigurations.h"
|
2020-07-10 14:40:38 +02:00
|
|
|
#include "iosconstants.h"
|
|
|
|
|
#include "iossimulator.h"
|
2013-04-25 16:02:17 +02:00
|
|
|
#include "iostoolhandler.h"
|
2022-12-20 13:39:23 +01:00
|
|
|
#include "iostr.h"
|
|
|
|
|
|
2020-07-10 14:40:38 +02:00
|
|
|
#include <coreplugin/helpmanager.h>
|
2022-12-20 13:39:23 +01:00
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
2020-07-10 14:40:38 +02:00
|
|
|
#include <projectexplorer/devicesupport/idevicewidget.h>
|
2023-08-11 09:18:56 +02:00
|
|
|
#include <projectexplorer/kitaspects.h>
|
2022-12-20 13:39:23 +01:00
|
|
|
|
2024-01-11 14:43:54 +01:00
|
|
|
#include <utils/layoutbuilder.h>
|
2014-02-25 16:02:02 +01:00
|
|
|
#include <utils/portlist.h>
|
2024-01-10 10:52:04 +01:00
|
|
|
#include <utils/process.h>
|
|
|
|
|
|
|
|
|
|
#include <solutions/tasking/tasktree.h>
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2024-01-10 10:52:04 +01:00
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonDocument>
|
2020-07-10 14:40:38 +02:00
|
|
|
#include <QMessageBox>
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2013-10-11 13:55:45 +02:00
|
|
|
#ifdef Q_OS_MAC
|
2013-04-25 16:02:17 +02:00
|
|
|
#include <IOKit/IOKitLib.h>
|
|
|
|
|
#include <IOKit/usb/IOUSBLib.h>
|
|
|
|
|
#include <CoreFoundation/CoreFoundation.h>
|
2020-01-16 10:32:05 +01:00
|
|
|
|
|
|
|
|
// Work around issue with not being able to retrieve USB serial number.
|
|
|
|
|
// See QTCREATORBUG-23460.
|
|
|
|
|
// For an unclear reason USBSpec.h in macOS SDK 10.15 uses a different value if
|
|
|
|
|
// MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_14, which just does not work.
|
|
|
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_14
|
|
|
|
|
#undef kUSBSerialNumberString
|
|
|
|
|
#define kUSBSerialNumberString "USB Serial Number"
|
|
|
|
|
#endif
|
|
|
|
|
|
2013-10-11 13:55:45 +02:00
|
|
|
#endif
|
|
|
|
|
|
2013-11-28 14:45:56 +01:00
|
|
|
#include <exception>
|
|
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
using namespace ProjectExplorer;
|
2023-08-23 16:11:48 +02:00
|
|
|
using namespace Utils;
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2014-07-07 09:24:17 +02:00
|
|
|
namespace {
|
2020-01-15 14:39:23 +01:00
|
|
|
static Q_LOGGING_CATEGORY(detectLog, "qtc.ios.deviceDetect", QtWarningMsg)
|
2014-07-07 09:24:17 +02:00
|
|
|
}
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2013-10-11 13:55:45 +02:00
|
|
|
#ifdef Q_OS_MAC
|
|
|
|
|
static QString CFStringRef2QString(CFStringRef s)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
|
|
|
|
unsigned char buf[250];
|
|
|
|
|
CFIndex len = CFStringGetLength(s);
|
|
|
|
|
CFIndex usedBufLen;
|
|
|
|
|
CFIndex converted = CFStringGetBytes(s, CFRangeMake(0,len), kCFStringEncodingUTF8,
|
|
|
|
|
'?', false, &buf[0], sizeof(buf), &usedBufLen);
|
|
|
|
|
if (converted == len)
|
|
|
|
|
return QString::fromUtf8(reinterpret_cast<char *>(&buf[0]), usedBufLen);
|
|
|
|
|
size_t bufSize = sizeof(buf)
|
|
|
|
|
+ CFStringGetMaximumSizeForEncoding(len - converted, kCFStringEncodingUTF8);
|
|
|
|
|
unsigned char *bigBuf = new unsigned char[bufSize];
|
|
|
|
|
memcpy(bigBuf, buf, usedBufLen);
|
|
|
|
|
CFIndex newUseBufLen;
|
|
|
|
|
CFStringGetBytes(s, CFRangeMake(converted,len), kCFStringEncodingUTF8,
|
|
|
|
|
'?', false, &bigBuf[usedBufLen], bufSize, &newUseBufLen);
|
|
|
|
|
QString res = QString::fromUtf8(reinterpret_cast<char *>(bigBuf), usedBufLen + newUseBufLen);
|
|
|
|
|
delete[] bigBuf;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
2013-10-11 13:55:45 +02:00
|
|
|
#endif
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2022-12-20 13:39:23 +01:00
|
|
|
namespace Ios::Internal {
|
|
|
|
|
|
2024-01-12 11:40:20 +01:00
|
|
|
const char kHandler[] = "Handler";
|
|
|
|
|
|
2020-07-10 14:40:38 +02:00
|
|
|
class IosDeviceInfoWidget : public IDeviceWidget
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
IosDeviceInfoWidget(const ProjectExplorer::IDevice::Ptr &device);
|
|
|
|
|
|
2020-11-18 15:26:38 +01:00
|
|
|
void updateDeviceFromUi() final {}
|
2020-07-10 14:40:38 +02:00
|
|
|
};
|
|
|
|
|
|
2019-08-16 10:26:42 +02:00
|
|
|
IosDevice::IosDevice(CtorHelper)
|
2019-01-14 16:57:51 +01:00
|
|
|
: m_lastPort(Constants::IOS_DEVICE_PORT_START)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
2019-01-14 16:11:27 +01:00
|
|
|
setType(Constants::IOS_DEVICE_TYPE);
|
2023-09-07 14:04:19 +02:00
|
|
|
settings()->displayName.setDefaultValue(IosDevice::name());
|
2022-12-20 13:39:23 +01:00
|
|
|
setDisplayType(Tr::tr("iOS"));
|
2019-01-14 15:27:52 +01:00
|
|
|
setMachineType(IDevice::Hardware);
|
2019-08-16 10:17:45 +02:00
|
|
|
setOsType(Utils::OsTypeMac);
|
2013-11-28 16:02:22 +01:00
|
|
|
setDeviceState(DeviceDisconnected);
|
2019-08-16 10:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IosDevice::IosDevice()
|
|
|
|
|
: IosDevice(CtorHelper{})
|
|
|
|
|
{
|
|
|
|
|
setupId(IDevice::AutoDetected, Constants::IOS_DEVICE_ID);
|
|
|
|
|
|
2014-02-25 16:02:02 +01:00
|
|
|
Utils::PortList ports;
|
2016-04-19 16:43:30 +02:00
|
|
|
ports.addRange(Utils::Port(Constants::IOS_DEVICE_PORT_START),
|
|
|
|
|
Utils::Port(Constants::IOS_DEVICE_PORT_END));
|
2014-02-25 16:02:02 +01:00
|
|
|
setFreePorts(ports);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IosDevice::IosDevice(const QString &uid)
|
2019-08-16 10:26:42 +02:00
|
|
|
: IosDevice(CtorHelper{})
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
2020-06-26 13:59:38 +02:00
|
|
|
setupId(IDevice::AutoDetected, Utils::Id(Constants::IOS_DEVICE_ID).withSuffix(uid));
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDevice::DeviceInfo IosDevice::deviceInformation() const
|
|
|
|
|
{
|
|
|
|
|
IDevice::DeviceInfo res;
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto i = m_extraInfo.cbegin(), end = m_extraInfo.cend(); i != end; ++i) {
|
2013-04-25 16:02:17 +02:00
|
|
|
IosDeviceManager::TranslationMap tMap = IosDeviceManager::translationMap();
|
|
|
|
|
if (tMap.contains(i.key()))
|
|
|
|
|
res.append(DeviceInfoItem(tMap.value(i.key()), tMap.value(i.value(), i.value())));
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDeviceWidget *IosDevice::createWidget()
|
|
|
|
|
{
|
2020-07-10 14:40:38 +02:00
|
|
|
return new IosDeviceInfoWidget(sharedFromThis());
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-23 16:53:06 +02:00
|
|
|
void IosDevice::fromMap(const Store &map)
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
|
|
|
|
IDevice::fromMap(map);
|
2019-07-24 13:43:54 +02:00
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
m_extraInfo.clear();
|
2023-08-28 10:55:31 +02:00
|
|
|
const Store vMap = storeFromVariant(map.value(Constants::EXTRA_INFO_KEY));
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto i = vMap.cbegin(), end = vMap.cend(); i != end; ++i)
|
2023-08-25 13:19:26 +02:00
|
|
|
m_extraInfo.insert(stringFromKey(i.key()), i.value().toString());
|
2024-01-12 11:40:20 +01:00
|
|
|
m_handler = Handler(map.value(kHandler).toInt());
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
2023-08-25 13:19:26 +02:00
|
|
|
Store IosDevice::toMap() const
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
2023-08-25 13:19:26 +02:00
|
|
|
Store res = IDevice::toMap();
|
|
|
|
|
Store vMap;
|
2019-07-24 13:43:54 +02:00
|
|
|
for (auto i = m_extraInfo.cbegin(), end = m_extraInfo.cend(); i != end; ++i)
|
2023-08-25 13:19:26 +02:00
|
|
|
vMap.insert(keyFromString(i.key()), i.value());
|
2023-08-28 10:55:31 +02:00
|
|
|
res.insert(Constants::EXTRA_INFO_KEY, variantFromStore(vMap));
|
2024-01-12 11:40:20 +01:00
|
|
|
res.insert(kHandler, int(m_handler));
|
2013-04-25 16:02:17 +02:00
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 14:40:38 +02:00
|
|
|
QString IosDevice::deviceName() const
|
|
|
|
|
{
|
|
|
|
|
return m_extraInfo.value(kDeviceName);
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
QString IosDevice::uniqueDeviceID() const
|
|
|
|
|
{
|
2023-08-25 13:19:26 +02:00
|
|
|
return id().suffixAfter(Id(Constants::IOS_DEVICE_ID));
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
2013-11-04 15:08:41 +01:00
|
|
|
|
2020-07-10 14:40:38 +02:00
|
|
|
QString IosDevice::uniqueInternalDeviceId() const
|
|
|
|
|
{
|
|
|
|
|
return m_extraInfo.value(kUniqueDeviceId);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-04 15:08:41 +01:00
|
|
|
QString IosDevice::name()
|
|
|
|
|
{
|
2022-12-20 13:39:23 +01:00
|
|
|
return Tr::tr("iOS Device");
|
2013-11-04 15:08:41 +01:00
|
|
|
}
|
|
|
|
|
|
2013-12-10 12:53:20 +01:00
|
|
|
QString IosDevice::osVersion() const
|
2013-04-25 16:02:17 +02:00
|
|
|
{
|
2024-01-10 10:52:04 +01:00
|
|
|
return m_extraInfo.value(kOsVersion);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
2020-07-08 16:14:40 +02:00
|
|
|
QString IosDevice::cpuArchitecture() const
|
|
|
|
|
{
|
2024-01-10 10:52:04 +01:00
|
|
|
return m_extraInfo.value(kCpuArchitecture);
|
2020-07-08 16:14:40 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-19 16:43:30 +02:00
|
|
|
Utils::Port IosDevice::nextPort() const
|
2014-02-25 16:02:02 +01:00
|
|
|
{
|
|
|
|
|
// use qrand instead?
|
|
|
|
|
if (++m_lastPort >= Constants::IOS_DEVICE_PORT_END)
|
|
|
|
|
m_lastPort = Constants::IOS_DEVICE_PORT_START;
|
2016-04-19 16:43:30 +02:00
|
|
|
return Utils::Port(m_lastPort);
|
2014-02-25 16:02:02 +01:00
|
|
|
}
|
|
|
|
|
|
2024-01-12 11:40:20 +01:00
|
|
|
IosDevice::Handler IosDevice::handler() const
|
|
|
|
|
{
|
|
|
|
|
return m_handler;
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-25 16:02:17 +02:00
|
|
|
// IosDeviceManager
|
|
|
|
|
|
|
|
|
|
IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
|
|
|
|
|
{
|
2018-11-12 19:55:59 +01:00
|
|
|
static TranslationMap *translationMap = nullptr;
|
2013-04-25 16:02:17 +02:00
|
|
|
if (translationMap)
|
|
|
|
|
return *translationMap;
|
|
|
|
|
TranslationMap &tMap = *new TranslationMap;
|
2022-12-20 13:39:23 +01:00
|
|
|
tMap[kDeviceName] = Tr::tr("Device name");
|
2013-11-04 15:10:15 +01:00
|
|
|
//: Whether the device is in developer mode.
|
2024-01-10 10:52:04 +01:00
|
|
|
tMap[kDeveloperStatus] = Tr::tr("Developer status");
|
|
|
|
|
tMap[kDeviceConnected] = Tr::tr("Connected");
|
|
|
|
|
tMap[vYes] = Tr::tr("yes");
|
2022-12-20 13:39:23 +01:00
|
|
|
tMap[QLatin1String("NO")] = Tr::tr("no");
|
|
|
|
|
tMap[QLatin1String("*unknown*")] = Tr::tr("unknown");
|
2024-01-10 10:52:04 +01:00
|
|
|
tMap[kOsVersion] = Tr::tr("OS version");
|
2013-04-25 16:02:17 +02:00
|
|
|
translationMap = &tMap;
|
|
|
|
|
return tMap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IosDeviceManager::deviceConnected(const QString &uid, const QString &name)
|
|
|
|
|
{
|
|
|
|
|
DeviceManager *devManager = DeviceManager::instance();
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id baseDevId(Constants::IOS_DEVICE_ID);
|
|
|
|
|
Utils::Id devType(Constants::IOS_DEVICE_TYPE);
|
|
|
|
|
Utils::Id devId = baseDevId.withSuffix(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
IDevice::ConstPtr dev = devManager->find(devId);
|
|
|
|
|
if (dev.isNull()) {
|
2018-11-12 19:55:59 +01:00
|
|
|
auto newDev = new IosDevice(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
if (!name.isNull())
|
2023-09-07 14:04:19 +02:00
|
|
|
newDev->settings()->displayName.setValue(name);
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "adding ios device " << uid;
|
2013-04-25 16:02:17 +02:00
|
|
|
devManager->addDevice(IDevice::ConstPtr(newDev));
|
|
|
|
|
} else if (dev->deviceState() != IDevice::DeviceConnected &&
|
|
|
|
|
dev->deviceState() != IDevice::DeviceReadyToUse) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "updating ios device " << uid;
|
2019-05-27 10:07:10 +02:00
|
|
|
if (dev->type() == devType) // FIXME: Should that be a QTC_ASSERT?
|
|
|
|
|
devManager->addDevice(dev->clone());
|
|
|
|
|
else
|
|
|
|
|
devManager->addDevice(IDevice::ConstPtr(new IosDevice(uid)));
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
updateInfo(uid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IosDeviceManager::deviceDisconnected(const QString &uid)
|
|
|
|
|
{
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "detected disconnection of ios device " << uid;
|
2024-01-15 11:47:44 +01:00
|
|
|
// if an update is currently still running for the device being connected, cancel that
|
|
|
|
|
// erasing deletes the unique_ptr which deletes the TaskTree which stops it
|
|
|
|
|
m_updateTasks.erase(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
DeviceManager *devManager = DeviceManager::instance();
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id baseDevId(Constants::IOS_DEVICE_ID);
|
|
|
|
|
Utils::Id devType(Constants::IOS_DEVICE_TYPE);
|
|
|
|
|
Utils::Id devId = baseDevId.withSuffix(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
IDevice::ConstPtr dev = devManager->find(devId);
|
|
|
|
|
if (dev.isNull() || dev->type() != devType) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(detectLog) << "ignoring disconnection of ios device " << uid; // should neve happen
|
2013-04-25 16:02:17 +02:00
|
|
|
} else {
|
2018-11-12 19:55:59 +01:00
|
|
|
auto iosDev = static_cast<const IosDevice *>(dev.data());
|
2014-02-25 22:20:16 +01:00
|
|
|
if (iosDev->m_extraInfo.isEmpty()
|
2020-07-10 14:40:38 +02:00
|
|
|
|| iosDev->m_extraInfo.value(kDeviceName) == QLatin1String("*unknown*")) {
|
2014-02-25 22:20:16 +01:00
|
|
|
devManager->removeDevice(iosDev->id());
|
|
|
|
|
} else if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "disconnecting device " << iosDev->uniqueDeviceID();
|
2013-04-25 16:02:17 +02:00
|
|
|
devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IosDeviceManager::updateInfo(const QString &devId)
|
|
|
|
|
{
|
2024-01-10 10:52:04 +01:00
|
|
|
using namespace Tasking;
|
|
|
|
|
|
|
|
|
|
const auto infoFromDeviceCtl = ProcessTask(
|
|
|
|
|
[](Process &process) {
|
|
|
|
|
process.setCommand({FilePath::fromString("/usr/bin/xcrun"),
|
|
|
|
|
{"devicectl", "list", "devices", "--quiet", "--json-output", "-"}});
|
|
|
|
|
},
|
2024-01-15 11:47:44 +01:00
|
|
|
[this, devId](const Process &process) {
|
2024-01-29 15:55:18 +01:00
|
|
|
const expected_str<QMap<QString, QString>> result = parseDeviceInfo(process.rawStdOut(),
|
|
|
|
|
devId);
|
|
|
|
|
if (!result) {
|
|
|
|
|
qCDebug(detectLog) << result.error();
|
|
|
|
|
return DoneResult::Error;
|
2024-01-10 10:52:04 +01:00
|
|
|
}
|
2024-01-29 15:55:18 +01:00
|
|
|
deviceInfo(devId, IosDevice::Handler::DeviceCtl, *result);
|
|
|
|
|
return DoneResult::Success;
|
2024-01-10 10:52:04 +01:00
|
|
|
},
|
|
|
|
|
CallDoneIf::Success);
|
|
|
|
|
|
|
|
|
|
const auto infoFromIosTool = IosToolTask([this, devId](IosToolRunner &runner) {
|
|
|
|
|
runner.setDeviceType(IosDeviceType::IosDevice);
|
|
|
|
|
runner.setStartHandler([this, devId](IosToolHandler *handler) {
|
2024-01-12 11:40:20 +01:00
|
|
|
connect(
|
|
|
|
|
handler,
|
|
|
|
|
&IosToolHandler::deviceInfo,
|
|
|
|
|
this,
|
|
|
|
|
[this](IosToolHandler *, const QString &uid, const Ios::IosToolHandler::Dict &info) {
|
|
|
|
|
deviceInfo(uid, IosDevice::Handler::IosTool, info);
|
|
|
|
|
},
|
|
|
|
|
Qt::QueuedConnection);
|
2024-01-10 10:52:04 +01:00
|
|
|
handler->requestDeviceInfo(devId);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const Group root{sequential, stopOnSuccess, infoFromDeviceCtl, infoFromIosTool};
|
2024-01-15 11:47:44 +01:00
|
|
|
|
|
|
|
|
TaskTree *task = new TaskTree(root);
|
|
|
|
|
m_updateTasks[devId].reset(task); // cancels any existing update, not calling done handlers
|
|
|
|
|
connect(task, &TaskTree::done, this, [this, task, devId] {
|
|
|
|
|
const auto taskIt = m_updateTasks.find(devId);
|
|
|
|
|
QTC_ASSERT(taskIt != m_updateTasks.end(), return);
|
|
|
|
|
QTC_ASSERT(taskIt->second.get() == task, return);
|
|
|
|
|
taskIt->second.release()->deleteLater();
|
|
|
|
|
m_updateTasks.erase(taskIt);
|
|
|
|
|
});
|
2024-01-10 10:52:04 +01:00
|
|
|
task->start();
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-12 11:40:20 +01:00
|
|
|
void IosDeviceManager::deviceInfo(const QString &uid,
|
|
|
|
|
IosDevice::Handler handler,
|
2013-04-25 16:02:17 +02:00
|
|
|
const Ios::IosToolHandler::Dict &info)
|
|
|
|
|
{
|
|
|
|
|
DeviceManager *devManager = DeviceManager::instance();
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id baseDevId(Constants::IOS_DEVICE_ID);
|
|
|
|
|
Utils::Id devType(Constants::IOS_DEVICE_TYPE);
|
|
|
|
|
Utils::Id devId = baseDevId.withSuffix(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
IDevice::ConstPtr dev = devManager->find(devId);
|
|
|
|
|
bool skipUpdate = false;
|
2018-11-12 19:55:59 +01:00
|
|
|
IosDevice *newDev = nullptr;
|
2013-04-25 16:02:17 +02:00
|
|
|
if (!dev.isNull() && dev->type() == devType) {
|
2018-11-12 19:55:59 +01:00
|
|
|
auto iosDev = static_cast<const IosDevice *>(dev.data());
|
2024-01-12 11:40:20 +01:00
|
|
|
if (iosDev->m_handler == handler && iosDev->m_extraInfo == info) {
|
2013-04-25 16:02:17 +02:00
|
|
|
skipUpdate = true;
|
|
|
|
|
newDev = const_cast<IosDevice *>(iosDev);
|
|
|
|
|
} else {
|
2019-05-07 12:44:45 +02:00
|
|
|
newDev = new IosDevice();
|
|
|
|
|
newDev->fromMap(iosDev->toMap());
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
newDev = new IosDevice(uid);
|
|
|
|
|
}
|
|
|
|
|
if (!skipUpdate) {
|
2020-07-10 14:40:38 +02:00
|
|
|
if (info.contains(kDeviceName))
|
2023-09-07 14:04:19 +02:00
|
|
|
newDev->settings()->displayName.setValue(info.value(kDeviceName));
|
2013-04-25 16:02:17 +02:00
|
|
|
newDev->m_extraInfo = info;
|
2024-01-12 11:40:20 +01:00
|
|
|
newDev->m_handler = handler;
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "updated info of ios device " << uid;
|
2013-04-25 16:02:17 +02:00
|
|
|
dev = IDevice::ConstPtr(newDev);
|
|
|
|
|
devManager->addDevice(dev);
|
|
|
|
|
}
|
|
|
|
|
QLatin1String devStatusKey = QLatin1String("developerStatus");
|
|
|
|
|
if (info.contains(devStatusKey)) {
|
|
|
|
|
QString devStatus = info.value(devStatusKey);
|
2024-01-10 10:52:04 +01:00
|
|
|
if (devStatus == vDevelopment) {
|
2013-04-25 16:02:17 +02:00
|
|
|
devManager->setDeviceState(newDev->id(), IDevice::DeviceReadyToUse);
|
2014-02-14 01:08:09 +01:00
|
|
|
m_userModeDeviceIds.removeOne(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
} else {
|
|
|
|
|
devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
|
2014-02-14 01:08:09 +01:00
|
|
|
bool shouldIgnore = newDev->m_ignoreDevice;
|
|
|
|
|
newDev->m_ignoreDevice = true;
|
2024-01-10 10:52:04 +01:00
|
|
|
if (devStatus == vOff) {
|
2014-02-14 01:08:09 +01:00
|
|
|
if (!shouldIgnore && !IosConfigurations::ignoreAllDevices()) {
|
|
|
|
|
QMessageBox mBox;
|
2022-12-20 13:39:23 +01:00
|
|
|
mBox.setText(Tr::tr("An iOS device in user mode has been detected."));
|
|
|
|
|
mBox.setInformativeText(Tr::tr("Do you want to see how to set it up for development?"));
|
2014-02-14 01:08:09 +01:00
|
|
|
mBox.setStandardButtons(QMessageBox::NoAll | QMessageBox::No | QMessageBox::Yes);
|
|
|
|
|
mBox.setDefaultButton(QMessageBox::Yes);
|
|
|
|
|
int ret = mBox.exec();
|
|
|
|
|
switch (ret) {
|
|
|
|
|
case QMessageBox::Yes:
|
2019-01-24 10:42:24 +01:00
|
|
|
Core::HelpManager::showHelpUrl(
|
2014-02-14 01:08:09 +01:00
|
|
|
QLatin1String("qthelp://org.qt-project.qtcreator/doc/creator-developing-ios.html"));
|
|
|
|
|
break;
|
|
|
|
|
case QMessageBox::No:
|
|
|
|
|
break;
|
|
|
|
|
case QMessageBox::NoAll:
|
|
|
|
|
IosConfigurations::setIgnoreAllDevices(true);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!m_userModeDeviceIds.contains(uid))
|
|
|
|
|
m_userModeDeviceIds.append(uid);
|
|
|
|
|
m_userModeDevicesTimer.start();
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-11 13:55:45 +02:00
|
|
|
#ifdef Q_OS_MAC
|
2013-04-25 16:02:17 +02:00
|
|
|
namespace {
|
|
|
|
|
io_iterator_t gAddedIter;
|
|
|
|
|
io_iterator_t gRemovedIter;
|
|
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
|
void deviceConnectedCallback(void *refCon, io_iterator_t iterator)
|
|
|
|
|
{
|
2013-11-28 14:45:56 +01:00
|
|
|
try {
|
|
|
|
|
kern_return_t kr;
|
|
|
|
|
io_service_t usbDevice;
|
|
|
|
|
(void) refCon;
|
|
|
|
|
|
|
|
|
|
while ((usbDevice = IOIteratorNext(iterator))) {
|
|
|
|
|
io_name_t deviceName;
|
|
|
|
|
|
|
|
|
|
// Get the USB device's name.
|
|
|
|
|
kr = IORegistryEntryGetName(usbDevice, deviceName);
|
|
|
|
|
QString name;
|
|
|
|
|
if (KERN_SUCCESS == kr)
|
|
|
|
|
name = QString::fromLocal8Bit(deviceName);
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "ios device " << name << " in deviceAddedCallback";
|
2013-04-25 16:02:17 +02:00
|
|
|
|
|
|
|
|
CFStringRef cfUid = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(
|
|
|
|
|
usbDevice,
|
|
|
|
|
CFSTR(kUSBSerialNumberString),
|
|
|
|
|
kCFAllocatorDefault, 0));
|
2020-01-14 15:09:10 +01:00
|
|
|
if (cfUid) {
|
|
|
|
|
QString uid = CFStringRef2QString(cfUid);
|
|
|
|
|
CFRelease(cfUid);
|
|
|
|
|
qCDebug(detectLog) << "device UID is" << uid;
|
|
|
|
|
IosDeviceManager::instance()->deviceConnected(uid, name);
|
|
|
|
|
} else {
|
|
|
|
|
qCDebug(detectLog) << "failed to retrieve device's UID";
|
|
|
|
|
}
|
2013-11-28 14:45:56 +01:00
|
|
|
|
|
|
|
|
// Done with this USB device; release the reference added by IOIteratorNext
|
|
|
|
|
kr = IOObjectRelease(usbDevice);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
2013-11-28 14:45:56 +01:00
|
|
|
}
|
2014-11-28 11:15:37 +01:00
|
|
|
catch (const std::exception &e) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(detectLog) << "Exception " << e.what() << " in iosdevice.cpp deviceConnectedCallback";
|
2013-11-28 14:45:56 +01:00
|
|
|
}
|
|
|
|
|
catch (...) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(detectLog) << "Exception in iosdevice.cpp deviceConnectedCallback";
|
2013-11-28 14:45:56 +01:00
|
|
|
throw;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void deviceDisconnectedCallback(void *refCon, io_iterator_t iterator)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
kern_return_t kr;
|
|
|
|
|
io_service_t usbDevice;
|
|
|
|
|
(void) refCon;
|
|
|
|
|
|
|
|
|
|
while ((usbDevice = IOIteratorNext(iterator))) {
|
|
|
|
|
io_name_t deviceName;
|
|
|
|
|
|
|
|
|
|
// Get the USB device's name.
|
|
|
|
|
kr = IORegistryEntryGetName(usbDevice, deviceName);
|
2020-01-14 15:09:10 +01:00
|
|
|
QString name;
|
|
|
|
|
if (KERN_SUCCESS == kr)
|
|
|
|
|
name = QString::fromLocal8Bit(deviceName);
|
|
|
|
|
qCDebug(detectLog) << "ios device " << name << " in deviceDisconnectedCallback";
|
|
|
|
|
|
|
|
|
|
CFStringRef cfUid = static_cast<CFStringRef>(
|
|
|
|
|
IORegistryEntryCreateCFProperty(usbDevice,
|
|
|
|
|
CFSTR(kUSBSerialNumberString),
|
|
|
|
|
kCFAllocatorDefault,
|
|
|
|
|
0));
|
|
|
|
|
if (cfUid) {
|
2013-11-28 14:45:56 +01:00
|
|
|
QString uid = CFStringRef2QString(cfUid);
|
|
|
|
|
CFRelease(cfUid);
|
|
|
|
|
IosDeviceManager::instance()->deviceDisconnected(uid);
|
2020-01-14 15:09:10 +01:00
|
|
|
} else {
|
|
|
|
|
qCDebug(detectLog) << "failed to retrieve device's UID";
|
2013-11-28 14:45:56 +01:00
|
|
|
}
|
2013-04-25 16:02:17 +02:00
|
|
|
|
2013-11-28 14:45:56 +01:00
|
|
|
// Done with this USB device; release the reference added by IOIteratorNext
|
|
|
|
|
kr = IOObjectRelease(usbDevice);
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-28 11:15:37 +01:00
|
|
|
catch (const std::exception &e) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(detectLog) << "Exception " << e.what() << " in iosdevice.cpp deviceDisconnectedCallback";
|
2013-11-28 14:45:56 +01:00
|
|
|
}
|
|
|
|
|
catch (...) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCWarning(detectLog) << "Exception in iosdevice.cpp deviceDisconnectedCallback";
|
2013-11-28 14:45:56 +01:00
|
|
|
throw;
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // extern C
|
|
|
|
|
|
|
|
|
|
} // anonymous namespace
|
2013-10-11 13:55:45 +02:00
|
|
|
#endif
|
2013-04-25 16:02:17 +02:00
|
|
|
|
|
|
|
|
void IosDeviceManager::monitorAvailableDevices()
|
|
|
|
|
{
|
2013-10-11 13:55:45 +02:00
|
|
|
#ifdef Q_OS_MAC
|
2013-04-25 16:02:17 +02:00
|
|
|
CFMutableDictionaryRef matchingDictionary =
|
|
|
|
|
IOServiceMatching("IOUSBDevice" );
|
|
|
|
|
{
|
|
|
|
|
UInt32 vendorId = 0x05ac;
|
|
|
|
|
CFNumberRef cfVendorValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
|
|
|
|
|
&vendorId );
|
|
|
|
|
CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBVendorID ), cfVendorValue);
|
|
|
|
|
CFRelease( cfVendorValue );
|
|
|
|
|
UInt32 productId = 0x1280;
|
|
|
|
|
CFNumberRef cfProductIdValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
|
|
|
|
|
&productId );
|
|
|
|
|
CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBProductID ), cfProductIdValue);
|
|
|
|
|
CFRelease( cfProductIdValue );
|
|
|
|
|
UInt32 productIdMask = 0xFFC0;
|
|
|
|
|
CFNumberRef cfProductIdMaskValue = CFNumberCreate( kCFAllocatorDefault, kCFNumberSInt32Type,
|
|
|
|
|
&productIdMask );
|
|
|
|
|
CFDictionaryAddValue( matchingDictionary, CFSTR( kUSBProductIDMask ), cfProductIdMaskValue);
|
|
|
|
|
CFRelease( cfProductIdMaskValue );
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-11 11:35:12 +02:00
|
|
|
#if QT_MACOS_DEPLOYMENT_TARGET_BELOW(120000)
|
|
|
|
|
const mach_port_t port = kIOMasterPortDefault; // deprecated in macOS 12
|
|
|
|
|
#else
|
|
|
|
|
const mach_port_t port = kIOMainPortDefault; // available since macOS 12
|
|
|
|
|
#endif
|
|
|
|
|
IONotificationPortRef notificationPort = IONotificationPortCreate(port);
|
2013-04-25 16:02:17 +02:00
|
|
|
CFRunLoopSourceRef runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
|
|
|
|
|
|
|
|
|
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
|
|
|
|
|
|
|
|
|
// IOServiceAddMatchingNotification releases this, so we retain for the next call
|
|
|
|
|
CFRetain(matchingDictionary);
|
|
|
|
|
|
|
|
|
|
// Now set up a notification to be called when a device is first matched by I/O Kit.
|
2022-05-11 11:35:12 +02:00
|
|
|
IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
|
kIOMatchedNotification,
|
|
|
|
|
matchingDictionary,
|
|
|
|
|
deviceConnectedCallback,
|
|
|
|
|
NULL,
|
|
|
|
|
&gAddedIter);
|
|
|
|
|
|
|
|
|
|
IOServiceAddMatchingNotification(notificationPort,
|
|
|
|
|
kIOTerminatedNotification,
|
|
|
|
|
matchingDictionary,
|
|
|
|
|
deviceDisconnectedCallback,
|
|
|
|
|
NULL,
|
|
|
|
|
&gRemovedIter);
|
2013-04-25 16:02:17 +02:00
|
|
|
|
|
|
|
|
// Iterate once to get already-present devices and arm the notification
|
|
|
|
|
deviceConnectedCallback(NULL, gAddedIter);
|
|
|
|
|
deviceDisconnectedCallback(NULL, gRemovedIter);
|
2013-10-11 13:55:45 +02:00
|
|
|
#endif
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IosDeviceManager::IosDeviceManager(QObject *parent) :
|
|
|
|
|
QObject(parent)
|
|
|
|
|
{
|
2014-02-14 01:08:09 +01:00
|
|
|
m_userModeDevicesTimer.setSingleShot(true);
|
|
|
|
|
m_userModeDevicesTimer.setInterval(8000);
|
2016-06-29 19:35:23 +03:00
|
|
|
connect(&m_userModeDevicesTimer, &QTimer::timeout,
|
|
|
|
|
this, &IosDeviceManager::updateUserModeDevices);
|
2014-02-14 01:08:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IosDeviceManager::updateUserModeDevices()
|
|
|
|
|
{
|
2022-10-07 14:46:06 +02:00
|
|
|
for (const QString &uid : std::as_const(m_userModeDeviceIds))
|
2014-02-14 01:08:09 +01:00
|
|
|
updateInfo(uid);
|
2013-04-25 16:02:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IosDeviceManager *IosDeviceManager::instance()
|
|
|
|
|
{
|
|
|
|
|
static IosDeviceManager obj;
|
|
|
|
|
return &obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IosDeviceManager::updateAvailableDevices(const QStringList &devices)
|
|
|
|
|
{
|
2022-05-31 16:21:46 +02:00
|
|
|
for (const QString &uid : devices)
|
2013-04-25 16:02:17 +02:00
|
|
|
deviceConnected(uid);
|
|
|
|
|
|
|
|
|
|
DeviceManager *devManager = DeviceManager::instance();
|
|
|
|
|
for (int iDevice = 0; iDevice < devManager->deviceCount(); ++iDevice) {
|
|
|
|
|
IDevice::ConstPtr dev = devManager->deviceAt(iDevice);
|
2020-06-26 13:59:38 +02:00
|
|
|
Utils::Id devType(Constants::IOS_DEVICE_TYPE);
|
2013-04-25 16:02:17 +02:00
|
|
|
if (dev.isNull() || dev->type() != devType)
|
|
|
|
|
continue;
|
2018-11-12 19:55:59 +01:00
|
|
|
auto iosDev = static_cast<const IosDevice *>(dev.data());
|
2013-04-25 16:02:17 +02:00
|
|
|
if (devices.contains(iosDev->uniqueDeviceID()))
|
|
|
|
|
continue;
|
|
|
|
|
if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
|
2014-07-07 09:24:17 +02:00
|
|
|
qCDebug(detectLog) << "disconnecting device " << iosDev->uniqueDeviceID();
|
2013-04-25 16:02:17 +02:00
|
|
|
devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-20 19:13:28 +01:00
|
|
|
// Factory
|
|
|
|
|
|
|
|
|
|
IosDeviceFactory::IosDeviceFactory()
|
2019-05-21 15:55:08 +02:00
|
|
|
: IDeviceFactory(Constants::IOS_DEVICE_TYPE)
|
2019-02-20 19:13:28 +01:00
|
|
|
{
|
|
|
|
|
setDisplayName(IosDevice::name());
|
|
|
|
|
setCombinedIcon(":/ios/images/iosdevicesmall.png",
|
|
|
|
|
":/ios/images/iosdevice.png");
|
|
|
|
|
setConstructionFunction([] { return IDevice::Ptr(new IosDevice); });
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-25 13:19:26 +02:00
|
|
|
bool IosDeviceFactory::canRestore(const Store &map) const
|
2019-02-20 19:13:28 +01:00
|
|
|
{
|
2023-08-25 13:19:26 +02:00
|
|
|
Store vMap = map.value(Constants::EXTRA_INFO_KEY).value<Store>();
|
2020-07-10 14:40:38 +02:00
|
|
|
if (vMap.isEmpty() || vMap.value(kDeviceName).toString() == QLatin1String("*unknown*"))
|
2019-02-20 19:13:28 +01:00
|
|
|
return false; // transient device (probably generated during an activation)
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-10 14:40:38 +02:00
|
|
|
IosDeviceInfoWidget::IosDeviceInfoWidget(const IDevice::Ptr &device)
|
|
|
|
|
: IDeviceWidget(device)
|
|
|
|
|
{
|
|
|
|
|
const auto iosDevice = qSharedPointerCast<IosDevice>(device);
|
2024-01-11 14:43:54 +01:00
|
|
|
using namespace Layouting;
|
|
|
|
|
// clang-format off
|
|
|
|
|
Form {
|
|
|
|
|
Tr::tr("Device name:"), iosDevice->deviceName(), br,
|
|
|
|
|
Tr::tr("Identifier:"), iosDevice->uniqueInternalDeviceId(), br,
|
|
|
|
|
Tr::tr("OS Version:"), iosDevice->osVersion(), br,
|
|
|
|
|
Tr::tr("CPU Architecture:"), iosDevice->cpuArchitecture(),
|
|
|
|
|
noMargin
|
|
|
|
|
}.attachTo(this);
|
|
|
|
|
// clang-format on
|
2020-07-10 14:40:38 +02:00
|
|
|
}
|
|
|
|
|
|
2022-12-20 13:39:23 +01:00
|
|
|
} // Ios::Internal
|