aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/ios/iosdevice.cpp
diff options
context:
space:
mode:
authorFawzi Mohamed <[email protected]>2013-04-25 16:02:17 +0200
committerEike Ziller <[email protected]>2013-10-02 13:15:49 +0200
commit8d96ce557ef5b49d0cfea2b1633e09354948088c (patch)
tree8810583d9685f954b0d7ea6689b314fb2f2eb4bb /src/plugins/ios/iosdevice.cpp
parent3a7d91ca4404937889986c54b730d7b01208cc27 (diff)
ios: preliminary support for ios
first work in progress support for ios * separate iosTool using xml communication used for device info and run * iossim tool to handle the simulator * debug prepared but not working * separate gcc toolchain detection fix for simulator 1) add a QT built for ios 2) open a project, for example qtbase/examples/widgets/animation/animatedtiles/animatedtiles.pro 3) build/run... Change-Id: I7e01604e416338cbe4692dfb34f5d3f31312702d Reviewed-by: Eike Ziller <[email protected]>
Diffstat (limited to 'src/plugins/ios/iosdevice.cpp')
-rw-r--r--src/plugins/ios/iosdevice.cpp519
1 files changed, 519 insertions, 0 deletions
diff --git a/src/plugins/ios/iosdevice.cpp b/src/plugins/ios/iosdevice.cpp
new file mode 100644
index 00000000000..853328a0ff6
--- /dev/null
+++ b/src/plugins/ios/iosdevice.cpp
@@ -0,0 +1,519 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "iosdevice.h"
+#include "iosconstants.h"
+#include "iosconfigurations.h"
+#include "iostoolhandler.h"
+#include <projectexplorer/devicesupport/devicemanager.h>
+#include <projectexplorer/kitinformation.h>
+
+#include <QCoreApplication>
+#include <QVariant>
+#include <QVariantMap>
+#include <QMessageBox>
+
+#include <IOKit/IOKitLib.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <CoreFoundation/CoreFoundation.h>
+using namespace ProjectExplorer;
+
+static bool debugDeviceDetection = false;
+
+namespace {
+QString CFStringRef2QString(CFStringRef s)
+{
+ 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;
+}
+
+}
+
+namespace Ios {
+namespace Internal {
+
+const char extraInfoKey[] = "extraInfo";
+
+IosDevice::IosDevice()
+ : IDevice(Core::Id(Constants::IOS_DEVICE_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Hardware,
+ Constants::IOS_DEVICE_ID)
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosDevice", "iOS Device"));
+ setDeviceState(DeviceStateUnknown);
+}
+
+IosDevice::IosDevice(const IosDevice &other)
+ : IDevice(other), m_extraInfo(other.m_extraInfo), m_ignoreDevice(other.m_ignoreDevice)
+{ }
+
+IosDevice::IosDevice(const QString &uid)
+ : IDevice(Core::Id(Constants::IOS_DEVICE_TYPE),
+ IDevice::AutoDetected,
+ IDevice::Hardware,
+ Core::Id(Constants::IOS_DEVICE_ID).withSuffix(uid))
+{
+ setDisplayName(QCoreApplication::translate("Ios::Internal::IosDevice", "iOS Device"));
+ setDeviceState(DeviceStateUnknown);
+}
+
+
+IDevice::DeviceInfo IosDevice::deviceInformation() const
+{
+ IDevice::DeviceInfo res;
+ QMapIterator<QString, QString> i(m_extraInfo);
+ while (i.hasNext()) {
+ i.next();
+ 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;
+}
+
+QString IosDevice::displayType() const
+{
+ return QCoreApplication::translate("Ios::Internal::IosDevice", "iOS");
+}
+
+IDeviceWidget *IosDevice::createWidget()
+{
+ return 0;
+}
+
+QList<Core::Id> IosDevice::actionIds() const
+{
+ return QList<Core::Id>(); // add activation action?
+}
+
+QString IosDevice::displayNameForActionId(Core::Id actionId) const
+{
+ Q_UNUSED(actionId)
+ return QString();
+}
+
+void IosDevice::executeAction(Core::Id actionId, QWidget *parent)
+{
+ Q_UNUSED(actionId)
+ Q_UNUSED(parent)
+}
+
+DeviceProcessSignalOperation::Ptr IosDevice::signalOperation() const
+{
+ return DeviceProcessSignalOperation::Ptr();
+}
+
+IDevice::Ptr IosDevice::clone() const
+{
+ return IDevice::Ptr(new IosDevice(*this));
+}
+
+void IosDevice::fromMap(const QVariantMap &map)
+{
+ IDevice::fromMap(map);
+ QVariantMap vMap = map.value(QLatin1String(extraInfoKey)).toMap();
+ QMapIterator<QString, QVariant> i(vMap);
+ m_extraInfo.clear();
+ while (i.hasNext()) {
+ i.next();
+ m_extraInfo.insert(i.key(), i.value().toString());
+ }
+}
+
+QVariantMap IosDevice::toMap() const
+{
+ QVariantMap res = IDevice::toMap();
+ QVariantMap vMap;
+ QMapIterator<QString, QString> i(m_extraInfo);
+ while (i.hasNext()) {
+ i.next();
+ vMap.insert(i.key(), i.value());
+ }
+ res.insert(QLatin1String(extraInfoKey), vMap);
+ return res;
+}
+
+QString IosDevice::uniqueDeviceID() const
+{
+ return id().suffixAfter(Core::Id(Constants::IOS_DEVICE_ID));
+}
+/*
+// add back?
+
+QString IosDevice::cpuArchitecure() const
+{
+ return m_extraInfo.value(QLatin1String("deviceInfo")).toMap()
+ .value(QLatin1String("CPUArchitecture")).toString();
+}
+
+QString IosDevice::productType() const
+{
+ return m_extraInfo.value(QLatin1String("deviceInfo")).toMap()
+ .value(QLatin1String("ProductType")).toString();
+}*/
+
+
+// IosDeviceManager
+
+IosDeviceManager::TranslationMap IosDeviceManager::translationMap()
+{
+ static TranslationMap *translationMap = 0;
+ if (translationMap)
+ return *translationMap;
+ TranslationMap &tMap = *new TranslationMap;
+ tMap[QLatin1String("deviceName")] = tr("Device name");
+ tMap[QLatin1String("developerStatus")] = tr("Developer status");
+ tMap[QLatin1String("deviceConnected")] = tr("Connected");
+ tMap[QLatin1String("YES")] = tr("yes");
+ tMap[QLatin1String("NO")] = tr("no");
+ tMap[QLatin1String("YES")] = tr("yes");
+ tMap[QLatin1String("*unknown*")] = tr("unknown");
+ translationMap = &tMap;
+ return tMap;
+}
+
+void IosDeviceManager::deviceConnected(const QString &uid, const QString &name)
+{
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ if (dev.isNull()) {
+ IosDevice *newDev = new IosDevice(uid);
+ if (!name.isNull())
+ newDev->setDisplayName(name);
+ if (debugDeviceDetection)
+ qDebug() << "adding ios device " << uid;
+ devManager->addDevice(IDevice::ConstPtr(newDev));
+ } else if (dev->deviceState() != IDevice::DeviceConnected &&
+ dev->deviceState() != IDevice::DeviceReadyToUse) {
+ if (debugDeviceDetection)
+ qDebug() << "updating ios device " << uid;
+ IosDevice *newDev = 0;
+ if (dev->type() == devType) {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ newDev = new IosDevice(*iosDev);
+ } else {
+ newDev = new IosDevice(uid);
+ }
+ devManager->addDevice(IDevice::ConstPtr(newDev));
+ }
+ updateInfo(uid);
+}
+
+void IosDeviceManager::deviceDisconnected(const QString &uid)
+{
+ if (debugDeviceDetection)
+ qDebug() << "detected disconnection of ios device " << uid;
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ if (dev.isNull() || dev->type() != devType) {
+ qDebug() << "ignoring disconnection of ios device " << uid; // should neve happen
+ } else {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
+ if (debugDeviceDetection)
+ qDebug() << "disconnecting device " << iosDev->uniqueDeviceID();
+ devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
+ }
+ }
+}
+
+void IosDeviceManager::updateInfo(const QString &devId)
+{
+ IosToolHandler *requester = new IosToolHandler(IosToolHandler::IosDeviceType, this);
+ connect(requester, SIGNAL(deviceInfo(Ios::IosToolHandler*,QString,Ios::IosToolHandler::Dict)),
+ SLOT(deviceInfo(Ios::IosToolHandler *,QString,Ios::IosToolHandler::Dict)));
+ connect(requester, SIGNAL(finished(Ios::IosToolHandler*)),
+ SLOT(infoGathererFinished(Ios::IosToolHandler*)));
+ requester->requestDeviceInfo(devId);
+}
+
+void IosDeviceManager::deviceInfo(IosToolHandler *, const QString &uid,
+ const Ios::IosToolHandler::Dict &info)
+{
+ DeviceManager *devManager = DeviceManager::instance();
+ Core::Id baseDevId(Constants::IOS_DEVICE_ID);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ Core::Id devId = baseDevId.withSuffix(uid);
+ IDevice::ConstPtr dev = devManager->find(devId);
+ bool skipUpdate = false;
+ IosDevice *newDev = 0;
+ if (!dev.isNull() && dev->type() == devType) {
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (iosDev->m_extraInfo == info) {
+ skipUpdate = true;
+ newDev = const_cast<IosDevice *>(iosDev);
+ } else {
+ newDev = new IosDevice(*iosDev);
+ }
+ } else {
+ newDev = new IosDevice(uid);
+ }
+ if (!skipUpdate) {
+ QString devNameKey = QLatin1String("deviceName");
+ if (info.contains(devNameKey))
+ newDev->setDisplayName(info.value(devNameKey));
+ newDev->m_extraInfo = info;
+ if (debugDeviceDetection)
+ qDebug() << "updated info of ios device " << uid;
+ dev = IDevice::ConstPtr(newDev);
+ devManager->addDevice(dev);
+ }
+ QLatin1String devStatusKey = QLatin1String("developerStatus");
+ if (info.contains(devStatusKey)) {
+ QString devStatus = info.value(devStatusKey);
+ if (devStatus == QLatin1String("*off*")) {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
+ if (!newDev->m_ignoreDevice && !IosConfigurations::instance().config().ignoreAllDevices) {
+ QMessageBox mBox;
+ mBox.setText(tr("An iOS device in user mode has been detected."));
+ mBox.setInformativeText(tr("Do you want to see how to set it up for development?"));
+ mBox.setStandardButtons(QMessageBox::NoAll | QMessageBox::No | QMessageBox::Yes);
+ mBox.setDefaultButton(QMessageBox::No);
+ int ret = mBox.exec();
+ switch (ret) {
+ case QMessageBox::Yes:
+ // open doc
+ break;
+ case QMessageBox::No:
+ newDev->m_ignoreDevice = true;
+ break;
+ case QMessageBox::NoAll:
+ {
+ IosConfig conf = IosConfigurations::instance().config();
+ conf.ignoreAllDevices = true;
+ IosConfigurations::instance().setConfig(conf);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ } else if (devStatus == QLatin1String("Development")) {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceReadyToUse);
+ } else {
+ devManager->setDeviceState(newDev->id(), IDevice::DeviceConnected);
+ }
+ }
+}
+
+void IosDeviceManager::infoGathererFinished(IosToolHandler *gatherer)
+{
+ gatherer->deleteLater();
+}
+
+namespace {
+io_iterator_t gAddedIter;
+io_iterator_t gRemovedIter;
+
+extern "C" {
+void deviceConnectedCallback(void *refCon, io_iterator_t iterator)
+{
+ 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);
+ if (debugDeviceDetection)
+ qDebug() << "ios device " << name << " in deviceAddedCallback";
+
+ CFStringRef cfUid = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(
+ usbDevice,
+ CFSTR(kUSBSerialNumberString),
+ kCFAllocatorDefault, 0));
+ QString uid = CFStringRef2QString(cfUid);
+ CFRelease(cfUid);
+ IosDeviceManager::instance()->deviceConnected(uid, name);
+
+ // Done with this USB device; release the reference added by IOIteratorNext
+ kr = IOObjectRelease(usbDevice);
+ }
+}
+
+void deviceDisconnectedCallback(void *refCon, io_iterator_t iterator)
+{
+ 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);
+ if (KERN_SUCCESS != kr)
+ deviceName[0] = '\0';
+ if (debugDeviceDetection)
+ qDebug() << "ios device " << deviceName << " in deviceDisconnectedCallback";
+
+ {
+ CFStringRef cfUid = static_cast<CFStringRef>(IORegistryEntryCreateCFProperty(
+ usbDevice,
+ CFSTR(kUSBSerialNumberString),
+ kCFAllocatorDefault, 0));
+ QString uid = CFStringRef2QString(cfUid);
+ CFRelease(cfUid);
+ IosDeviceManager::instance()->deviceDisconnected(uid);
+ }
+
+ // Done with this USB device; release the reference added by IOIteratorNext
+ kr = IOObjectRelease(usbDevice);
+ }
+}
+
+} // extern C
+
+} // anonymous namespace
+
+void IosDeviceManager::monitorAvailableDevices()
+{
+ 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 );
+ }
+
+ IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
+ 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.
+ kern_return_t kr;
+ kr = IOServiceAddMatchingNotification(notificationPort,
+ kIOMatchedNotification,
+ matchingDictionary,
+ deviceConnectedCallback,
+ NULL,
+ &gAddedIter);
+
+
+ kr = IOServiceAddMatchingNotification(notificationPort,
+ kIOTerminatedNotification,
+ matchingDictionary,
+ deviceDisconnectedCallback,
+ NULL,
+ &gRemovedIter);
+
+ // Iterate once to get already-present devices and arm the notification
+ deviceConnectedCallback(NULL, gAddedIter);
+ deviceDisconnectedCallback(NULL, gRemovedIter);
+
+}
+
+
+IosDeviceManager::IosDeviceManager(QObject *parent) :
+ QObject(parent)
+{
+}
+
+IosDeviceManager *IosDeviceManager::instance()
+{
+ static IosDeviceManager obj;
+ return &obj;
+}
+
+void IosDeviceManager::updateAvailableDevices(const QStringList &devices)
+{
+ foreach (const QString &uid, devices)
+ deviceConnected(uid);
+
+ DeviceManager *devManager = DeviceManager::instance();
+ for (int iDevice = 0; iDevice < devManager->deviceCount(); ++iDevice) {
+ IDevice::ConstPtr dev = devManager->deviceAt(iDevice);
+ Core::Id devType(Constants::IOS_DEVICE_TYPE);
+ if (dev.isNull() || dev->type() != devType)
+ continue;
+ const IosDevice *iosDev = static_cast<const IosDevice *>(dev.data());
+ if (devices.contains(iosDev->uniqueDeviceID()))
+ continue;
+ if (iosDev->deviceState() != IDevice::DeviceDisconnected) {
+ if (debugDeviceDetection)
+ qDebug() << "disconnecting device " << iosDev->uniqueDeviceID();
+ devManager->setDeviceState(iosDev->id(), IDevice::DeviceDisconnected);
+ }
+ }
+}
+
+IosDevice::ConstPtr IosKitInformation::device(Kit *kit)
+{
+ if (!kit)
+ return IosDevice::ConstPtr();
+ ProjectExplorer::IDevice::ConstPtr dev = ProjectExplorer::DeviceKitInformation::device(kit);
+ IosDevice::ConstPtr res = dev.dynamicCast<const IosDevice>();
+ return res;
+}
+
+} // namespace Internal
+} // namespace Ios