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 #include #include +#include #include #include @@ -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 + +#include + +namespace Ios { + +template +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>; +using CFUrl_t = std::unique_ptr>; +using CFPropertyList_t = std::unique_ptr>; +using CFBundle_t = std::unique_ptr>; +using CFDictionary_t = std::unique_ptr>; +using CFArray_t = std::unique_ptr>; + +} // 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 #include #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -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,70 +1239,143 @@ 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(&key[0]), - reinterpret_cast(&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(&key[0]), + reinterpret_cast(&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)); + 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; } - IosDeviceManagerPrivate::instance()->didTransferApp(bundlePath, deviceId, - (success ? IosDeviceManager::Success : IosDeviceManager::Failure)); - return success; + 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(&keys[0]), + reinterpret_cast(&values[0]), + 7, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + + if (int error = mLib.deviceSecureInstallApplicationBundle(0, + device, + bundleUrl.get(), + options.get(), + &appSecureTransferSessionCallback)) + return false; + + return true; } void AppOpSession::deviceCallbackReturned() @@ -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(lib.resolve("AMDSetLogLevel")); if (m_AMDSetLogLevel == 0) addError("MobileDeviceLib does not define AMDSetLogLevel"); - m_AMDeviceNotificationSubscribe = reinterpret_cast(lib.resolve("AMDeviceNotificationSubscribe")); + + m_AMDeviceSecureInstallApplicationBundle + = reinterpret_cast( + lib.resolve("AMDeviceSecureInstallApplicationBundle")); + if (m_AMDeviceSecureInstallApplicationBundle == 0) + addError("MobileDeviceLib does not define m_AMDeviceSecureInstallApplicationBundle"); + + m_AMDeviceNotificationSubscribe = reinterpret_cast( + lib.resolve("AMDeviceNotificationSubscribe")); if (m_AMDeviceNotificationSubscribe == 0) addError("MobileDeviceLib does not define AMDeviceNotificationSubscribe"); m_AMDeviceNotificationUnsubscribe = reinterpret_cast(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;