File indexing completed on 2024-05-12 04:59:44
0001 /* 0002 This file is part of the MTP KIOD module, part of the KDE project. 0003 0004 SPDX-FileCopyrightText: 2018 Andreas Krutzler <andreas.krutzler@gmx.net> 0005 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "kmtpd.h" 0011 0012 #include <chrono> 0013 0014 #include <QDBusConnection> 0015 #include <QDebug> 0016 0017 #include <KDirNotify> 0018 #include <KPluginFactory> 0019 #include <Solid/DeviceNotifier> 0020 #include <Solid/GenericInterface> 0021 0022 #include "daemonadaptor.h" 0023 #include "kiod_kmtpd_debug.h" 0024 #include "mtpdevice.h" 0025 0026 using namespace std::chrono_literals; 0027 0028 K_PLUGIN_CLASS_WITH_JSON(KMTPd, "kmtpd.json") 0029 0030 KMTPd::KMTPd(QObject *parent, const QList<QVariant> ¶meters) 0031 : KDEDModule(parent) 0032 { 0033 Q_UNUSED(parameters) 0034 0035 LIBMTP_Init(); 0036 0037 // search for already connected devices 0038 for (const Solid::Device &solidDevice : Solid::Device::listFromType(Solid::DeviceInterface::PortableMediaPlayer)) { 0039 checkDevice(solidDevice); 0040 } 0041 0042 auto notifier = Solid::DeviceNotifier::instance(); 0043 connect(notifier, &Solid::DeviceNotifier::deviceAdded, this, &KMTPd::deviceAdded); 0044 connect(notifier, &Solid::DeviceNotifier::deviceRemoved, this, &KMTPd::deviceRemoved); 0045 0046 new DaemonAdaptor(this); 0047 } 0048 0049 KMTPd::~KMTPd() 0050 { 0051 // Release devices 0052 for (const MTPDevice *device : qAsConst(m_devices)) { 0053 deviceRemoved(device->udi()); 0054 } 0055 } 0056 0057 QString KMTPd::version() const 0058 { 0059 return QStringLiteral(LIBMTP_VERSION_STRING); 0060 } 0061 0062 void KMTPd::checkDevice(const Solid::Device &solidDevice) 0063 { 0064 if (!deviceFromUdi(solidDevice.udi())) { 0065 qCDebug(LOG_KIOD_KMTPD) << "new device, getting raw devices"; 0066 0067 const Solid::GenericInterface *iface = solidDevice.as<Solid::GenericInterface>(); 0068 if (!iface) { 0069 qCDebug(LOG_KIOD_KMTPD) << "Solid device " << solidDevice.udi() << " has NOT a Solid::GenericInterface"; 0070 return; 0071 } 0072 0073 const QMap<QString, QVariant> &properties = iface->allProperties(); 0074 const quint32 solidBusNum = properties.value(QStringLiteral("BUSNUM")).toUInt(); 0075 const quint32 solidDevNum = properties.value(QStringLiteral("DEVNUM")).toUInt(); 0076 0077 LIBMTP_raw_device_t *rawdevices = nullptr; 0078 int numrawdevices; 0079 LIBMTP_error_number_t err; 0080 0081 err = LIBMTP_Detect_Raw_Devices(&rawdevices, &numrawdevices); 0082 switch (err) { 0083 case LIBMTP_ERROR_CONNECTING: 0084 qCWarning(LOG_KIOD_KMTPD) << "There has been an error connecting to the devices"; 0085 break; 0086 case LIBMTP_ERROR_MEMORY_ALLOCATION: 0087 qCWarning(LOG_KIOD_KMTPD) << "Encountered a Memory Allocation Error"; 0088 break; 0089 case LIBMTP_ERROR_NONE: { 0090 qCDebug(LOG_KIOD_KMTPD) << "No Error, continuing"; 0091 0092 for (int i = 0; i < numrawdevices; i++) { 0093 LIBMTP_raw_device_t *rawDevice = &rawdevices[i]; 0094 uint32_t rawBusNum = rawDevice->bus_location; 0095 uint32_t rawDevNum = rawDevice->devnum; 0096 0097 if (rawBusNum == solidBusNum && rawDevNum == solidDevNum) { 0098 qCDebug(LOG_KIOD_KMTPD) << "Found device matching the Solid description"; 0099 0100 LIBMTP_mtpdevice_t *mtpDevice = LIBMTP_Open_Raw_Device_Uncached(rawDevice); 0101 if (mtpDevice) { 0102 auto device = new MTPDevice(QStringLiteral("/modules/kmtpd/device%1").arg(m_devices.count()), mtpDevice, rawDevice, solidDevice.udi()); 0103 0104 // Always let MTPDevice know any changes to devices. It might be helpful Daemon interfaces 0105 connect(this, &KMTPd::devicesChanged, device, [device] { 0106 device->setDevicesUpdatedStatus(true); 0107 org::kde::KDirNotify::emitFilesAdded(device->url()); 0108 }); 0109 org::kde::KDirNotify::emitFilesAdded(device->url()); // notify for the current change as well 0110 0111 m_devices.append(device); 0112 Q_EMIT devicesChanged(); 0113 } else { 0114 qCWarning(LOG_KIOD_KMTPD) << "LIBMTP_Open_Raw_Device_Uncached: Could not open MTP device"; 0115 } 0116 } 0117 } 0118 } break; 0119 case LIBMTP_ERROR_GENERAL: 0120 default: 0121 qCWarning(LOG_KIOD_KMTPD) << "Unknown connection error"; 0122 break; 0123 } 0124 free(rawdevices); 0125 } 0126 } 0127 0128 MTPDevice *KMTPd::deviceFromUdi(const QString &udi) const 0129 { 0130 auto deviceIt = std::find_if(m_devices.constBegin(), m_devices.constEnd(), [udi](const MTPDevice *device) { 0131 return device->udi() == udi; 0132 }); 0133 0134 return deviceIt == m_devices.constEnd() ? nullptr : *deviceIt; 0135 } 0136 0137 QList<QDBusObjectPath> KMTPd::listDevices() const 0138 { 0139 QList<QDBusObjectPath> list; 0140 for (const auto &device : m_devices) { 0141 list.append(QDBusObjectPath(device->dbusObjectName())); 0142 } 0143 0144 return list; 0145 } 0146 0147 void KMTPd::deviceAdded(const QString &udi) 0148 { 0149 qCDebug(LOG_KIOD_KMTPD) << "New device attached with udi=" << udi << ". Checking if PortableMediaPlayer..."; 0150 0151 const Solid::Device device(udi); 0152 if (device.isDeviceInterface(Solid::DeviceInterface::PortableMediaPlayer)) { 0153 qCDebug(LOG_KIOD_KMTPD) << "SOLID: New Device with udi=" << udi; 0154 0155 org::kde::KDirNotify::emitFilesAdded(QUrl(QStringLiteral("mtp:/"))); 0156 checkDevice(device); 0157 } 0158 } 0159 0160 void KMTPd::deviceRemoved(const QString &udi) 0161 { 0162 MTPDevice *device = deviceFromUdi(udi); 0163 if (device) { 0164 qCDebug(LOG_KIOD_KMTPD) << "SOLID: Device with udi=" << udi << " removed."; 0165 0166 const QUrl url = device->url(); 0167 0168 // When allowing access on the Device side the device is briefly removed and then added again. 0169 // If we were to signal removal right away that would lead the KDirModel to leave the directory as it thinks 0170 // the dir no longer exists. To mitigate we delay the removal and if it was a real removal, rather than a 0171 // temporary remove-add dance, we'll emit it. 0172 QTimer::singleShot(5s, this, [this, udi, url] { 0173 if (!deviceFromUdi(udi)) { 0174 qCDebug(LOG_KIOD_KMTPD) << "executing scheduled removal of " << udi; 0175 org::kde::KDirNotify::emitFilesRemoved({url}); 0176 } 0177 }); 0178 0179 Q_EMIT devicesChanged(); 0180 m_devices.removeOne(device); 0181 delete device; 0182 } 0183 } 0184 0185 #include "kmtpd.moc" 0186 #include "moc_kmtpd.cpp"