Fixes to imobiledeviceconnection support

This commit is contained in:
Jonas Kvinge
2020-05-29 17:36:01 +02:00
parent 823f65f1ca
commit 6c77294a86
15 changed files with 91 additions and 51 deletions

View File

@@ -20,6 +20,8 @@
#include "config.h" #include "config.h"
#include <memory>
#include <QThread> #include <QThread>
#include <QFile> #include <QFile>
#include <QList> #include <QList>
@@ -28,6 +30,7 @@
#include "core/application.h" #include "core/application.h"
#include "core/utilities.h" #include "core/utilities.h"
#include "core/song.h"
#include "afcdevice.h" #include "afcdevice.h"
#include "afcfile.h" #include "afcfile.h"
#include "afctransfer.h" #include "afctransfer.h"
@@ -35,13 +38,23 @@
#include "gpodloader.h" #include "gpodloader.h"
#include "imobiledeviceconnection.h" #include "imobiledeviceconnection.h"
AfcDevice::AfcDevice(const QUrl &url, DeviceLister* lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time) AfcDevice::AfcDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time) : GPodDevice(url, lister, unique_id, manager, app, database_id, first_time), transfer_(nullptr) {}
: GPodDevice(url, lister, unique_id, manager, app, database_id, first_time), transfer_(nullptr)
{
}
AfcDevice::~AfcDevice() { AfcDevice::~AfcDevice() {
Utilities::RemoveRecursive(local_path_); Utilities::RemoveRecursive(local_path_);
if (loader_) {
loader_->deleteLater();
loader_ = nullptr;
}
if (loader_thread_) {
loader_thread_->exit();
loader_thread_->deleteLater();
loader_thread_ = nullptr;
}
} }
bool AfcDevice::Init() { bool AfcDevice::Init() {
@@ -51,11 +64,16 @@ bool AfcDevice::Init() {
InitBackendDirectory(local_path_, first_time_, false); InitBackendDirectory(local_path_, first_time_, false);
model_->Init(); model_->Init();
if (!loader_thread_) loader_thread_ = new QThread();
if (url_.isEmpty() || url_.path().isEmpty()) return false;
transfer_ = new AfcTransfer(url_.host(), local_path_, app_->task_manager(), shared_from_this()); transfer_ = new AfcTransfer(url_.host(), local_path_, app_->task_manager(), shared_from_this());
transfer_->moveToThread(loader_thread_); transfer_->moveToThread(loader_thread_);
connect(transfer_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int))); connect(transfer_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(transfer_, SIGNAL(CopyFinished(bool)), SLOT(CopyFinished(bool))); connect(transfer_, SIGNAL(CopyFinished(bool)), SLOT(CopyFinished(bool)));
connect(loader_thread_, SIGNAL(started()), transfer_, SLOT(CopyFromDevice())); connect(loader_thread_, SIGNAL(started()), transfer_, SLOT(CopyFromDevice()));
loader_thread_->start(); loader_thread_->start();
@@ -63,7 +81,7 @@ bool AfcDevice::Init() {
} }
void AfcDevice::CopyFinished(bool success) { void AfcDevice::CopyFinished(const bool success) {
transfer_->deleteLater(); transfer_->deleteLater();
transfer_ = nullptr; transfer_ = nullptr;
@@ -76,12 +94,12 @@ void AfcDevice::CopyFinished(bool success) {
// Now load the songs from the local database // Now load the songs from the local database
loader_ = new GPodLoader(local_path_, app_->task_manager(), backend_, shared_from_this()); loader_ = new GPodLoader(local_path_, app_->task_manager(), backend_, shared_from_this());
loader_->set_music_path_prefix("afc://" + url_.host()); loader_->set_music_path_prefix("afc://" + url_.host());
//loader_->set_song_type(Song::Type_Stream); loader_->set_song_type(Song::FileType_Stream);
loader_->moveToThread(loader_thread_); loader_->moveToThread(loader_thread_);
connect(loader_, SIGNAL(Error(QString)), SIGNAL(Error(QString))); connect(loader_, SIGNAL(Error(QString)), SLOT(LoaderError(QString)));
connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int))); connect(loader_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*)), SLOT(LoadFinished(Itdb_iTunesDB*))); connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*, bool)), SLOT(LoadFinished(Itdb_iTunesDB*, bool)));
QMetaObject::invokeMethod(loader_, "LoadDatabase"); QMetaObject::invokeMethod(loader_, "LoadDatabase");
} }

