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 <memory>
#include <QThread>
#include <QFile>
#include <QList>
@ -28,6 +30,7 @@
#include "core/application.h"
#include "core/utilities.h"
#include "core/song.h"
#include "afcdevice.h"
#include "afcfile.h"
#include "afctransfer.h"
@ -35,13 +38,23 @@
#include "gpodloader.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)
: GPodDevice(url, lister, unique_id, manager, app, database_id, first_time), transfer_(nullptr)
{
}
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) {}
AfcDevice::~AfcDevice() {
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() {
@ -51,11 +64,16 @@ bool AfcDevice::Init() {
InitBackendDirectory(local_path_, first_time_, false);
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_->moveToThread(loader_thread_);
connect(transfer_, SIGNAL(TaskStarted(int)), SIGNAL(TaskStarted(int)));
connect(transfer_, SIGNAL(CopyFinished(bool)), SLOT(CopyFinished(bool)));
connect(loader_thread_, SIGNAL(started()), transfer_, SLOT(CopyFromDevice()));
loader_thread_->start();
@ -63,7 +81,7 @@ bool AfcDevice::Init() {
}
void AfcDevice::CopyFinished(bool success) {
void AfcDevice::CopyFinished(const bool success) {
transfer_->deleteLater();
transfer_ = nullptr;
@ -76,12 +94,12 @@ void AfcDevice::CopyFinished(bool success) {
// Now load the songs from the local database
loader_ = new GPodLoader(local_path_, app_->task_manager(), backend_, shared_from_this());
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_);
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(LoadFinished(Itdb_iTunesDB*)), SLOT(LoadFinished(Itdb_iTunesDB*)));
connect(loader_, SIGNAL(LoadFinished(Itdb_iTunesDB*, bool)), SLOT(LoadFinished(Itdb_iTunesDB*, bool)));
QMetaObject::invokeMethod(loader_, "LoadDatabase");
}

View File

@ -23,6 +23,8 @@
#include "config.h"
#include <memory>
#include <gpod/itdb.h>
#include <QObject>
@ -41,7 +43,7 @@ class AfcDevice : public GPodDevice {
Q_OBJECT
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();
bool Init();
@ -50,20 +52,20 @@ public:
bool StartCopy(QList<Song::FileType> *supported_types);
bool CopyToStorage(const CopyJob &job);
void FinishCopy(bool success);
void FinishCopy(const bool success);
bool DeleteFromStorage(const DeleteJob &job);
protected:
protected:
void FinaliseDatabase();
private slots:
private slots:
void CopyFinished(bool success);
private:
private:
void RemoveRecursive(const QString &path);
private:
private:
AfcTransfer *transfer_;
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() {
int task_id = 0;
@ -57,6 +54,7 @@ void AfcTransfer::CopyFromDevice() {
}
// Connect to the device
iMobileDeviceConnection c(uuid_);
// Copy directories. If one fails we stop.

View File

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

View File

@ -78,7 +78,7 @@ ConnectedDevice::~ConnectedDevice() {
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()) {
backend_->AddDirectory(mount_point);

View File

@ -79,7 +79,7 @@ class ConnectedDevice : public QObject, public virtual MusicStorage, public std:
void CloseFinished(const QString& id);
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:
Application *app_;

View File

@ -283,6 +283,10 @@ void GioLister::VolumeAdded(GVolume *volume) {
DeviceInfo info;
info.ReadVolumeInfo(volume);
if (info.volume_root_uri.startsWith("afc://") || info.volume_root_uri.startsWith("gphoto2://")) {
// Handled by iLister.
return;
}
#ifdef HAVE_AUDIOCD
if (info.volume_root_uri.startsWith("cdda"))
// Audio CD devices are already handled by CDDA lister
@ -322,6 +326,10 @@ void GioLister::MountAdded(GMount *mount) {
DeviceInfo info;
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
if (info.volume_root_uri.startsWith("cdda"))
// 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) {
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 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),
loader_(nullptr),
loader_thread_(nullptr),
@ -72,11 +72,15 @@ bool GPodDevice::Init() {
}
GPodDevice::~GPodDevice() {
if (loader_) {
loader_thread_->exit();
loader_->deleteLater();
loader_thread_->deleteLater();
loader_ = nullptr;
loader_thread_ = nullptr;
}
}
void GPodDevice::ConnectAsync() {

View File

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

View File

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

View File

@ -36,15 +36,17 @@ iLister::iLister() {}
iLister::~iLister() {}
bool iLister::Init() {
idevice_event_subscribe(&EventCallback, reinterpret_cast<void*>(this));
return true;
}
void iLister::EventCallback(const idevice_event_t *event, void *context) {
iLister *me = reinterpret_cast<iLister*>(context);
const char *uuid = event->udid;
QString uuid = QString::fromUtf8(event->udid);
switch (event->event) {
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);
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);
{
QMutexLocker l(&mutex_);
if (!devices_.contains(id))
@ -97,8 +100,8 @@ void iLister::DeviceRemovedCallback(const char *uuid) {
}
QString iLister::UniqueId(const char *uuid) {
return "ithing/" + QString::fromUtf8(uuid);
QString iLister::UniqueId(const QString uuid) {
return "ithing/" + uuid;
}
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;
iMobileDeviceConnection conn(uuid);
if (!conn.is_valid()) return ret;
ret.valid = conn.is_valid();
ret.uuid = uuid;
ret.product_type = conn.GetProperty("ProductType").toString();

View File

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

View File

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

View File

@ -38,7 +38,7 @@
class iMobileDeviceConnection {
public:
explicit iMobileDeviceConnection(const QString &uuid);
explicit iMobileDeviceConnection(const QString uuid);
~iMobileDeviceConnection();
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);
~MtpDevice();
static QStringList url_schemes() { return QStringList() << "mtp" << "gphoto2"; }
static QStringList url_schemes() { return QStringList() << "mtp"; }
bool Init();
void ConnectAsync();