diff options
author | Fawzi Mohamed <[email protected]> | 2013-04-25 16:02:17 +0200 |
---|---|---|
committer | Eike Ziller <[email protected]> | 2013-10-02 13:15:49 +0200 |
commit | 8d96ce557ef5b49d0cfea2b1633e09354948088c (patch) | |
tree | 8810583d9685f954b0d7ea6689b314fb2f2eb4bb /src/plugins/ios/iosdevice.cpp | |
parent | 3a7d91ca4404937889986c54b730d7b01208cc27 (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.cpp | 519 |
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 |