View File

@@ -23,6 +23,8 @@
#include "config.h" #include "config.h"
#include <memory>
#include <gpod/itdb.h> #include <gpod/itdb.h>
#include <QObject> #include <QObject>
@@ -41,7 +43,7 @@ class AfcDevice : public GPodDevice {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE AfcDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time); Q_INVOKABLE AfcDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time);
~AfcDevice(); ~AfcDevice();
bool Init(); bool Init();
@@ -50,20 +52,20 @@ public:
bool StartCopy(QList<Song::FileType> *supported_types); bool StartCopy(QList<Song::FileType> *supported_types);
bool CopyToStorage(const CopyJob &job); bool CopyToStorage(const CopyJob &job);
void FinishCopy(bool success); void FinishCopy(const bool success);
bool DeleteFromStorage(const DeleteJob &job); bool DeleteFromStorage(const DeleteJob &job);
protected: protected:
void FinaliseDatabase(); void FinaliseDatabase();
private slots: private slots:
void CopyFinished(bool success); void CopyFinished(bool success);
private: private:
void RemoveRecursive(const QString &path); void RemoveRecursive(const QString &path);
private: private:
AfcTransfer *transfer_; AfcTransfer *transfer_;
std::shared_ptr<iMobileDeviceConnection> connection_; std::shared_ptr<iMobileDeviceConnection> connection_;

View File

