diff options
author | Marcus Tillmanns <[email protected]> | 2022-06-15 10:55:51 +0200 |
---|---|---|
committer | Marcus Tillmanns <[email protected]> | 2022-06-17 07:09:45 +0000 |
commit | e3fd840f98195a4f47ee7c9b3a2f27230eb3b099 (patch) | |
tree | 44885e1fd5d698d2cf794d53ef2725d1fa4b15fc | |
parent | fd68b1c58ea654b4f218f1a32093b09301ea0e89 (diff) |
iostool: Improve deployment speed using delta deploy
The iostool did always deploy the whole bundle, without taking
into account whether anything has actually changed. This meant
that for big bundles anytime the user starts the application
on his device, a full deployment was done.
For a ~1GB bundle this would take around a minute on a recent
Mac and iPhone 12.
This fix uses a new function from the mobiledevice framework
called AMDeviceSecureInstallApplicationBundle.
This function takes a new parameter "ShadowPathKey" which points
to a directory where the last deploy state is captured temporarily.
Before deploying to the device, the function compares
what is to be deployed against the last deploy state and
only deploys the parts that actually changed.
QtCreator provides a temporary folder for this. Due to this,
the initial deployment still does a complete deployment as
no state is available yet. All subsequent deployments
take the captured state into account.
For backwards compatibility, the old deployment method is left intact.
Fixes: QTCREATORBUG-24371
Change-Id: I4df6aa79d41b34c326d78be7952d7eeb23774648
Reviewed-by: Eike Ziller <[email protected]>
Reviewed-by: <[email protected]>
Reviewed-by: Qt CI Bot <[email protected]>
-rw-r--r-- | src/plugins/ios/iostoolhandler.cpp | 7 | ||||
-rw-r--r-- | src/tools/iostool/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/tools/iostool/cfutils.h | 55 | ||||
-rw-r--r-- | src/tools/iostool/iosdevicemanager.cpp | 229 | ||||
-rw-r--r-- | src/tools/iostool/iosdevicemanager.h | 2 | ||||
-rw-r--r-- | src/tools/iostool/iostool.cpp | 8 | ||||
-rw-r--r-- | src/tools/iostool/iostool.h | 1 | ||||
-rw-r--r-- | src/tools/iostool/iostool.qbs | 1 | ||||
-rw-r--r-- | src/tools/iostool/mobiledevicelib.cpp | 25 | ||||
-rw-r--r-- | src/tools/iostool/mobiledevicelib.h | 9 |
10 files changed, 269 insertions, 69 deletions
diff --git a/src/plugins/ios/iostoolhandler.cpp b/src/plugins/ios/iostoolhandler.cpp index c64a2731420..a3ccf2e5866 100644 --- a/src/plugins/ios/iostoolhandler.cpp +++ b/src/plugins/ios/iostoolhandler.cpp @@ -38,6 +38,7 @@ #include <utils/qtcassert.h> #include <utils/qtcprocess.h> #include <utils/runextensions.h> +#include <utils/temporarydirectory.h> #include <QCoreApplication> #include <QDir> @@ -701,10 +702,14 @@ void IosDeviceToolHandlerPrivate::requestTransferApp(const QString &bundlePath, { m_bundlePath = bundlePath; m_deviceId = deviceId; + QString tmpDeltaPath = Utils::TemporaryDirectory::masterDirectoryFilePath().pathAppended("ios").toString(); QStringList args; args << QLatin1String("--id") << deviceId << QLatin1String("--bundle") << bundlePath << QLatin1String("--timeout") << QString::number(timeout) - << QLatin1String("--install"); + << QLatin1String("--install") + << QLatin1String("--delta-path") + << tmpDeltaPath; + start(IosToolHandler::iosDeviceToolPath(), args); } diff --git a/src/tools/iostool/CMakeLists.txt b/src/tools/iostool/CMakeLists.txt index e02bf7463d4..b7efdc1abda 100644 --- a/src/tools/iostool/CMakeLists.txt +++ b/src/tools/iostool/CMakeLists.txt @@ -18,6 +18,7 @@ add_qtc_executable(iostool main.cpp mobiledevicelib.cpp mobiledevicelib.h relayserver.cpp relayserver.h + cfutils.h ) if (TARGET iostool) diff --git a/src/tools/iostool/cfutils.h b/src/tools/iostool/cfutils.h new file mode 100644 index 00000000000..94e8452f0ce --- /dev/null +++ b/src/tools/iostool/cfutils.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2022 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QString> + +#include <CoreFoundation/CoreFoundation.h> + +namespace Ios { + +template<typename CFType> +struct CFRefDeleter +{ + using pointer = CFType; + void operator()(CFType ref) { CFRelease(ref); } +}; + +inline QString toQStringRelease(CFStringRef str) +{ + QString result = QString::fromCFString(str); + CFRelease(str); + return result; +} + +using CFString_t = std::unique_ptr<CFStringRef, CFRefDeleter<CFStringRef>>; +using CFUrl_t = std::unique_ptr<CFURLRef, CFRefDeleter<CFURLRef>>; +using CFPropertyList_t = std::unique_ptr<CFPropertyListRef, CFRefDeleter<CFPropertyListRef>>; +using CFBundle_t = std::unique_ptr<CFBundleRef, CFRefDeleter<CFBundleRef>>; +using CFDictionary_t = std::unique_ptr<CFDictionaryRef, CFRefDeleter<CFDictionaryRef>>; +using CFArray_t = std::unique_ptr<CFArrayRef, CFRefDeleter<CFArrayRef>>; + +} // namespace Ios diff --git a/src/tools/iostool/iosdevicemanager.cpp b/src/tools/iostool/iosdevicemanager.cpp index 2e6aa4ccf36..781420beeb0 100644 --- a/src/tools/iostool/iosdevicemanager.cpp +++ b/src/tools/iostool/iosdevicemanager.cpp @@ -25,19 +25,19 @@ #include "iosdevicemanager.h" +#include "cfutils.h" #include "mobiledevicelib.h" -#include <QDebug> #include <QDir> #include <QFile> #include <QHash> #include <QLibrary> +#include <QLoggingCategory> #include <QMultiHash> #include <QMutex> #include <QMutexLocker> #include <QProcess> #include <QRegularExpression> -#include <QRegularExpression> #include <QSettings> #include <QThread> #include <QTimer> @@ -54,6 +54,10 @@ static const bool debugAll = false; static const bool verbose = true; static const bool noWifi = true; +namespace { + Q_LOGGING_CATEGORY(loggingCategory, "qtc.iostool.iosdevicemanager", QtWarningMsg) +} + // ------- MobileDeviceLib interface -------- namespace { @@ -277,8 +281,12 @@ 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 requestAppOp(const QString &bundlePath, + const QStringList &extraArgs, + Ios::IosDeviceManager::AppOp appOp, + const QString &deviceId, + int timeout, + const QString &deltaPath); void requestDeviceInfo(const QString &deviceId, int timeout); QStringList errors(); void addError(QString errorMsg); @@ -321,13 +329,17 @@ public: QString bundlePath; QStringList extraArgs; Ios::IosDeviceManager::AppOp appOp; + QString deltaPath; - - AppOpSession(const QString &deviceId, const QString &bundlePath, - const QStringList &extraArgs, Ios::IosDeviceManager::AppOp appOp); + AppOpSession(const QString &deviceId, + const QString &bundlePath, + const QStringList &extraArgs, + Ios::IosDeviceManager::AppOp appOp, + const QString &deltaPath); void deviceCallbackReturned() override; bool installApp(); + bool installAppNew(); bool runApp(); int qmljsDebugPort() const override; am_res_t appTransferCallback(CFDictionaryRef dict) override; @@ -486,11 +498,13 @@ bool IosDeviceManagerPrivate::watchDevices() } void IosDeviceManagerPrivate::requestAppOp(const QString &bundlePath, - const QStringList &extraArgs, - IosDeviceManager::AppOp appOp, - const QString &deviceId, int timeout) + const QStringList &extraArgs, + IosDeviceManager::AppOp appOp, + const QString &deviceId, + int timeout, + const QString &deltaPath) { - AppOpSession *session = new AppOpSession(deviceId, bundlePath, extraArgs, appOp); + AppOpSession *session = new AppOpSession(deviceId, bundlePath, extraArgs, appOp, deltaPath); session->startDeviceLookup(timeout); } @@ -1205,10 +1219,17 @@ bool CommandSession::developerDiskImagePath(QString *path, QString *signaturePat return false; } -AppOpSession::AppOpSession(const QString &deviceId, const QString &bundlePath, - const QStringList &extraArgs, IosDeviceManager::AppOp appOp): - CommandSession(deviceId), bundlePath(bundlePath), extraArgs(extraArgs), appOp(appOp) -{ } +AppOpSession::AppOpSession(const QString &deviceId, + const QString &bundlePath, + const QStringList &extraArgs, + IosDeviceManager::AppOp appOp, + const QString &deltaPath) + : CommandSession(deviceId) + , bundlePath(bundlePath) + , extraArgs(extraArgs) + , appOp(appOp) + , deltaPath(deltaPath) +{} QString AppOpSession::commandName() { @@ -1218,72 +1239,145 @@ QString AppOpSession::commandName() bool AppOpSession::installApp() { bool success = false; - if (device != 0) { - CFURLRef bundleUrl = QUrl::fromLocalFile(bundlePath).toCFURL(); - 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 (device) { + if (!installAppNew()) { + addError(QString::fromLatin1( + "Failed to transfer and install application, trying old way ...")); - MobileDeviceLib &mLib = MobileDeviceLib::instance(); - // Transfer bundle with secure API AMDeviceTransferApplication. - if (int error = mLib.deviceSecureTransferApplicationPath(0, device, bundleUrl, options, - &appSecureTransferSessionCallback,0)) { - addError(QString::fromLatin1("TransferAppSession(%1,%2) failed, AMDeviceTransferApplication returned %3 (0x%4)") - .arg(bundlePath, deviceId).arg(mobileDeviceErrorString(error)).arg(error)); - success = false; - } else { - // App is transferred. Try installing. - if (connectDevice()) { - // Secure install app api requires device to be connected. - if (am_res_t error = mLib.deviceSecureInstallApplication(0, device, bundleUrl, options, - &appSecureTransferSessionCallback,0)) { - const QString errorString = mobileDeviceErrorString(error); - if (!errorString.isEmpty()) { - addError(errorString - + QStringLiteral(" (0x") - + QString::number(error, 16) - + QStringLiteral(")")); + const CFUrl_t bundleUrl(QUrl::fromLocalFile(bundlePath).toCFURL()); + MobileDeviceLib &mLib = MobileDeviceLib::instance(); + + CFStringRef key[1] = {CFSTR("PackageType")}; + CFStringRef value[1] = {CFSTR("Developer")}; + const CFDictionary_t options( + CFDictionaryCreate(0, + reinterpret_cast<const void **>(&key[0]), + reinterpret_cast<const void **>(&value[0]), + 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + // Transfer bundle with secure API AMDeviceTransferApplication. + if (int error + = mLib.deviceSecureTransferApplicationPath(0, + device, + bundleUrl.get(), + options.get(), + &appSecureTransferSessionCallback, + 0)) { + addError(QString::fromLatin1("TransferAppSession(%1,%2) failed, " + "AMDeviceTransferApplication returned %3 (0x%4)") + .arg(bundlePath, deviceId) + .arg(mobileDeviceErrorString(error)) + .arg(error)); + success = false; + } else { + // App is transferred. Try installing. + if (connectDevice()) { + // Secure install app api requires device to be connected. + if (am_res_t error + = mLib.deviceSecureInstallApplication(0, + device, + bundleUrl.get(), + options.get(), + &appSecureTransferSessionCallback, + 0)) { + const QString errorString = mobileDeviceErrorString(error); + if (!errorString.isEmpty()) { + addError(errorString + QStringLiteral(" (0x") + + QString::number(error, 16) + QStringLiteral(")")); + } else { + addError(QString::fromLatin1("InstallAppSession(%1,%2) failed, " + "AMDeviceInstallApplication returned 0x%3") + .arg(bundlePath, deviceId) + .arg(QString::number(error, 16))); + } + success = false; } else { - addError(QString::fromLatin1("InstallAppSession(%1,%2) failed, " - "AMDeviceInstallApplication returned 0x%3") - .arg(bundlePath, deviceId).arg(QString::number(error, 16))); + // App is installed. + success = true; } - success = false; - } else { - // App is installed. - success = true; + disconnectDevice(); } - disconnectDevice(); } + } else { + success = true; } - if (debugAll) { - qDebug() << "AMDeviceSecureTransferApplication finished request with " << (success ? "Success" : "Failure"); - } - - CFRelease(options); - CFRelease(bundleUrl); + qCDebug(loggingCategory) << "AMDeviceSecureTransferApplication finished request with" + << (success ? "Success" : "Failure"); progressBase += 100; } - if (success) { sleep(5); // after installation the device needs a bit of quiet.... } - if (debugAll) { - qDebug() << "AMDeviceSecureInstallApplication finished request with " << (success ? "Success" : "Failure"); - } + qCDebug(loggingCategory) << "AMDeviceSecureInstallApplication finished request with" + << (success ? "Success" : "Failure"); - IosDeviceManagerPrivate::instance()->didTransferApp(bundlePath, deviceId, - (success ? IosDeviceManager::Success : IosDeviceManager::Failure)); + IosDeviceManagerPrivate::instance()->didTransferApp(bundlePath, + deviceId, + (success ? IosDeviceManager::Success + : IosDeviceManager::Failure)); return success; } +bool AppOpSession::installAppNew() +{ + const CFUrl_t bundleUrl(QUrl::fromLocalFile(bundlePath).toCFURL()); + MobileDeviceLib &mLib = MobileDeviceLib::instance(); + + CFBundle_t bundle(CFBundleCreate(kCFAllocatorDefault, bundleUrl.get())); + + if (!bundle) { + addError(QString::fromLatin1("Failed to create bundle")); + return false; + } + + const CFString_t bundleId(CFBundleGetIdentifier(bundle.get())); + if (!bundleId) { + addError(QString::fromLatin1("Failed to retrieve bundle id")); + return false; + } + + CFUrl_t dpath(QUrl::fromLocalFile(deltaPath).toCFURL()); + + CFStringRef keys[] = { + CFSTR("CFBundleIdentifier"), + CFSTR("CloseOnInvalidate"), + CFSTR("InvalidateOnDetach"), + CFSTR("IsUserInitiated"), + CFSTR("PackageType"), + CFSTR("PreferWifi"), + CFSTR("ShadowParentKey"), + }; + CFStringRef values[] = {bundleId.get(), + CFSTR("1"), + CFSTR("1"), + CFSTR("1"), + CFSTR("Developer"), + CFSTR("1"), + (CFStringRef)dpath.get()}; + + const CFDictionary_t options(CFDictionaryCreate(0, + reinterpret_cast<const void **>(&keys[0]), + reinterpret_cast<const void **>(&values[0]), + 7, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + if (int error = mLib.deviceSecureInstallApplicationBundle(0, + device, + bundleUrl.get(), + options.get(), + &appSecureTransferSessionCallback)) + return false; + + return true; +} + void AppOpSession::deviceCallbackReturned() { switch (appOp) { @@ -1575,9 +1669,14 @@ 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::requestAppOp(const QString &bundlePath, + const QStringList &extraArgs, + AppOp appOp, + const QString &deviceId, + int timeout, + QString deltaPath) +{ + d->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout, deltaPath); } void IosDeviceManager::requestDeviceInfo(const QString &deviceId, int timeout) diff --git a/src/tools/iostool/iosdevicemanager.h b/src/tools/iostool/iosdevicemanager.h index 7c521c789fd..2fb3bb1c3a3 100644 --- a/src/tools/iostool/iosdevicemanager.h +++ b/src/tools/iostool/iosdevicemanager.h @@ -62,7 +62,7 @@ public: static IosDeviceManager *instance(); bool watchDevices(); void requestAppOp(const QString &bundlePath, const QStringList &extraArgs, AppOp appOp, - const QString &deviceId, int timeout = 1000); + const QString &deviceId, int timeout = 1000, QString deltaPath = QString()); void requestDeviceInfo(const QString &deviceId, int timeout = 1000); int processGdbServer(ServiceConnRef conn); void stopGdbServer(ServiceConnRef conn, int phase); diff --git a/src/tools/iostool/iostool.cpp b/src/tools/iostool/iostool.cpp index e65cae852a4..2def3afa74a 100644 --- a/src/tools/iostool/iostool.cpp +++ b/src/tools/iostool/iostool.cpp @@ -82,6 +82,12 @@ void IosTool::run(const QStringList &args) printHelp = true; } bundlePath = args.value(iarg); + } else if (arg == QLatin1String("--delta-path")) { + if (++iarg == args.size()) { + writeMsg(QStringLiteral("missing path after ") + arg); + printHelp = true; + } + m_deltasPath = args.value(iarg); } else if (arg == QLatin1String("--install")) { appOp = IosDeviceManager::AppOp(appOp | IosDeviceManager::Install); } else if (arg == QLatin1String("--run")) { @@ -163,7 +169,7 @@ void IosTool::run(const QStringList &args) break; } maxProgress = 200; - manager->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout); + manager->requestAppOp(bundlePath, extraArgs, appOp, deviceId, timeout, m_deltasPath); } if (opLeft == 0) doExit(0); diff --git a/src/tools/iostool/iostool.h b/src/tools/iostool/iostool.h index 6aa85216ca0..c71a30e6d8c 100644 --- a/src/tools/iostool/iostool.h +++ b/src/tools/iostool/iostool.h @@ -79,6 +79,7 @@ private: Ios::IosDeviceManager::AppOp appOp; QFile outFile; QString m_qmlPort; + QString m_deltasPath; QXmlStreamWriter out; GdbRelayServer *gdbServer; QmlRelayServer *qmlServer; diff --git a/src/tools/iostool/iostool.qbs b/src/tools/iostool/iostool.qbs index a39ace4dba1..bac9f43725f 100644 --- a/src/tools/iostool/iostool.qbs +++ b/src/tools/iostool/iostool.qbs @@ -11,6 +11,7 @@ QtcTool { Depends { name: "app_version_header" } files: [ + "cfutils.h" "Info.plist", "gdbrunner.cpp", "gdbrunner.h", diff --git a/src/tools/iostool/mobiledevicelib.cpp b/src/tools/iostool/mobiledevicelib.cpp index 025248262f1..ae1c70f4a86 100644 --- a/src/tools/iostool/mobiledevicelib.cpp +++ b/src/tools/iostool/mobiledevicelib.cpp @@ -89,7 +89,15 @@ bool MobileDeviceLib::load() 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")); + + m_AMDeviceSecureInstallApplicationBundle + = reinterpret_cast<AMDeviceSecureInstallApplicationBundlePtr>( + lib.resolve("AMDeviceSecureInstallApplicationBundle")); + if (m_AMDeviceSecureInstallApplicationBundle == 0) + addError("MobileDeviceLib does not define m_AMDeviceSecureInstallApplicationBundle"); + + 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")); @@ -358,6 +366,21 @@ int MobileDeviceLib::deviceSecureTransferApplicationPath(int zero, AMDeviceRef d return returnCode; } +int MobileDeviceLib::deviceSecureInstallApplicationBundle( + int zero, + AMDeviceRef device, + CFURLRef url, + CFDictionaryRef options, + AMDeviceSecureInstallApplicationCallback callback) +{ + int returnCode = -1; + + if (m_AMDeviceSecureInstallApplicationBundle) { + returnCode = m_AMDeviceSecureInstallApplicationBundle(device, url, options, callback, zero); + } + return returnCode; +} + int MobileDeviceLib::deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg) { int returnCode = -1; diff --git a/src/tools/iostool/mobiledevicelib.h b/src/tools/iostool/mobiledevicelib.h index 97735fdcb1a..68bca9d316d 100644 --- a/src/tools/iostool/mobiledevicelib.h +++ b/src/tools/iostool/mobiledevicelib.h @@ -101,6 +101,7 @@ typedef am_res_t (MDEV_API *USBMuxConnectByPortPtr)(unsigned int, int, ServiceSo // secure Api's typedef am_res_t (MDEV_API *AMDeviceSecureStartServicePtr)(AMDeviceRef, CFStringRef, unsigned int *, ServiceConnRef *); typedef int (MDEV_API *AMDeviceSecureTransferPathPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); +typedef int (MDEV_API *AMDeviceSecureInstallApplicationBundlePtr)(AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int zero); typedef int (MDEV_API *AMDeviceSecureInstallApplicationPtr)(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback, int); typedef int (MDEV_API *AMDServiceConnectionGetSocketPtr)(ServiceConnRef); @@ -158,6 +159,13 @@ public: int deviceSecureTransferApplicationPath(int, AMDeviceRef, CFURLRef, CFDictionaryRef, AMDeviceSecureInstallApplicationCallback callback, int); + + int deviceSecureInstallApplicationBundle(int zero, + AMDeviceRef device, + CFURLRef url, + CFDictionaryRef options, + AMDeviceSecureInstallApplicationCallback callback); + int deviceSecureInstallApplication(int zero, AMDeviceRef device, CFURLRef url, CFDictionaryRef options, AMDeviceSecureInstallApplicationCallback callback, int arg); @@ -191,6 +199,7 @@ private: AMDeviceMountImagePtr m_AMDeviceMountImage; AMDeviceSecureStartServicePtr m_AMDeviceSecureStartService; AMDeviceSecureTransferPathPtr m_AMDeviceSecureTransferPath; + AMDeviceSecureInstallApplicationBundlePtr m_AMDeviceSecureInstallApplicationBundle; AMDeviceSecureInstallApplicationPtr m_AMDeviceSecureInstallApplication; AMDServiceConnectionGetSocketPtr m_AMDServiceConnectionGetSocket; AMDServiceConnectionSendPtr m_AMDServiceConnectionSend; |