Files
qt-creator/src/tools/iostool/iosdevicemanager.cpp
Fawzi Mohamed 8d96ce557e 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 <eike.ziller@digia.com>
2013-10-02 13:15:49 +02:00

1585 lines
60 KiB
C++

/****************************************************************************
**
** 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 "iosdevicemanager.h"
#include <QLibrary>
#include <QDebug>
#include <QStringList>
#include <QString>
#include <QHash>
#include <QMultiHash>
#include <QTimer>
#include <QThread>
#include <QSettings>
#include <mach/error.h>
/* // annoying to import, do without
#if QT_VERSION < 0x050000
#include <private/qcore_mac_p.h>
#else
#include <QtCore/private/qcore_mac_p.h>
#endif
*/
/* standard calling convention under Win32 is __stdcall */
/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
#define MDEV_API __stdcall
#else
#define MDEV_API
#endif
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFRunLoop.h>
#ifdef MOBILE_DEV_DIRECT_LINK
#include "MobileDevice.h"
#endif
static const bool debugGdbServer = false;
static const bool debugAll = false;
// ------- MobileDeviceLib interface --------
namespace {
#ifndef MOBILE_DEV_DIRECT_LINK
/* Messages passed to device notification callbacks: passed as part of
* AMDeviceNotificationCallbackInfo. */
enum ADNCI_MSG {
ADNCI_MSG_CONNECTED = 1,
ADNCI_MSG_DISCONNECTED = 2,
ADNCI_MSG_UNSUBSCRIBED = 3
};
#endif
extern "C" {
typedef unsigned int ServiceSocket; // match_port_t (i.e. natural_t) or socket (on windows, i.e sock_t)
typedef int am_res_t; // mach_error_t
#ifndef MOBILE_DEV_DIRECT_LINK
class AMDeviceNotification;
typedef const AMDeviceNotification *AMDeviceNotificationRef;
class AMDevice;
struct AMDeviceNotificationCallbackInfo {
AMDevice *_device;
unsigned int _message;
AMDeviceNotification *_subscription;
};
typedef void (MDEV_API *AMDeviceNotificationCallback)(AMDeviceNotificationCallbackInfo *, void *);
typedef am_res_t (MDEV_API *AMDeviceInstallApplicationCallback)(CFDictionaryRef, void *);
typedef AMDevice *AMDeviceRef;
#endif
typedef am_res_t (MDEV_API *AMDeviceMountImageCallback)(void *, void *);
typedef void (MDEV_API *AMDSetLogLevelPtr)(int);
typedef am_res_t (MDEV_API *AMDeviceNotificationSubscribePtr)(AMDeviceNotificationCallback,
unsigned int, unsigned int, void *,
const AMDeviceNotification **);
typedef am_res_t (MDEV_API *AMDeviceNotificationUnsubscribePtr)(void *);
typedef CFPropertyListRef (MDEV_API *AMDeviceCopyValuePtr)(AMDeviceRef,CFStringRef,CFStringRef);
typedef unsigned int (MDEV_API *AMDeviceGetConnectionIDPtr)(AMDeviceRef);
typedef CFStringRef (MDEV_API *AMDeviceCopyDeviceIdentifierPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceConnectPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDevicePairPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceIsPairedPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceValidatePairingPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceStartSessionPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceStopSessionPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceDisconnectPtr)(AMDeviceRef);
typedef am_res_t (MDEV_API *AMDeviceMountImagePtr)(AMDeviceRef, CFStringRef, CFDictionaryRef,
AMDeviceMountImageCallback, void *);
typedef am_res_t (MDEV_API *AMDeviceStartServicePtr)(AMDeviceRef, CFStringRef, ServiceSocket *, void *);
typedef am_res_t (MDEV_API *AMDeviceTransferApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void *);
typedef am_res_t (MDEV_API *AMDeviceInstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void*);
typedef am_res_t (MDEV_API *AMDeviceUninstallApplicationPtr)(ServiceSocket, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void*);
typedef am_res_t (MDEV_API *AMDeviceLookupApplicationsPtr)(AMDeviceRef, unsigned int, CFDictionaryRef *);
} // extern C
QString CFStringRef2QString(CFStringRef s)
{
if (!s)
return QString();
if (CFGetTypeID(s) != CFStringGetTypeID()) {
qDebug() << "CFStringRef2QString argument is not a string!";
return QLatin1String("*NON CFStringRef*");
}
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;
}
CFStringRef CFStringCreateWithQString(const QString &s)
{
QByteArray bytes = s.toUtf8();
CFStringRef res = CFStringCreateWithBytes ( 0, reinterpret_cast<const unsigned char *>(bytes.constBegin()), bytes.length(),
kCFStringEncodingUTF8, false);
return res;
}
} // anonymous namespace
namespace Ios {
namespace Internal {
class MobileDeviceLib {
public :
MobileDeviceLib();
bool load();
bool isLoaded();
QStringList errors();
//
void setLogLevel(int i) ;
am_res_t deviceNotificationSubscribe(AMDeviceNotificationCallback callback,
unsigned int v1, unsigned int v2, void *v3,
const AMDeviceNotification **handle);
am_res_t deviceNotificationUnsubscribe(void *handle);
CFPropertyListRef deviceCopyValue(AMDeviceRef,CFStringRef,CFStringRef);
unsigned int deviceGetConnectionID(AMDeviceRef);
CFStringRef deviceCopyDeviceIdentifier(AMDeviceRef);
am_res_t deviceConnect(AMDeviceRef);
am_res_t devicePair(AMDeviceRef);
am_res_t deviceIsPaired(AMDeviceRef);
am_res_t deviceValidatePairing(AMDeviceRef);
am_res_t deviceStartSession(AMDeviceRef);
am_res_t deviceStopSession(AMDeviceRef);
am_res_t deviceDisconnect(AMDeviceRef);
am_res_t deviceMountImage(AMDeviceRef, CFStringRef, CFDictionaryRef,
AMDeviceMountImageCallback, void *);
am_res_t deviceStartService(AMDeviceRef, CFStringRef, ServiceSocket *, void *);
am_res_t deviceTransferApplication(int, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void *);
am_res_t deviceInstallApplication(int, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void*);
am_res_t deviceUninstallApplication(int, CFStringRef, CFDictionaryRef,
AMDeviceInstallApplicationCallback,
void*);
am_res_t deviceLookupApplications(AMDeviceRef, unsigned int, CFDictionaryRef *);
void addError(const QString &msg);
void addError(const char *msg);
QStringList m_errors;
private:
QLibrary lib;
QList<QLibrary *> deps;
AMDSetLogLevelPtr m_AMDSetLogLevel;
AMDeviceNotificationSubscribePtr m_AMDeviceNotificationSubscribe;
AMDeviceNotificationUnsubscribePtr m_AMDeviceNotificationUnsubscribe;
AMDeviceCopyValuePtr m_AMDeviceCopyValue;
AMDeviceGetConnectionIDPtr m_AMDeviceGetConnectionID;
AMDeviceCopyDeviceIdentifierPtr m_AMDeviceCopyDeviceIdentifier;
AMDeviceConnectPtr m_AMDeviceConnect;
AMDevicePairPtr m_AMDevicePair;
AMDeviceIsPairedPtr m_AMDeviceIsPaired;
AMDeviceValidatePairingPtr m_AMDeviceValidatePairing;
AMDeviceStartSessionPtr m_AMDeviceStartSession;
AMDeviceStopSessionPtr m_AMDeviceStopSession;
AMDeviceDisconnectPtr m_AMDeviceDisconnect;
AMDeviceMountImagePtr m_AMDeviceMountImage;
AMDeviceStartServicePtr m_AMDeviceStartService;
AMDeviceTransferApplicationPtr m_AMDeviceTransferApplication;
AMDeviceInstallApplicationPtr m_AMDeviceInstallApplication;
AMDeviceUninstallApplicationPtr m_AMDeviceUninstallApplication;
AMDeviceLookupApplicationsPtr m_AMDeviceLookupApplications;
};
extern "C" {
typedef void (*DeviceAvailableCallback)(QString deviceId, AMDeviceRef, void *userData);
}
class PendingDeviceLookup {
public:
QTimer timer;
DeviceAvailableCallback callback;
void *userData;
};
class CommandSession {
public:
virtual ~CommandSession();
explicit CommandSession(const QString &deviceId);
void internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device);
virtual void deviceCallbackReturned() { }
virtual am_res_t appTransferCallback(CFDictionaryRef) { return 0; }
virtual am_res_t appInstallCallback(CFDictionaryRef) { return 0; }
virtual void reportProgress(CFDictionaryRef dict);
virtual void reportProgress2(int progress, const QString &status);
virtual QString commandName();
bool connectDevice();
bool disconnectDevice();
bool startService(const QString &service, ServiceSocket &fd);
void stopService(ServiceSocket fd);
void startDeviceLookup(int timeout);
void addError(const QString &msg);
bool writeAll(ServiceSocket fd, const char *cmd, ssize_t len = -1);
bool sendGdbCommand(ServiceSocket fd, const char *cmd, ssize_t len = -1);
QByteArray readGdbReply(ServiceSocket fd);
bool expectGdbReply(ServiceSocket gdbFd, QByteArray expected);
bool expectGdbOkReply(ServiceSocket gdbFd);
MobileDeviceLib *lib();
QString deviceId;
AMDeviceRef device;
int progressBase;
int unexpectedChars;
private:
bool checkRead(ssize_t nRead, int &maxRetry);
int handleChar(QByteArray &res, char c, int status);
};
// ------- IosManagerPrivate interface --------
class IosDeviceManagerPrivate {
public:
static IosDeviceManagerPrivate *instance();
explicit IosDeviceManagerPrivate (IosDeviceManager *q);
bool watchDevices();
void requestAppOp(const QString &bundlePath, const QStringList &extraArgs,
Ios::IosDeviceManager::AppOp appOp, const QString &deviceId, int timeout);
void requestDeviceInfo(const QString &deviceId, int timeout);
QStringList errors();
void addError(QString errorMsg);
void addDevice(AMDeviceRef device);
void removeDevice(AMDeviceRef device);
void checkPendingLookups();
MobileDeviceLib *lib();
void didTransferApp(const QString &bundlePath, const QString &deviceId,
Ios::IosDeviceManager::OpStatus status);
void didStartApp(const QString &bundlePath, const QString &deviceId,
Ios::IosDeviceManager::OpStatus status, int fd);
void isTransferringApp(const QString &bundlePath, const QString &deviceId, int progress,
const QString &info);
void deviceWithId(QString deviceId, int timeout, DeviceAvailableCallback callback, void *userData);
int processGdbServer(int fd);
private:
IosDeviceManager *q;
QHash<QString, AMDeviceRef> m_devices;
QMultiHash<QString, PendingDeviceLookup *> m_pendingLookups;
AMDeviceNotificationRef m_notification;
MobileDeviceLib m_lib;
};
class DevInfoSession: public CommandSession {
public:
DevInfoSession(const QString &deviceId);
void deviceCallbackReturned();
QString commandName();
};
class AppOpSession: public CommandSession {
public:
QString bundlePath;
QStringList extraArgs;
Ios::IosDeviceManager::AppOp appOp;
AppOpSession(const QString &deviceId, const QString &bundlePath,
const QStringList &extraArgs, Ios::IosDeviceManager::AppOp appOp);
void deviceCallbackReturned();
bool installApp();
bool runApp();
am_res_t appTransferCallback(CFDictionaryRef dict);
am_res_t appInstallCallback(CFDictionaryRef dict);
void reportProgress2(int progress, const QString &status);
QString appPathOnDevice();
QString appId();
QString commandName();
};
} // namespace Internal
} // namespace Ios
namespace {
// ------- callbacks --------
extern "C" void deviceNotificationCallback(AMDeviceNotificationCallbackInfo *info, void *user)
{
if (info == 0)
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("null info in deviceNotificationCallback"));
if (debugAll) {
QDebug dbg=qDebug();
dbg << "device_notification_callback(";
if (info)
dbg << " dev:" << info->_device << " msg:" << info->_message << " subscription:" << info->_subscription;
else
dbg << "*NULL*";
dbg << "," << user << ")";
}
switch (info->_message) {
case ADNCI_MSG_CONNECTED:
Ios::Internal::IosDeviceManagerPrivate::instance()->addDevice(info->_device);
break;
case ADNCI_MSG_DISCONNECTED:
Ios::Internal::IosDeviceManagerPrivate::instance()->removeDevice(info->_device);
break;
case ADNCI_MSG_UNSUBSCRIBED:
break;
default:
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("unexpected notification message value ")
+ QString::number(info->_message));
}
}
extern "C" void deviceAvailableSessionCallback(QString deviceId, AMDeviceRef device, void *userData)
{
if (debugAll)
qDebug() << "deviceAvailableSessionCallback" << QThread::currentThread();
if (userData == 0) {
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("deviceAvailableSessionCallback called with null userData"));
return;
}
Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
session->internalDeviceAvailableCallback(deviceId, device);
}
extern "C" am_res_t appTransferSessionCallback(CFDictionaryRef dict, void *userData)
{
if (debugAll) {
qDebug() << "appTransferSessionCallback" << QThread::currentThread();
CFShow(dict);
}
if (userData == 0) {
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("appTransferSessionCallback called with null userData"));
return 0; // return -1?
}
Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
return session->appTransferCallback(dict);
}
extern "C" am_res_t appInstallSessionCallback(CFDictionaryRef dict, void *userData)
{
if (debugAll) {
qDebug() << "appInstallSessionCallback" << QThread::currentThread();
CFShow(dict);
}
if (userData == 0) {
Ios::Internal::IosDeviceManagerPrivate::instance()->addError(QLatin1String("appInstallSessionCallback called with null userData"));
return 0; // return -1?
}
Ios::Internal::CommandSession *session = static_cast<Ios::Internal::CommandSession *>(userData);
return session->appInstallCallback(dict);
}
} // anonymous namespace
namespace Ios {
namespace Internal {
// ------- IosManagerPrivate implementation --------
IosDeviceManagerPrivate *IosDeviceManagerPrivate::instance()
{
return IosDeviceManager::instance()->d;
}
IosDeviceManagerPrivate::IosDeviceManagerPrivate (IosDeviceManager *q) : q(q), m_notification(0) { }
bool IosDeviceManagerPrivate::watchDevices()
{
if (!m_lib.load())
addError(QLatin1String("Error loading MobileDevice.framework"));
if (!m_lib.errors().isEmpty())
foreach (const QString &msg, m_lib.errors())
addError(msg);
m_lib.setLogLevel(5);
am_res_t e = m_lib.deviceNotificationSubscribe(&deviceNotificationCallback, 0, 0,
0, &m_notification);
if (e != 0) {
addError(QLatin1String("AMDeviceNotificationSubscribe failed"));
return false;
}
if (debugAll)
qDebug() << "AMDeviceNotificationSubscribe successful, m_notificationIter:" << m_notification << QThread::currentThread();
return true;
}
void IosDeviceManagerPrivate::requestAppOp(const QString &bundlePath,
const QStringList &extraArgs,
IosDeviceManager::AppOp appOp,
const QString &deviceId, int timeout)
{
AppOpSession *session = new AppOpSession(deviceId, bundlePath, extraArgs, appOp);
session->startDeviceLookup(timeout);
}
void IosDeviceManagerPrivate::requestDeviceInfo(const QString &deviceId, int timeout)
{
DevInfoSession *session = new DevInfoSession(deviceId);
session->startDeviceLookup(timeout);
}
QStringList IosDeviceManagerPrivate::errors()
{
return m_lib.errors();
}
void IosDeviceManagerPrivate::addError(QString errorMsg)
{
if (debugAll)
qDebug() << "IosManagerPrivate ERROR: " << errorMsg;
emit q->errorMsg(errorMsg);
}
void IosDeviceManagerPrivate::addDevice(AMDeviceRef device)
{
CFStringRef s = m_lib.deviceCopyDeviceIdentifier(device);
QString devId = CFStringRef2QString(s);
if (s) CFRelease(s);
CFRetain(device);
if (debugAll)
qDebug() << "addDevice " << devId;
if (m_devices.contains(devId)) {
if (m_devices.value(devId) == device) {
addError(QLatin1String("double add of device ") + devId);
return;
}
addError(QLatin1String("device change without remove ") + devId);
removeDevice(m_devices.value(devId));
}
m_devices.insert(devId, device);
emit q->deviceAdded(devId);
if (m_pendingLookups.contains(devId) || m_pendingLookups.contains(QString())) {
QList<PendingDeviceLookup *> devices = m_pendingLookups.values(devId);
m_pendingLookups.remove(devId);
devices << m_pendingLookups.values(QString());
m_pendingLookups.remove(QString());
foreach (PendingDeviceLookup *devLookup, devices) {
if (debugAll) qDebug() << "found pending op";
devLookup->timer.stop();
devLookup->callback(devId, device, devLookup->userData);
delete devLookup;
}
}
}
void IosDeviceManagerPrivate::removeDevice(AMDeviceRef device)
{
CFStringRef s = m_lib.deviceCopyDeviceIdentifier(device);
QString devId = CFStringRef2QString(s);
if (s)
CFRelease(s);
if (debugAll)
qDebug() << "removeDevice " << devId;
if (m_devices.contains(devId)) {
if (m_devices.value(devId) == device) {
CFRelease(device);
m_devices.remove(devId);
emit q->deviceRemoved(devId);
return;
}
addError(QLatin1String("ignoring remove of changed device ") + devId);
} else {
addError(QLatin1String("removal of unknown device ") + devId);
}
}
void IosDeviceManagerPrivate::checkPendingLookups()
{
foreach (const QString &deviceId, m_pendingLookups.keys()) {
foreach (PendingDeviceLookup *deviceLookup, m_pendingLookups.values(deviceId)) {
if (!deviceLookup->timer.isActive()) {
m_pendingLookups.remove(deviceId, deviceLookup);
deviceLookup->callback(deviceId, 0, deviceLookup->userData);
delete deviceLookup;
}
}
}
}
MobileDeviceLib *IosDeviceManagerPrivate::lib()
{
return &m_lib;
}
void IosDeviceManagerPrivate::didTransferApp(const QString &bundlePath, const QString &deviceId,
IosDeviceManager::OpStatus status)
{
emit IosDeviceManagerPrivate::instance()->q->didTransferApp(bundlePath, deviceId, status);
}
void IosDeviceManagerPrivate::didStartApp(const QString &bundlePath, const QString &deviceId,
IosDeviceManager::OpStatus status, int fd)
{
emit IosDeviceManagerPrivate::instance()->q->didStartApp(bundlePath, deviceId, status, fd);
}
void IosDeviceManagerPrivate::isTransferringApp(const QString &bundlePath, const QString &deviceId,
int progress, const QString &info)
{
emit IosDeviceManagerPrivate::instance()->q->isTransferringApp(bundlePath, deviceId, progress, info);
}
void IosDeviceManagerPrivate::deviceWithId(QString deviceId, int timeout,
DeviceAvailableCallback callback,
void *userData)
{
if (!m_notification) {
qDebug() << "null notification!!";
/*if (!watchDevices()) {
callback(deviceId, 0, userData);
return;
}*/
}
if (deviceId.isEmpty() && !m_devices.isEmpty()) {
QHash<QString,AMDeviceRef>::iterator i = m_devices.begin();
callback(i.key(), i.value() , userData);
return;
}
if (m_devices.contains(deviceId)) {
callback(deviceId, m_devices.value(deviceId), userData);
return;
}
if (timeout <= 0) {
callback(deviceId, 0, userData);
return;
}
PendingDeviceLookup *pendingLookup = new PendingDeviceLookup;
pendingLookup->callback = callback;
pendingLookup->userData = userData;
pendingLookup->timer.setSingleShot(true);
pendingLookup->timer.setInterval(timeout);
QObject::connect(&(pendingLookup->timer), SIGNAL(timeout()), q, SLOT(checkPendingLookups()));
m_pendingLookups.insertMulti(deviceId, pendingLookup);
pendingLookup->timer.start();
}
enum GdbServerStatus {
NORMAL_PROCESS,
PROTOCOL_ERROR,
STOP_FOR_SIGNAL,
INFERIOR_EXITED,
PROTOCOL_UNHANDLED
};
int IosDeviceManagerPrivate::processGdbServer(int fd)
{
CommandSession session((QString()));
session.sendGdbCommand(fd, "vCont;c"); // resume all threads
GdbServerStatus state = NORMAL_PROCESS;
int maxRetry = 10;
int maxSignal = 5;
while (state == NORMAL_PROCESS) {
QByteArray repl = session.readGdbReply(fd);
int signal = 0;
if (repl.size() > 0) {
state = PROTOCOL_ERROR;
switch (repl.at(0)) {
case 'S':
if (repl.size() < 3) {
addError(QString::fromLatin1("invalid S signal message %1")
.arg(QString::fromLatin1(repl.constData(), repl.size())));
state = PROTOCOL_ERROR;
} else {
signal = QByteArray::fromHex(repl.mid(1,2)).at(0);
addError(QString::fromLatin1("program received signal %1")
.arg(signal));
state = STOP_FOR_SIGNAL;
}
break;
case 'T':
if (repl.size() < 3) {
addError(QString::fromLatin1("invalid T signal message %1")
.arg(QString::fromLatin1(repl.constData(), repl.size())));
state = PROTOCOL_ERROR;
} else {
signal = QByteArray::fromHex(repl.mid(1,2)).at(0);
addError(QString::fromLatin1("program received signal %1, %2")
.arg(signal)
.arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
state = STOP_FOR_SIGNAL;
}
break;
case 'W':
if (repl.size() < 3) {
addError(QString::fromLatin1("invalid W signal message %1")
.arg(QString::fromLatin1(repl.constData(), repl.size())));
state = PROTOCOL_ERROR;
} else {
int exitCode = QByteArray::fromHex(repl.mid(1,2)).at(0);
addError(QString::fromLatin1("exited with exit code %1, %2")
.arg(exitCode)
.arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
state = INFERIOR_EXITED;
}
break;
case 'X':
if (repl.size() < 3) {
addError(QString::fromLatin1("invalid X signal message %1")
.arg(QString::fromLatin1(repl.constData(), repl.size())));
state = PROTOCOL_ERROR;
} else {
int exitCode = QByteArray::fromHex(repl.mid(1,2)).at(0);
addError(QString::fromLatin1("exited due to signal %1, %2")
.arg(exitCode)
.arg(QString::fromLatin1(repl.mid(3,repl.size()-3))));
state = INFERIOR_EXITED;
}
break;
case 'O':
emit q->appOutput(QString::fromLocal8Bit(QByteArray::fromHex(repl.mid(1))));
state = NORMAL_PROCESS;
break;
default:
addError(QString::fromLatin1("non handled response:")
+ QString::fromLatin1(repl.constData(), repl.size()));
state = PROTOCOL_UNHANDLED;
}
if (state == STOP_FOR_SIGNAL) {
QList<int> okSig = QList<int>() << SIGCHLD << SIGCONT << SIGALRM << SIGURG
<< SIGUSR1 << SIGUSR2 << SIGPIPE
<< SIGPROF << SIGWINCH << SIGINFO;
if (signal == 9) {
break;
} else if (!okSig.contains(signal) && --maxSignal < 0) {
addError(QLatin1String("hit maximum number of consecutive signals, stopping"));
break;
}
if (session.sendGdbCommand(fd, "vCont;c"))
state = NORMAL_PROCESS;
else
break;
} else {
maxSignal = 5;
}
maxRetry = 10;
} else {
if (--maxRetry < 0)
break;
}
}
return state != INFERIOR_EXITED;
}
// ------- ConnectSession implementation --------
CommandSession::CommandSession(const QString &deviceId) : deviceId(deviceId), device(0),
progressBase(0), unexpectedChars(0)
{ }
CommandSession::~CommandSession() { }
bool CommandSession::connectDevice()
{
if (!device)
return false;
if (am_res_t error1 = lib()->deviceConnect(device)) {
addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceConnect returned %2")
.arg(deviceId).arg(error1));
return false;
}
if (lib()->deviceIsPaired(device) == 0) { // not paired
if (am_res_t error = lib()->devicePair(device)) {
addError(QString::fromLatin1("connectDevice %1 failed, AMDevicePair returned %2")
.arg(deviceId).arg(error));
return false;
}
}
if (am_res_t error2 = lib()->deviceValidatePairing(device)) {
addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceValidatePairing returned %2")
.arg(deviceId).arg(error2));
return false;
}
if (am_res_t error3 = lib()->deviceStartSession(device)) {
addError(QString::fromLatin1("connectDevice %1 failed, AMDeviceStartSession returned %2")
.arg(deviceId).arg(error3));
return false;
}
return true;
}
bool CommandSession::disconnectDevice()
{
if (am_res_t error = lib()->deviceStopSession(device)) {
addError(QString::fromLatin1("stopSession %1 failed, AMDeviceStopSession returned %2")
.arg(deviceId).arg(error));
return false;
}
if (am_res_t error = lib()->deviceDisconnect(device)) {
addError(QString::fromLatin1("disconnectDevice %1 failed, AMDeviceDisconnect returned %2")
.arg(deviceId).arg(error));
return false;
}
return true;
}
bool CommandSession::startService(const QString &serviceName, ServiceSocket &fd)
{
bool failure = false;
fd = 0;
if (!connectDevice())
return false;
CFStringRef cfsService = CFStringCreateWithQString(serviceName);
if (am_res_t error = lib()->deviceStartService(device, cfsService, &fd, 0)) {
addError(QString::fromLatin1("startService on device %1 failed, AMDeviceStartService returned %2")
.arg(deviceId).arg(error));
failure = true;
fd = -1;
}
CFRelease(cfsService);
disconnectDevice();
return !failure;
}
void CommandSession::stopService(ServiceSocket fd)
{
// would be close socket on windows
close(fd);
}
void CommandSession::startDeviceLookup(int timeout)
{
IosDeviceManagerPrivate::instance()->deviceWithId(deviceId, timeout,
&deviceAvailableSessionCallback, this);
}
void CommandSession::addError(const QString &msg)
{
if (debugAll)
qDebug() << "CommandSession ERROR: " << msg;
IosDeviceManagerPrivate::instance()->addError(commandName() + msg);
}
bool CommandSession::writeAll(ServiceSocket fd, const char *cmd, ssize_t len)
{
if (len == -1)
len = strlen(cmd);
if (debugGdbServer) {
QByteArray cmdBA(cmd,len);
qDebug() << "writeAll(" << fd << "," << QString::fromLocal8Bit(cmdBA.constData(), cmdBA.size())
<< " (" << cmdBA.toHex() << "))";
}
ssize_t i = 0;
int maxRetry = 10;
while (i < len) {
ssize_t nWritten = write(fd, cmd, len - i);
if (nWritten < 1) {
--maxRetry;
if (nWritten == -1 && errno != 0 && errno != EINTR) {
char buf[256];
if (!strerror_r(errno, buf, sizeof(buf))) {
buf[sizeof(buf)-1] = 0;
addError(QString::fromLocal8Bit(buf));
} else {
addError(QLatin1String("Unknown writeAll error"));
}
return false;
}
if (maxRetry <= 0) {
addError(QLatin1String("Hit maximum retries in writeAll"));
return false;
}
} else {
maxRetry = 10;
}
i += nWritten;
}
return true;
}
bool CommandSession::sendGdbCommand(ServiceSocket fd, const char *cmd, ssize_t len)
{
if (len == -1)
len = strlen(cmd);
unsigned char checkSum = 0;
for (int i = 0; i < len; ++i)
checkSum += static_cast<unsigned char>(cmd[i]);
bool failure = !writeAll(fd, "$", 1);
if (!failure)
failure = !writeAll(fd, cmd, len);
char buf[3];
buf[0] = '#';
const char *hex = "0123456789abcdef";
buf[1] = hex[(checkSum >> 4) & 0xF];
buf[2] = hex[checkSum & 0xF];
if (!failure)
failure = !writeAll(fd, buf, 3);
return !failure;
}
bool CommandSession::checkRead(ssize_t nRead, int &maxRetry)
{
if (nRead < 1 || nRead > 4) {
--maxRetry;
if ((nRead < 0 || nRead > 4) && errno != 0 && errno != EINTR) {
char buf[256];
if (!strerror_r(errno, buf, sizeof(buf))) {
buf[sizeof(buf)-1] = 0;
addError(QString::fromLocal8Bit(buf));
} else {
addError(QLatin1String("Unknown writeAll error"));
}
return false;
}
if (maxRetry <= 0) {
addError(QLatin1String("Hit maximum retries in readGdbReply"));
return false;
}
} else {
maxRetry = 10;
}
return true;
}
int CommandSession::handleChar(QByteArray &res, char c, int status)
{
switch (status) {
case 0:
if (c == '$')
return 1;
if (c != '+' && c != '-') {
if (unexpectedChars < 10) {
addError(QString::fromLatin1("unexpected char %1 in readGdbReply looking for $")
.arg(QChar::fromLatin1(c)));
++unexpectedChars;
} else if (unexpectedChars == 10) {
addError(QString::fromLatin1("hit maximum number of unexpected chars, ignoring them in readGdbReply looking for $"));
++unexpectedChars;
}
}
return 0;
case 1:
if (c != '#') {
res.append(c);
return 1;
}
return 2;
case 2:
case 3:
if ((c < '0' || c > '9') && (c < 'a' || c > 'f') && (c < 'A' | c > 'F')) {
if (unexpectedChars < 15) {
addError(QString::fromLatin1("unexpected char %1 in readGdbReply as checksum")
.arg(QChar::fromLatin1(c)));
++unexpectedChars;
} else if (unexpectedChars == 15) {
addError(QString::fromLatin1("hit maximum number of unexpected chars in checksum, ignoring them in readGdbReply"));
++unexpectedChars;
}
}
return status + 1;
case 4:
addError(QString::fromLatin1("gone past end in readGdbReply"));
return 5;
case 5:
return 5;
default:
addError(QString::fromLatin1("unexpected status readGdbReply"));
return 5;
}
}
QByteArray CommandSession::readGdbReply(ServiceSocket fd)
{
// read with minimal buffering because we might want to give the socket away...
QByteArray res;
char buf[5];
int maxRetry = 10;
int status = 0;
int toRead = 4;
while (status < 4 && toRead > 0) {
ssize_t nRead = read(fd, buf, toRead);
if (!checkRead(nRead, maxRetry))
return QByteArray();
if (debugGdbServer) {
buf[nRead] = 0;
qDebug() << "gdbReply read " << buf;
}
for (ssize_t i = 0; i< nRead; ++i)
status = handleChar(res, buf[i], status);
toRead = 4 - status;
}
if (status != 4) {
addError(QString::fromLatin1("unexpected parser status %1 in readGdbReply").arg(status));
return QByteArray();
}
return res;
}
void CommandSession::reportProgress(CFDictionaryRef dict)
{
QString status;
CFStringRef cfStatus;
if (CFDictionaryGetValueIfPresent(dict, CFSTR("Status"), reinterpret_cast<const void **>(&cfStatus))) {
if (cfStatus && CFGetTypeID(cfStatus) == CFStringGetTypeID())
status = CFStringRef2QString(cfStatus);
}
quint32 progress = 0;
CFNumberRef cfProgress;
if (CFDictionaryGetValueIfPresent(dict, CFSTR("PercentComplete"), reinterpret_cast<const void **>(&cfProgress))) {
if (cfProgress && CFGetTypeID(cfProgress) == CFNumberGetTypeID())
CFNumberGetValue(cfProgress,kCFNumberSInt32Type, reinterpret_cast<const void **>(&progress));
}
reportProgress2(progressBase + progress, status);
}
void CommandSession::reportProgress2(int progress, const QString &status)
{
Q_UNUSED(progress);
Q_UNUSED(status);
}
QString CommandSession::commandName()
{
return QString::fromLatin1("CommandSession(%1)").arg(deviceId);
}
bool CommandSession::expectGdbReply(ServiceSocket gdbFd, QByteArray expected)
{
QByteArray repl = readGdbReply(gdbFd);
if (repl != expected) {
addError(QString::fromLatin1("Unexpected reply: %1 (%2) vs %3 (%3)")
.arg(QString::fromLocal8Bit(repl.constData(), repl.size()))
.arg(QString::fromLatin1(repl.toHex().constData(), 2*repl.size()))
.arg(QString::fromLocal8Bit(expected.constData(), expected.size()))
.arg(QString::fromLocal8Bit(expected.toHex().constData(), 2*expected.size())));
return false;
}
return true;
}
bool CommandSession::expectGdbOkReply(ServiceSocket gdbFd)
{
return expectGdbReply(gdbFd, QByteArray("OK"));
}
MobileDeviceLib *CommandSession::lib()
{
return IosDeviceManagerPrivate::instance()->lib();
}
AppOpSession::AppOpSession(const QString &deviceId, const QString &bundlePath,
const QStringList &extraArgs, IosDeviceManager::AppOp appOp):
CommandSession(deviceId), bundlePath(bundlePath), extraArgs(extraArgs), appOp(appOp)
{ }
QString AppOpSession::commandName()
{
return QString::fromLatin1("TransferAppSession(%1, %2)").arg(deviceId, bundlePath);
}
bool AppOpSession::installApp()
{
ServiceSocket fd;
bool failure = (device == 0);
if (!failure) {
failure = !startService(QLatin1String("com.apple.afc"), fd);
if (!failure) {
CFStringRef cfsBundlePath = CFStringCreateWithQString(bundlePath);
if (am_res_t error = lib()->deviceTransferApplication(fd, cfsBundlePath, 0,
&appTransferSessionCallback,
static_cast<CommandSession *>(this))) {
addError(QString::fromLatin1("TransferAppSession(%1,%2) failed, AMDeviceTransferApplication returned %3")
.arg(bundlePath, deviceId).arg(error));
failure = true;
}
progressBase += 100;
CFRelease(cfsBundlePath);
}
stopService(fd);
}
if (debugAll)
qDebug() << "AMDeviceTransferApplication finished request with " << failure;
if (!failure) {
failure = !startService(QLatin1String("com.apple.mobile.installation_proxy"), fd);
if (!failure) {
CFStringRef cfsBundlePath = CFStringCreateWithQString(bundlePath);
CFStringRef key[1] = {CFSTR("PackageType")};
CFStringRef value[1] = {CFSTR("Developer")};
CFDictionaryRef options = CFDictionaryCreate(0, reinterpret_cast<const void**>(&key[0]),
reinterpret_cast<const void**>(&value[0]), 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (am_res_t error = lib()->deviceInstallApplication(fd, cfsBundlePath, options,
&appInstallSessionCallback,
static_cast<CommandSession *>(this))) {
addError(QString::fromLatin1("InstallAppSession(%1,%2) failed, AMDeviceInstallApplication returned %3")
.arg(bundlePath, deviceId).arg(error));
failure = true;
}
progressBase += 100;
CFRelease(options);
CFRelease(cfsBundlePath);
}
stopService(fd);
}
if (debugAll)
qDebug() << "AMDeviceInstallApplication finished request with " << failure;
IosDeviceManagerPrivate::instance()->didTransferApp(bundlePath, deviceId,
(failure ? IosDeviceManager::Failure : IosDeviceManager::Success));
return !failure;
}
void AppOpSession::deviceCallbackReturned()
{
switch (appOp) {
case IosDeviceManager::None:
break;
case IosDeviceManager::Install:
installApp();
break;
case IosDeviceManager::InstallAndRun:
if (installApp())
runApp();
break;
case IosDeviceManager::Run:
runApp();
break;
}
}
bool AppOpSession::runApp()
{
bool failure = (device == 0);
ServiceSocket gdbFd = -1;
if (!failure && !startService(QLatin1String("com.apple.debugserver"), gdbFd))
gdbFd = -1;
if (gdbFd > 0) {
// gdbServer protocol, see http://sourceware.org/gdb/onlinedocs/gdb/Remote-Protocol.html#Remote-Protocol
// and the lldb handling of that (with apple specific stuff)
// http://llvm.org/viewvc/llvm-project/lldb/trunk/tools/debugserver/source/RNBRemote.cpp
failure = !sendGdbCommand(gdbFd, "QStartNoAckMode"); // avoid and send required aknowledgements?
if (!failure) failure = !expectGdbOkReply(gdbFd);
if (!failure) failure = !sendGdbCommand(gdbFd, "QEnvironmentHexEncoded:"); // send the environment with a series of these commands...
if (!failure) failure = !sendGdbCommand(gdbFd, "QSetDisableASLR:1"); // avoid address randomization to debug
if (!failure) failure = !expectGdbOkReply(gdbFd);
QString exe = appPathOnDevice();
QStringList args = extraArgs;
QByteArray runCommand("A");
args.insert(0, exe);
if (debugAll)
qDebug() << " trying to start " << args;
for (int iarg = 0; iarg < args.size(); ++iarg) {
if (iarg)
runCommand.append(',');
QByteArray arg = args.at(iarg).toLocal8Bit().toHex();
runCommand.append(QString::number(arg.size()).toLocal8Bit());
runCommand.append(',');
runCommand.append(QString::number(iarg).toLocal8Bit());
runCommand.append(',');
runCommand.append(arg);
}
if (!failure) failure = !sendGdbCommand(gdbFd, runCommand.constData(), runCommand.size());
if (!failure) failure = !expectGdbOkReply(gdbFd);
if (!failure) failure = !sendGdbCommand(gdbFd, "qLaunchSuccess");
if (!failure) failure = !expectGdbOkReply(gdbFd);
}
IosDeviceManagerPrivate::instance()->didStartApp(
bundlePath, deviceId,
(failure ? IosDeviceManager::Failure : IosDeviceManager::Success), gdbFd);
return !failure;
}
void AppOpSession::reportProgress2(int progress, const QString &status)
{
IosDeviceManagerPrivate::instance()->isTransferringApp(
bundlePath, deviceId, progress, status);
}
QString AppOpSession::appId()
{
QSettings settings(bundlePath + QLatin1String("/Info.plist"), QSettings::NativeFormat);
QString res = settings.value(QString::fromLatin1("CFBundleIdentifier")).toString();
if (debugAll)
qDebug() << "appId:" << res;
return res;
}
QString AppOpSession::appPathOnDevice()
{
QString res;
if (!connectDevice())
return QString();
CFDictionaryRef apps;
if (int err = lib()->deviceLookupApplications(device, 0, &apps)) {
addError(QString::fromLatin1("app lookup failed, AMDeviceLookupApplications returned %1")
.arg(err));
}
if (debugAll)
CFShow(apps);
if (apps && CFGetTypeID(apps) == CFDictionaryGetTypeID()) {
CFStringRef cfAppId = CFStringCreateWithQString(appId());
CFDictionaryRef cfAppInfo = 0;
if (CFDictionaryGetValueIfPresent(apps, cfAppId, reinterpret_cast<const void**>(&cfAppInfo))) {
if (cfAppInfo && CFGetTypeID(cfAppInfo) == CFDictionaryGetTypeID()) {
CFStringRef cfPath, cfBundleExe;
QString path, bundleExe;
if (CFDictionaryGetValueIfPresent(cfAppInfo, CFSTR("Path"), reinterpret_cast<const void **>(&cfPath)))
path = CFStringRef2QString(cfPath);
if (CFDictionaryGetValueIfPresent(cfAppInfo, CFSTR("CFBundleExecutable"), reinterpret_cast<const void **>(&cfBundleExe)))
bundleExe = CFStringRef2QString(cfBundleExe);
if (!path.isEmpty() && ! bundleExe.isEmpty())
res = path + QLatin1Char('/') + bundleExe;
}
}
}
if (apps)
CFRelease(apps);
disconnectDevice();
if (res.isEmpty())
addError(QString::fromLatin1("failed to get app Path on device for bundle %1 with appId: %2")
.arg(bundlePath, appId()));
return res;
}
am_res_t AppOpSession::appTransferCallback(CFDictionaryRef dict)
{
if (debugAll)
qDebug() << "TransferAppSession::appTransferCallback";
reportProgress(dict);
return 0;
}
am_res_t AppOpSession::appInstallCallback(CFDictionaryRef dict)
{
if (debugAll)
qDebug() << "TransferAppSession::appInstallCallback";
reportProgress(dict);
return 0;
}
DevInfoSession::DevInfoSession(const QString &deviceId) : CommandSession(deviceId)
{ }
QString DevInfoSession::commandName()
{
return QString::fromLatin1("DevInfoSession(%1, %2)").arg(deviceId);
}
void DevInfoSession::deviceCallbackReturned()
{
if (debugAll)
qDebug() << "device available";
QMap<QString,QString> res;
QString deviceNameKey = QLatin1String("deviceName");
QString developerStatusKey = QLatin1String("developerStatus");
QString deviceConnectedKey = QLatin1String("deviceConnected");
bool failure = !device;
if (!failure) {
failure = !connectDevice();
if (!failure) {
res[deviceConnectedKey] = QLatin1String("YES");
CFPropertyListRef cfDeviceName = lib()->deviceCopyValue(device, 0,
CFSTR("DeviceName"));
// CFShow(cfDeviceName);
if (cfDeviceName) {
if (CFGetTypeID(cfDeviceName) == CFStringGetTypeID())
res[deviceNameKey] = CFStringRef2QString(reinterpret_cast<CFStringRef>(cfDeviceName));
CFRelease(cfDeviceName);
}
if (!res.contains(deviceNameKey))
res[deviceNameKey] = QString();
}
if (!failure) {
CFPropertyListRef cfDevStatus = lib()->deviceCopyValue(device,
CFSTR("com.apple.xcode.developerdomain"),
CFSTR("DeveloperStatus"));
// CFShow(cfDevStatus);
if (cfDevStatus) {
if (CFGetTypeID(cfDevStatus) == CFStringGetTypeID())
res[developerStatusKey] = CFStringRef2QString(reinterpret_cast<CFStringRef>(cfDevStatus));
CFRelease(cfDevStatus);
}
if (!res.contains(developerStatusKey))
res[developerStatusKey] = QLatin1String("*off*");
}
disconnectDevice();
}
if (!res.contains(deviceConnectedKey))
res[deviceConnectedKey] = QLatin1String("NO");
if (!res.contains(deviceNameKey))
res[deviceNameKey] = QLatin1String("*unknown*");
if (!res.contains(developerStatusKey))
res[developerStatusKey] = QLatin1String("*unknown*");
if (debugAll)
qDebug() << "deviceInfo:" << res << ", failure:" << failure;
emit Ios::IosDeviceManager::instance()->deviceInfo(deviceId, res);
/* should we also check the provision profiles??? i.e.
int fd;
startService(QLatin1String("com.apple.misagent"), &fd);
... MISAgentCopyProvisioningProfiles, AMAuthInstallProvisioningGetProvisionedInfo & co still to add */
}
// ------- MobileDeviceLib implementation --------
MobileDeviceLib::MobileDeviceLib() { }
bool MobileDeviceLib::load()
{
#ifdef MOBILE_DEV_DIRECT_LINK
m_AMDSetLogLevel = &AMDSetLogLevel;
m_AMDeviceNotificationSubscribe = &AMDeviceNotificationSubscribe;
//m_AMDeviceNotificationUnsubscribe = &AMDeviceNotificationUnsubscribe;
m_AMDeviceCopyValue = &AMDeviceCopyValue;
m_AMDeviceGetConnectionID = &AMDeviceGetConnectionID;
m_AMDeviceCopyDeviceIdentifier = &AMDeviceCopyDeviceIdentifier;
m_AMDeviceConnect = &AMDeviceConnect;
//m_AMDevicePair = &AMDevicePair;
m_AMDeviceIsPaired = &AMDeviceIsPaired;
m_AMDeviceValidatePairing = &AMDeviceValidatePairing;
m_AMDeviceStartSession = &AMDeviceStartSession;
m_AMDeviceStopSession = &AMDeviceStopSession;
m_AMDeviceDisconnect = &AMDeviceDisconnect;
m_AMDeviceMountImage = &AMDeviceMountImage;
m_AMDeviceStartService = &AMDeviceStartService;
m_AMDeviceTransferApplication = &AMDeviceTransferApplication;
m_AMDeviceInstallApplication = &AMDeviceInstallApplication;
//m_AMDeviceUninstallApplication = &AMDeviceUninstallApplication;
//m_AMDeviceLookupApplications = &AMDeviceLookupApplications;
#else
QLibrary *libAppleFSCompression = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/AppleFSCompression.framework/AppleFSCompression"));
if (!libAppleFSCompression->load())
addError("MobileDevice dependency AppleFSCompression failed to load");
deps << libAppleFSCompression;
QLibrary *libBom = new QLibrary(QLatin1String("/System/Library/PrivateFrameworks/Bom.framework/Bom"));
if (!libBom->load())
addError("MobileDevice dependency Bom failed to load");
deps << libBom;
lib.setFileName(QLatin1String("/System/Library/PrivateFrameworks/MobileDevice.framework/MobileDevice"));
if (!lib.load())
return false;
m_AMDSetLogLevel = reinterpret_cast<AMDSetLogLevelPtr>(lib.resolve("AMDSetLogLevel"));
if (m_AMDSetLogLevel == 0)
addError("MobileDeviceLib does not define AMDSetLogLevel");
m_AMDeviceNotificationSubscribe = reinterpret_cast<AMDeviceNotificationSubscribePtr>(lib.resolve("AMDeviceNotificationSubscribe"));
if (m_AMDeviceNotificationSubscribe == 0)
addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe");
m_AMDeviceNotificationUnsubscribe = reinterpret_cast<AMDeviceNotificationUnsubscribePtr>(lib.resolve("AMDeviceNotificationUnsubscribe"));
if (m_AMDeviceNotificationUnsubscribe == 0)
addError("MobileDeviceLib does not define AMDeviceNotificationUnsubscribe");
m_AMDeviceCopyValue = reinterpret_cast<AMDeviceCopyValuePtr>(lib.resolve("AMDeviceCopyValue"));
if (m_AMDSetLogLevel == 0)
addError("MobileDeviceLib does not define AMDSetLogLevel");
m_AMDeviceGetConnectionID = reinterpret_cast<AMDeviceGetConnectionIDPtr>(lib.resolve("AMDeviceGetConnectionID"));
if (m_AMDeviceGetConnectionID == 0)
addError("MobileDeviceLib does not define AMDeviceGetConnectionID");
m_AMDeviceCopyDeviceIdentifier = reinterpret_cast<AMDeviceCopyDeviceIdentifierPtr>(lib.resolve("AMDeviceCopyDeviceIdentifier"));
if (m_AMDeviceCopyDeviceIdentifier == 0)
addError("MobileDeviceLib does not define AMDeviceCopyDeviceIdentifier");
m_AMDeviceConnect = reinterpret_cast<AMDeviceConnectPtr>(lib.resolve("AMDeviceConnect"));
if (m_AMDeviceConnect == 0)
addError("MobileDeviceLib does not define AMDeviceConnect");
m_AMDevicePair = reinterpret_cast<AMDevicePairPtr>(lib.resolve("AMDevicePair"));
if (m_AMDevicePair == 0)
addError("MobileDeviceLib does not define AMDevicePair");
m_AMDeviceIsPaired = reinterpret_cast<AMDeviceIsPairedPtr>(lib.resolve("AMDeviceIsPaired"));
if (m_AMDeviceIsPaired == 0)
addError("MobileDeviceLib does not define AMDeviceIsPaired");
m_AMDeviceValidatePairing = reinterpret_cast<AMDeviceValidatePairingPtr>(lib.resolve("AMDeviceValidatePairing"));
if (m_AMDeviceValidatePairing == 0)
addError("MobileDeviceLib does not define AMDeviceValidatePairing");
m_AMDeviceStartSession = reinterpret_cast<AMDeviceStartSessionPtr>(lib.resolve("AMDeviceStartSession"));
if (m_AMDeviceStartSession == 0)
addError("MobileDeviceLib does not define AMDeviceStartSession");
m_AMDeviceStopSession = reinterpret_cast<AMDeviceStopSessionPtr>(lib.resolve("AMDeviceStopSession"));
if (m_AMDeviceStopSession == 0)
addError("MobileDeviceLib does not define AMDeviceStopSession");
m_AMDeviceDisconnect = reinterpret_cast<AMDeviceDisconnectPtr>(lib.resolve("AMDeviceDisconnect"));
if (m_AMDeviceDisconnect == 0)
addError("MobileDeviceLib does not define AMDeviceDisconnect");
m_AMDeviceMountImage = reinterpret_cast<AMDeviceMountImagePtr>(lib.resolve("AMDeviceMountImage"));
if (m_AMDeviceMountImage == 0)
addError("MobileDeviceLib does not define AMDeviceMountImage");
m_AMDeviceStartService = reinterpret_cast<AMDeviceStartServicePtr>(lib.resolve("AMDeviceStartService"));
if (m_AMDeviceStartService == 0)
addError("MobileDeviceLib does not define AMDeviceStartService");
m_AMDeviceTransferApplication = reinterpret_cast<AMDeviceTransferApplicationPtr>(lib.resolve("AMDeviceTransferApplication"));
if (m_AMDeviceTransferApplication == 0)
addError("MobileDeviceLib does not define AMDeviceTransferApplication");
m_AMDeviceInstallApplication = reinterpret_cast<AMDeviceInstallApplicationPtr>(lib.resolve("AMDeviceInstallApplication"));
if (m_AMDeviceInstallApplication == 0)
addError("MobileDeviceLib does not define AMDeviceInstallApplication");
m_AMDeviceUninstallApplication = reinterpret_cast<AMDeviceUninstallApplicationPtr>(lib.resolve("AMDeviceUninstallApplication"));
if (m_AMDeviceUninstallApplication == 0)
addError("MobileDeviceLib does not define AMDeviceUninstallApplication");
m_AMDeviceLookupApplications = reinterpret_cast<AMDeviceLookupApplicationsPtr>(lib.resolve("AMDeviceLookupApplications"));
if (m_AMDeviceLookupApplications == 0)
addError("MobileDeviceLib does not define AMDeviceLookupApplications");
#endif
return true;
}
bool MobileDeviceLib::isLoaded()
{
return lib.isLoaded();
}
QStringList MobileDeviceLib::errors()
{
return m_errors;
}
void MobileDeviceLib::setLogLevel(int i)
{
if (m_AMDSetLogLevel)
m_AMDSetLogLevel(i);
}
am_res_t MobileDeviceLib::deviceNotificationSubscribe(AMDeviceNotificationCallback callback,
unsigned int v1, unsigned int v2, void *callbackArgs,
const AMDeviceNotification **handle)
{
if (m_AMDeviceNotificationSubscribe)
return m_AMDeviceNotificationSubscribe(callback,v1,v2,callbackArgs,handle);
return -1;
}
am_res_t MobileDeviceLib::deviceNotificationUnsubscribe(void *handle)
{
if (m_AMDeviceNotificationUnsubscribe)
return m_AMDeviceNotificationUnsubscribe(handle);
return -1;
}
CFPropertyListRef MobileDeviceLib::deviceCopyValue(AMDeviceRef device,CFStringRef group,CFStringRef key)
{
if (m_AMDeviceCopyValue)
return m_AMDeviceCopyValue(device, group, key);
return 0;
}
unsigned int MobileDeviceLib::deviceGetConnectionID(AMDeviceRef device)
{
if (m_AMDeviceGetConnectionID)
return m_AMDeviceGetConnectionID(device);
return -1;
}
CFStringRef MobileDeviceLib::deviceCopyDeviceIdentifier(AMDeviceRef device)
{
if (m_AMDeviceCopyDeviceIdentifier)
return m_AMDeviceCopyDeviceIdentifier(device);
return 0;
}
am_res_t MobileDeviceLib::deviceConnect(AMDeviceRef device)
{
if (m_AMDeviceConnect)
return m_AMDeviceConnect(device);
return -1;
}
am_res_t MobileDeviceLib::devicePair(AMDeviceRef device)
{
if (m_AMDevicePair)
return m_AMDevicePair(device);
return -1;
}
am_res_t MobileDeviceLib::deviceIsPaired(AMDeviceRef device)
{
if (m_AMDeviceIsPaired)
return m_AMDeviceIsPaired(device);
return -1;
}
am_res_t MobileDeviceLib::deviceValidatePairing(AMDeviceRef device)
{
if (m_AMDeviceValidatePairing)
return m_AMDeviceValidatePairing(device);
return -1;
}
am_res_t MobileDeviceLib::deviceStartSession(AMDeviceRef device)
{
if (m_AMDeviceStartSession)
return m_AMDeviceStartSession(device);
return -1;
}
am_res_t MobileDeviceLib::deviceStopSession(AMDeviceRef device)
{
if (m_AMDeviceStopSession)
return m_AMDeviceStopSession(device);
return -1;
}
am_res_t MobileDeviceLib::deviceDisconnect(AMDeviceRef device)
{
if (m_AMDeviceDisconnect)
return m_AMDeviceDisconnect(device);
return -1;
}
am_res_t MobileDeviceLib::deviceMountImage(AMDeviceRef device, CFStringRef imagePath,
CFDictionaryRef options,
AMDeviceMountImageCallback callback,
void * callbackExtraArgs)
{
if (m_AMDeviceMountImage)
return m_AMDeviceMountImage(device, imagePath, options, callback, callbackExtraArgs);
return -1;
}
am_res_t MobileDeviceLib::deviceStartService(AMDeviceRef device, CFStringRef serviceName,
ServiceSocket *fdRef, void *extra)
{
if (m_AMDeviceStartService)
return m_AMDeviceStartService(device, serviceName, fdRef, extra);
return -1;
}
am_res_t MobileDeviceLib::deviceTransferApplication(int serviceFd, CFStringRef appPath,
CFDictionaryRef options,
AMDeviceInstallApplicationCallback callback,
void *callbackExtraArgs)
{
if (m_AMDeviceTransferApplication)
return m_AMDeviceTransferApplication(serviceFd, appPath, options, callback, callbackExtraArgs);
return -1;
}
am_res_t MobileDeviceLib::deviceInstallApplication(int serviceFd, CFStringRef appPath,
CFDictionaryRef options,
AMDeviceInstallApplicationCallback callback,
void *callbackExtraArgs)
{
if (m_AMDeviceInstallApplication)
return m_AMDeviceInstallApplication(serviceFd, appPath, options, callback, callbackExtraArgs);
return -1;
}
am_res_t MobileDeviceLib::deviceUninstallApplication(int serviceFd, CFStringRef bundleId,
CFDictionaryRef options,
AMDeviceInstallApplicationCallback callback,
void* callbackExtraArgs)
{
if (m_AMDeviceUninstallApplication)
return m_AMDeviceUninstallApplication(serviceFd, bundleId, options, callback, callbackExtraArgs);
return -1;
}
am_res_t MobileDeviceLib::deviceLookupApplications(AMDeviceRef device, unsigned int i,
CFDictionaryRef *res)
{
if (m_AMDeviceLookupApplications)
return m_AMDeviceLookupApplications(device, i, res);
return -1;
}
void MobileDeviceLib::addError(const QString &msg)
{
qDebug() << "MobileDeviceLib ERROR:" << msg;
m_errors << QLatin1String("MobileDeviceLib ERROR:") << msg;
}
void MobileDeviceLib::addError(const char *msg)
{
addError(QLatin1String(msg));
}
void CommandSession::internalDeviceAvailableCallback(QString deviceId, AMDeviceRef device)
{
if (deviceId != this->deviceId && !this->deviceId.isEmpty())
addError(QString::fromLatin1("deviceId mismatch in deviceAvailableCallback, %1 vs %2")
.arg(deviceId, this->deviceId));
this->deviceId = deviceId;
if (this->device)
addError(QString::fromLatin1("session had non null device in deviceAvailableCallback"));
this->device = device;
deviceCallbackReturned();
}
} // namespace Internal
// ------- IosManager implementation (just forwarding) --------
IosDeviceManager *IosDeviceManager::instance()
{
static IosDeviceManager instanceVal;
return &instanceVal;
}
IosDeviceManager::IosDeviceManager(QObject *parent) :
QObject(parent)
{
d = new Internal::IosDeviceManagerPrivate(this);
}
bool IosDeviceManager::watchDevices() {
return d->watchDevices();
}
void IosDeviceManager::requestAppOp(const QString &bundlePath, const QStringList &extraArgs,
AppOp appOp, const QString &deviceId, int timeout) {
d->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout);
}
void IosDeviceManager::requestDeviceInfo(const QString &deviceId, int timeout)
{
d->requestDeviceInfo(deviceId, timeout);
}
int IosDeviceManager::processGdbServer(int fd)
{
return d->processGdbServer(fd);
}
QStringList IosDeviceManager::errors() {
return d->errors();
}
void IosDeviceManager::checkPendingLookups()
{
d->checkPendingLookups();
}
} // namespace Ios