@@ -45,9 +45,6 @@ AfcTransfer::AfcTransfer(const QString &uuid, const QString &local_destination,
} }
AfcTransfer::~AfcTransfer() {
}
void AfcTransfer::CopyFromDevice() { void AfcTransfer::CopyFromDevice() {
int task_id = 0; int task_id = 0;
@@ -57,6 +54,7 @@ void AfcTransfer::CopyFromDevice() {
} }
// Connect to the device // Connect to the device
iMobileDeviceConnection c(uuid_); iMobileDeviceConnection c(uuid_);
// Copy directories. If one fails we stop. // Copy directories. If one fails we stop.

View File

@@ -40,7 +40,6 @@ class AfcTransfer : public QObject {
public: public:
explicit AfcTransfer(const QString &uuid, const QString &local_destination, TaskManager *task_manager, std::shared_ptr<ConnectedDevice> device); explicit AfcTransfer(const QString &uuid, const QString &local_destination, TaskManager *task_manager, std::shared_ptr<ConnectedDevice> device);
~AfcTransfer();
bool CopyToDevice(iMobileDeviceConnection *connection); bool CopyToDevice(iMobileDeviceConnection *connection);

View File

@@ -78,7 +78,7 @@ ConnectedDevice::~ConnectedDevice() {
backend_->deleteLater(); backend_->deleteLater();
} }
void ConnectedDevice::InitBackendDirectory(const QString &mount_point, bool first_time, bool rewrite_path) { void ConnectedDevice::InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path) {
if (first_time || backend_->GetAllDirectories().isEmpty()) { if (first_time || backend_->GetAllDirectories().isEmpty()) {
backend_->AddDirectory(mount_point); backend_->AddDirectory(mount_point);

View File

@@ -79,7 +79,7 @@ class ConnectedDevice : public QObject, public virtual MusicStorage, public std:
void CloseFinished(const QString& id); void CloseFinished(const QString& id);
protected: protected:
void InitBackendDirectory(const QString &mount_point, bool first_time, bool rewrite_path = true); void InitBackendDirectory(const QString &mount_point, const bool first_time, const bool rewrite_path = true);
protected: protected:
Application *app_; Application *app_;

View File

@@ -283,6 +283,10 @@ void GioLister::VolumeAdded(GVolume *volume) {
DeviceInfo info; DeviceInfo info;
info.ReadVolumeInfo(volume); info.ReadVolumeInfo(volume);
if (info.volume_root_uri.startsWith("afc://") || info.volume_root_uri.startsWith("gphoto2://")) {
// Handled by iLister.
return;
}
#ifdef HAVE_AUDIOCD #ifdef HAVE_AUDIOCD
if (info.volume_root_uri.startsWith("cdda")) if (info.volume_root_uri.startsWith("cdda"))
// Audio CD devices are already handled by CDDA lister // Audio CD devices are already handled by CDDA lister
@@ -322,6 +326,10 @@ void GioLister::MountAdded(GMount *mount) {
DeviceInfo info; DeviceInfo info;
info.ReadVolumeInfo(g_mount_get_volume(mount)); info.ReadVolumeInfo(g_mount_get_volume(mount));
if (info.volume_root_uri.startsWith("afc://") || info.volume_root_uri.startsWith("gphoto2://")) {
// Handled by iLister.
return;
}
#ifdef HAVE_AUDIOCD #ifdef HAVE_AUDIOCD
if (info.volume_root_uri.startsWith("cdda")) if (info.volume_root_uri.startsWith("cdda"))
// Audio CD devices are already handled by CDDA lister // Audio CD devices are already handled by CDDA lister
@@ -566,7 +574,7 @@ void GioLister::UpdateDeviceFreeSpace(const QString &id) {
bool GioLister::DeviceNeedsMount(const QString &id) { bool GioLister::DeviceNeedsMount(const QString &id) {
QMutexLocker l(&mutex_); QMutexLocker l(&mutex_);
return devices_.contains(id) && !devices_[id].mount_ptr && !devices_[id].volume_root_uri.startsWith("mtp://"); return devices_.contains(id) && !devices_[id].mount_ptr && !devices_[id].volume_root_uri.startsWith("mtp://") && !devices_[id].volume_root_uri.startsWith("gphoto2://");
} }

View File

@@ -46,7 +46,7 @@
class DeviceLister; class DeviceLister;
class DeviceManager; class DeviceManager;
GPodDevice::GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time) GPodDevice::GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time)
: ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time), : ConnectedDevice(url, lister, unique_id, manager, app, database_id, first_time),
loader_(nullptr), loader_(nullptr),
loader_thread_(nullptr), loader_thread_(nullptr),
@@ -72,11 +72,15 @@ bool GPodDevice::Init() {
} }
GPodDevice::~GPodDevice() { GPodDevice::~GPodDevice() {
if (loader_) { if (loader_) {
loader_thread_->exit(); loader_thread_->exit();
loader_->deleteLater(); loader_->deleteLater();
loader_thread_->deleteLater(); loader_thread_->deleteLater();
loader_ = nullptr;
loader_thread_ = nullptr;
} }
} }
void GPodDevice::ConnectAsync() { void GPodDevice::ConnectAsync() {

View File

@@ -47,13 +47,7 @@ class GPodDevice : public ConnectedDevice, public virtual MusicStorage {
Q_OBJECT Q_OBJECT
public: public:
Q_INVOKABLE GPodDevice( Q_INVOKABLE GPodDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, const int database_id, const bool first_time);
const QUrl &url, DeviceLister *lister,
const QString &unique_id,
DeviceManager *manager,
Application *app,
int database_id,
bool first_time);
~GPodDevice(); ~GPodDevice();
bool Init(); bool Init();

View File

@@ -52,7 +52,7 @@ class GPodLoader : public QObject {
void LoadDatabase(); void LoadDatabase();
signals: signals:
void Error(const QString &message); void Error(QString message);
void TaskStarted(int task_id); void TaskStarted(int task_id);
void LoadFinished(Itdb_iTunesDB *db, bool success); void LoadFinished(Itdb_iTunesDB *db, bool success);

View File

@@ -36,15 +36,17 @@ iLister::iLister() {}
iLister::~iLister() {} iLister::~iLister() {}
bool iLister::Init() { bool iLister::Init() {
idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this)); idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this));
return true; return true;
} }
void iLister::EventCallback(const idevice_event_t *event, void *context) { void iLister::EventCallback(const idevice_event_t *event, void *context) {
iLister *me = reinterpret_cast<iLister*>(context); iLister *me = reinterpret_cast<iLister*>(context);
const char *uuid = event->udid; QString uuid = QString::fromUtf8(event->udid);
switch (event->event) { switch (event->event) {
case IDEVICE_DEVICE_ADD: case IDEVICE_DEVICE_ADD:
@@ -60,7 +62,7 @@ void iLister::EventCallback(const idevice_event_t *event, void *context) {
} }
void iLister::DeviceAddedCallback(const char *uuid) { void iLister::DeviceAddedCallback(const QString uuid) {
DeviceInfo info = ReadDeviceInfo(uuid); DeviceInfo info = ReadDeviceInfo(uuid);
if (!info.valid) return; if (!info.valid) return;
@@ -82,9 +84,10 @@ void iLister::DeviceAddedCallback(const char *uuid) {
} }
void iLister::DeviceRemovedCallback(const char *uuid) { void iLister::DeviceRemovedCallback(const QString uuid) {
QString id = UniqueId(uuid); QString id = UniqueId(uuid);
{ {
QMutexLocker l(&mutex_); QMutexLocker l(&mutex_);
if (!devices_.contains(id)) if (!devices_.contains(id))
@@ -97,8 +100,8 @@ void iLister::DeviceRemovedCallback(const char *uuid) {
} }
QString iLister::UniqueId(const char *uuid) { QString iLister::UniqueId(const QString uuid) {
return "ithing/" + QString::fromUtf8(uuid); return "ithing/" + uuid;
} }
QStringList iLister::DeviceUniqueIDs() { QStringList iLister::DeviceUniqueIDs() {
@@ -191,12 +194,13 @@ QList<QUrl> iLister::MakeDeviceUrls(const QString &id) {
} }
iLister::DeviceInfo iLister::ReadDeviceInfo(const char *uuid) { iLister::DeviceInfo iLister::ReadDeviceInfo(const QString uuid) {
DeviceInfo ret; DeviceInfo ret;
iMobileDeviceConnection conn(uuid); iMobileDeviceConnection conn(uuid);
if (!conn.is_valid()) return ret; if (!conn.is_valid()) return ret;
ret.valid = conn.is_valid(); ret.valid = conn.is_valid();
ret.uuid = uuid; ret.uuid = uuid;
ret.product_type = conn.GetProperty("ProductType").toString(); ret.product_type = conn.GetProperty("ProductType").toString();

View File

@@ -37,6 +37,7 @@
class iLister : public DeviceLister { class iLister : public DeviceLister {
Q_OBJECT Q_OBJECT
public: public:
explicit iLister(); explicit iLister();
~iLister(); ~iLister();
@@ -58,7 +59,7 @@ class iLister : public DeviceLister {
private: private:
struct DeviceInfo { struct DeviceInfo {
DeviceInfo() : valid(false), free_bytes(0), total_bytes(0) {} DeviceInfo() : valid(false), free_bytes(0), total_bytes(0), password_protected(false) {}
bool valid; bool valid;
@@ -83,16 +84,16 @@ class iLister : public DeviceLister {
static void EventCallback(const idevice_event_t *event, void *context); static void EventCallback(const idevice_event_t *event, void *context);
void DeviceAddedCallback(const char *uuid); void DeviceAddedCallback(const QString uuid);
void DeviceRemovedCallback(const char *uuid); void DeviceRemovedCallback(const QString uuid);
DeviceInfo ReadDeviceInfo(const char *uuid); DeviceInfo ReadDeviceInfo(const QString uuid);
static QString UniqueId(const char *uuid); static QString UniqueId(const QString uuid);
template <typename T> template <typename T>
T LockAndGetDeviceInfo(const QString &id, T DeviceInfo::*field); T LockAndGetDeviceInfo(const QString &id, T DeviceInfo::*field);
private: private:
QMutex mutex_; QMutex mutex_;
QMap<QString, DeviceInfo> devices_; QMap<QString, DeviceInfo> devices_;
}; };

View File

@@ -22,6 +22,11 @@
#include <plist/plist.h> #include <plist/plist.h>
#include <libimobiledevice/afc.h>
#include <libimobiledevice/libimobiledevice.h>
#include <libimobiledevice/mobileactivation.h>
#include <libimobiledevice/lockdown.h>
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QByteArray> #include <QByteArray>
@@ -29,15 +34,18 @@
#include <QStringList> #include <QStringList>
#include <QUrl> #include <QUrl>
#include <QtDebug> #include <QtDebug>
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
# include <QRandomGenerator>
#endif
#include "core/logging.h" #include "core/logging.h"
#include "imobiledeviceconnection.h" #include "imobiledeviceconnection.h"
iMobileDeviceConnection::iMobileDeviceConnection(const QString &uuid) : device_(nullptr), afc_(nullptr) { iMobileDeviceConnection::iMobileDeviceConnection(const QString uuid) : device_(nullptr), afc_(nullptr) {
idevice_error_t err = idevice_new(&device_, uuid.toUtf8().constData()); idevice_error_t err = idevice_new(&device_, uuid.toUtf8().constData());
if (err != IDEVICE_E_SUCCESS) { if (err != IDEVICE_E_SUCCESS) {
qLog(Warning) << "idevice error:" << err; qLog(Warning) << "idevice_new error:" << err;
return; return;
} }
@@ -47,27 +55,25 @@ iMobileDeviceConnection::iMobileDeviceConnection(const QString &uuid) : device_(
lockdownd_client_t lockdown; lockdownd_client_t lockdown;
lockdownd_error_t lockdown_err = lockdownd_client_new_with_handshake(device_, &lockdown, label); lockdownd_error_t lockdown_err = lockdownd_client_new_with_handshake(device_, &lockdown, label);
if (lockdown_err != LOCKDOWN_E_SUCCESS) { if (lockdown_err != LOCKDOWN_E_SUCCESS) {
qLog(Warning) << "lockdown error:" << lockdown_err; qLog(Warning) << "lockdownd_client_new_with_handshake error:" << lockdown_err;
return; return;
} }
lockdownd_service_descriptor_t lockdown_service_desc; lockdownd_service_descriptor_t lockdown_service_desc;
lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &lockdown_service_desc); lockdown_err = lockdownd_start_service(lockdown, "com.apple.afc", &lockdown_service_desc);
if (lockdown_err != LOCKDOWN_E_SUCCESS) { if (lockdown_err != LOCKDOWN_E_SUCCESS) {
qLog(Warning) << "lockdown error:" << lockdown_err; qLog(Warning) << "lockdownd_start_service error:" << lockdown_err;
lockdownd_client_free(lockdown); lockdownd_client_free(lockdown);
return; return;
} }
afc_error_t afc_err = afc_client_new(device_, lockdown_service_desc, &afc_); afc_error_t afc_err = afc_client_new(device_, lockdown_service_desc, &afc_);
if (afc_err != AFC_E_SUCCESS) { if (afc_err != AFC_E_SUCCESS) {
qLog(Warning) << "afc error:" << afc_err; qLog(Warning) << "afc_client_new error:" << afc_err;
lockdownd_service_descriptor_free(lockdown_service_desc);
lockdownd_client_free(lockdown); lockdownd_client_free(lockdown);
return; return;
} }
lockdownd_service_descriptor_free(lockdown_service_desc);
lockdownd_client_free(lockdown); lockdownd_client_free(lockdown);
} }
@@ -187,6 +193,7 @@ QString iMobileDeviceConnection::GetFileInfo(const QString &path, const QString
char **infolist = nullptr; char **infolist = nullptr;
afc_error_t err = afc_get_file_info(afc_, path.toUtf8().constData(), &infolist); afc_error_t err = afc_get_file_info(afc_, path.toUtf8().constData(), &infolist);
if (err != AFC_E_SUCCESS || !infolist) { if (err != AFC_E_SUCCESS || !infolist) {
qLog(Debug) << "afc_get_file_info error:" << path << err;
return ret; return ret;
} }
@@ -232,7 +239,12 @@ QString iMobileDeviceConnection::GetUnusedFilename(Itdb_iTunesDB *itdb, const So
} }
// Pick one at random // Pick one at random
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
const int dir_num = QRandomGenerator::global()->bounded(total_musicdirs);
#else
const int dir_num = qrand() % total_musicdirs; const int dir_num = qrand() % total_musicdirs;
#endif
QString dir = QString::asprintf("/iTunes_Control/Music/F%02d", dir_num); QString dir = QString::asprintf("/iTunes_Control/Music/F%02d", dir_num);
if (!Exists(dir)) { if (!Exists(dir)) {

View File

@@ -38,7 +38,7 @@
class iMobileDeviceConnection { class iMobileDeviceConnection {
public: public:
explicit iMobileDeviceConnection(const QString &uuid); explicit iMobileDeviceConnection(const QString uuid);
~iMobileDeviceConnection(); ~iMobileDeviceConnection();
afc_client_t afc() { return afc_; } afc_client_t afc() { return afc_; }

View File

@@ -51,7 +51,7 @@ class MtpDevice : public ConnectedDevice {
Q_INVOKABLE MtpDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time); Q_INVOKABLE MtpDevice(const QUrl &url, DeviceLister *lister, const QString &unique_id, DeviceManager *manager, Application *app, int database_id, bool first_time);
~MtpDevice(); ~MtpDevice();
static QStringList url_schemes() { return QStringList() << "mtp" << "gphoto2"; } static QStringList url_schemes() { return QStringList() << "mtp"; }
bool Init(); bool Init();
void ConnectAsync(); void ConnectAsync();