udisks2 support for devicemanager (refs #3264)

This commit is contained in:
Valeriy 2016-05-17 22:56:16 +03:00
parent 806e689d1d
commit d4e932db9a
12 changed files with 478 additions and 1 deletions

View File

@ -254,6 +254,10 @@ optional_component(DEVICEKIT ON "Devices: DeviceKit backend"
DEPENDS "D-Bus support" HAVE_DBUS
)
optional_component(UDISKS2 ON "Devices: UDisks2 backend"
DEPENDS "D-Bus support" HAVE_DBUS
)
optional_component(SPOTIFY_BLOB ON "Spotify support: non-GPL binary helper"
DEPENDS "protobuf" PROTOBUF_FOUND PROTOBUF_PROTOC_EXECUTABLE
DEPENDS "libspotify" SPOTIFY_FOUND

View File

@ -964,6 +964,29 @@ if(HAVE_DBUS)
dbus/udisksdevice)
endif(HAVE_DEVICEKIT)
if(HAVE_UDISKS2)
set_source_files_properties(dbus/org.freedesktop.DBus.ObjectManager.xml
PROPERTIES NO_NAMESPACE dbus/objectmanager INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Filesystem.xml
PROPERTIES NO_NAMESPACE dbus/udisks2filesystem INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Block.xml
PROPERTIES NO_NAMESPACE dbus/udisks2block INCLUDE dbus/metatypes.h)
set_source_files_properties(dbus/org.freedesktop.UDisks2.Drive.xml
PROPERTIES NO_NAMESPACE dbus/udisks2drive INCLUDE dbus/metatypes.h)
qt4_add_dbus_interface(SOURCES
dbus/org.freedesktop.DBus.ObjectManager.xml
dbus/objectmanager)
qt4_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Filesystem.xml
dbus/udisks2filesystem)
qt4_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Block.xml
dbus/udisks2block)
qt4_add_dbus_interface(SOURCES
dbus/org.freedesktop.UDisks2.Drive.xml
dbus/udisks2drive)
endif(HAVE_UDISKS2)
# Wiimotedev interface classes
if(ENABLE_WIIMOTEDEV)
qt4_add_dbus_interface(SOURCES
@ -999,6 +1022,11 @@ optional_source(HAVE_DEVICEKIT
HEADERS devices/devicekitlister.h
)
optional_source(HAVE_UDISKS2
SOURCES devices/udisks2lister.cpp
HEADERS devices/udisks2lister.h
)
# Libgpod device backend
optional_source(HAVE_LIBGPOD
INCLUDE_DIRECTORIES ${LIBGPOD_INCLUDE_DIRS}

View File

@ -41,6 +41,7 @@
#cmakedefine HAVE_SKYDRIVE
#cmakedefine HAVE_SPARKLE
#cmakedefine HAVE_SPOTIFY_DOWNLOADER
#cmakedefine HAVE_UDISKS2
#cmakedefine HAVE_VK
#cmakedefine HAVE_WIIMOTEDEV
#cmakedefine TAGLIB_HAS_OPUS

View File

@ -125,5 +125,8 @@ void RegisterMetaTypes() {
qDBusRegisterMetaType<MprisPlaylist>();
qDBusRegisterMetaType<MaybePlaylist>();
qDBusRegisterMetaType<MprisPlaylistList>();
qDBusRegisterMetaType<InterfacesAndProperties>();
qDBusRegisterMetaType<ManagedObjectList>();
#endif
}

View File

@ -20,6 +20,12 @@
#include <QMetaType>
Q_DECLARE_METATYPE(QList<QByteArray>);
Q_DECLARE_METATYPE(QList<QByteArray>)
typedef QMap<QString, QVariantMap> InterfacesAndProperties;
typedef QMap<QDBusObjectPath, InterfacesAndProperties> ManagedObjectList;
Q_DECLARE_METATYPE(InterfacesAndProperties)
Q_DECLARE_METATYPE(ManagedObjectList)
#endif // DBUS_METATYPES_H_

View File

@ -0,0 +1,19 @@
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ManagedObjectList"/>
</method>
<signal name="InterfacesAdded">
<arg name="object" type="o"/>
<arg name="interfaces" type="a{sa{sv}}"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="InterfacesAndProperties"/>
</signal>
<signal name="InterfacesRemoved">
<arg type="o" name="object_path"/>
<arg type="as" name="interfaces"/>
</signal>
</interface>
</node>

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.UDisks2.Block">
<property type="o" name="Drive" access="read"/>
<property type="s" name="IdLabel" access="read"/>
<property type="s" name="IdUUID" access="read"/>
</interface>
</node>

View File

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.UDisks2.Drive">
<method name="Eject">
<arg type="a{sv}" name="options" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
</method>
<property type="s" name="Vendor" access="read"/>
<property type="s" name="Model" access="read"/>
<property type="s" name="Revision" access="read"/>
<property type="s" name="Serial" access="read"/>
<property type="s" name="Id" access="read"/>
<property type="b" name="MediaRemovable" access="read"/>
<property type="t" name="Size" access="read"/>
</interface>
</node>

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.UDisks2.Filesystem">
<method name="Mount">
<arg type="a{sv}" name="options" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
<arg type="s" name="mount_path" direction="out"/>
</method>
<method name="Unmount">
<arg type="a{sv}" name="options" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
</method>
<property type="aay" name="MountPoints" access="read">
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QList&lt;QByteArray&gt;"/>
</property>
</interface>
</node>

View File

@ -59,6 +59,9 @@
#ifdef HAVE_LIBMTP
#include "mtpdevice.h"
#endif
#ifdef HAVE_UDISKS2
#include "udisks2lister.h"
#endif
using std::bind;
@ -191,6 +194,9 @@ DeviceManager::DeviceManager(Application* app, QObject* parent)
#ifdef HAVE_DEVICEKIT
AddLister(new DeviceKitLister);
#endif
#ifdef HAVE_UDISKS2
AddLister(new Udisks2Lister);
#endif
#ifdef HAVE_GIO
AddLister(new GioLister);
#endif
@ -228,7 +234,10 @@ void DeviceManager::LoadAllDevices() {
for (const DeviceDatabaseBackend::Device& device : devices) {
DeviceInfo info;
info.InitFromDb(device);
beginInsertRows(QModelIndex(), devices_.count(), devices_.count());
devices_ << info;
endInsertRows();
}
}

View File

@ -0,0 +1,286 @@
#include "udisks2lister.h"
#include <QDBusConnection>
#include "core/logging.h"
#include "core/utilities.h"
#include "dbus/udisks2filesystem.h"
#include "dbus/udisks2block.h"
#include "dbus/udisks2drive.h"
const QString Udisks2Lister::udisks2service_ = "org.freedesktop.UDisks2";
Udisks2Lister::Udisks2Lister() {
}
Udisks2Lister::~Udisks2Lister() {
qLog(Debug) << __PRETTY_FUNCTION__;
}
QStringList Udisks2Lister::DeviceUniqueIDs() {
QReadLocker locker(&device_data_lock_);
return device_data_.keys();
}
QVariantList Udisks2Lister::DeviceIcons(const QString &id) {
return QVariantList();
}
QString Udisks2Lister::DeviceManufacturer(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return "";
return device_data_[id].vendor;
}
QString Udisks2Lister::DeviceModel(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return "";
return device_data_[id].model;
}
quint64 Udisks2Lister::DeviceCapacity(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return 0;
return device_data_[id].capacity;
}
quint64 Udisks2Lister::DeviceFreeSpace(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return 0;
return device_data_[id].free_space;
}
QVariantMap Udisks2Lister::DeviceHardwareInfo(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return QVariantMap();
QVariantMap result;
const auto &data = device_data_[id];
result[QT_TR_NOOP("DBus path")] = data.dbus_path;
result[QT_TR_NOOP("Serial number")] = data.serial;
result[QT_TR_NOOP("Mount points")] = data.mount_paths.join(", ");
result[QT_TR_NOOP("Parition label")] = data.label;
result[QT_TR_NOOP("UUID")] = data.uuid;
return result;
}
QString Udisks2Lister::MakeFriendlyName(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return "";
return device_data_[id].friendly_name;
}
QList<QUrl> Udisks2Lister::MakeDeviceUrls(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return QList<QUrl>();
return QList<QUrl>() << QUrl::fromLocalFile(device_data_[id].mount_paths.at(0));
}
void Udisks2Lister::UnmountDevice(const QString &id) {
QReadLocker locker(&device_data_lock_);
if (!device_data_.contains(id))
return;
OrgFreedesktopUDisks2FilesystemInterface filesystem(
udisks2service_,
device_data_[id].dbus_path,
QDBusConnection::systemBus());
if (filesystem.isValid())
{
auto umountResult = filesystem.Unmount(QVariantMap());
umountResult.waitForFinished();
OrgFreedesktopUDisks2DriveInterface drive(
udisks2service_,
device_data_[id].dbus_drive_path,
QDBusConnection::systemBus());
if (drive.isValid())
{
auto ejectResult = drive.Eject(QVariantMap());
ejectResult.waitForFinished();
}
}
}
void Udisks2Lister::UpdateDeviceFreeSpace(const QString &id) {
QWriteLocker locker(&device_data_lock_);
device_data_[id].free_space = Utilities::FileSystemFreeSpace(device_data_[id].mount_paths.at(0));
emit DeviceChanged(id);
}
void Udisks2Lister::Init() {
udisks2_interface_.reset(new OrgFreedesktopDBusObjectManagerInterface(
udisks2service_,
"/org/freedesktop/UDisks2",
QDBusConnection::systemBus()));
QDBusPendingReply<ManagedObjectList> reply = udisks2_interface_->GetManagedObjects();
reply.waitForFinished();
if (!reply.isValid()) {
qLog(Warning) << "Error enumerating udisks2 devices:"
<< reply.error().name() << reply.error().message();
udisks2_interface_.reset();
return;
}
for (const QDBusObjectPath &path : reply.value().keys()) {
auto partitionData = ReadPartitionData(path, false);
if (!partitionData.dbus_path.isEmpty())
{
QWriteLocker locker(&device_data_lock_);
device_data_[partitionData.unique_id()] = partitionData;
}
}
for (const auto &id : device_data_.keys()) {
emit DeviceAdded(id);
}
connect(udisks2_interface_.get(), SIGNAL(InterfacesAdded(QDBusObjectPath, InterfacesAndProperties)),
SLOT(DBusInterfaceAdded(QDBusObjectPath, InterfacesAndProperties)));
connect(udisks2_interface_.get(), SIGNAL(InterfacesRemoved(QDBusObjectPath, QStringList)),
SLOT(DBusInterfaceRemoved(QDBusObjectPath, QStringList)));
}
void Udisks2Lister::DBusInterfaceAdded(const QDBusObjectPath &path,
const InterfacesAndProperties &interfaces) {
// FIXME handle unmount jobs too
for (auto interface = interfaces.constBegin(); interface != interfaces.constEnd(); ++interface)
{
if (interface.key() != "org.freedesktop.UDisks2.Job"
|| interface.value()["Operation"] != "filesystem-mount")
continue;
const QDBusArgument &objects = interface.value()["Objects"].value<QDBusArgument>();
QList<QDBusObjectPath> mountedParititons;
objects.beginArray();
while (!objects.atEnd()) {
QDBusObjectPath extractedPath;
objects >> extractedPath;
mountedParititons.push_back(extractedPath);
}
objects.endArray();
qLog(Debug) << "Udisks2 something mounted: " << mountedParititons.at(0).path();
{
QMutexLocker locker(&jobs_lock_);
mounting_jobs_[path.path()] = mountedParititons;
}
}
}
void Udisks2Lister::DBusInterfaceRemoved(const QDBusObjectPath &path, const QStringList &ifaces) {
if (!isPendingJob(path))
RemoveDevice(path);
}
bool Udisks2Lister::isPendingJob(const QDBusObjectPath &path)
{
// should be actually done with a succcess signal from job, I guess, but it makes it kinda complicated
QMutexLocker locker(&jobs_lock_);
if (!mounting_jobs_.contains(path.path()))
return false;
const auto &mountpaths = mounting_jobs_[path.path()];
for (const auto &partition : mountpaths) {
auto data = ReadPartitionData(partition, true);
if (!data.dbus_path.isEmpty()) {
QWriteLocker locker(&device_data_lock_);
device_data_[data.unique_id()] = data;
DeviceAdded(data.unique_id());
}
}
mounting_jobs_.remove(path.path());
return true;
}
void Udisks2Lister::RemoveDevice(const QDBusObjectPath &path)
{
QWriteLocker locker(&device_data_lock_);
QString id;
for (const auto &data : device_data_) {
if (data.dbus_path == path.path())
id = data.unique_id();
}
if (id.isEmpty())
return;
device_data_.remove(id);
DeviceRemoved(id);
}
Udisks2Lister::PartitionData Udisks2Lister::ReadPartitionData(const QDBusObjectPath &path,
bool beingMounted) {
PartitionData result;
OrgFreedesktopUDisks2FilesystemInterface filesystem(
udisks2service_,
path.path(),
QDBusConnection::systemBus());
OrgFreedesktopUDisks2BlockInterface block(
udisks2service_,
path.path(),
QDBusConnection::systemBus());
if (filesystem.isValid()
&& block.isValid()
&& (beingMounted || !filesystem.mountPoints().empty())) {
OrgFreedesktopUDisks2DriveInterface drive(
udisks2service_,
block.drive().path(),
QDBusConnection::systemBus());
if (drive.isValid()
&& drive.mediaRemovable()) {
result.dbus_path = path.path();
result.dbus_drive_path = block.drive().path();
result.serial = drive.serial();
result.vendor = drive.vendor();
result.model = drive.model();
result.label = block.idLabel();
result.uuid = block.idUUID();
result.capacity = drive.size();
if (!result.label.isEmpty())
result.friendly_name = result.label;
else
result.friendly_name = result.model + " " + result.uuid;
for (const auto &path : filesystem.mountPoints())
result.mount_paths.push_back(path);
result.free_space = Utilities::FileSystemFreeSpace(result.mount_paths.at(0));
}
}
return result;
}
QString Udisks2Lister::PartitionData::unique_id() const {
return QString("Udisks2/%1/%2/%3/%4/%5")
.arg(serial, vendor, model)
.arg(capacity)
.arg(uuid);
}

View File

@ -0,0 +1,77 @@
#pragma once
#include <memory>
#include "devicelister.h"
#include "dbus/objectmanager.h"
class Udisks2Lister : public DeviceLister {
Q_OBJECT
public:
Udisks2Lister();
~Udisks2Lister();
QStringList DeviceUniqueIDs();
QVariantList DeviceIcons(const QString &id);
QString DeviceManufacturer(const QString &id);
QString DeviceModel(const QString &id);
quint64 DeviceCapacity(const QString &id);
quint64 DeviceFreeSpace(const QString &id);
QVariantMap DeviceHardwareInfo(const QString &id);
QString MakeFriendlyName(const QString &id);
QList<QUrl> MakeDeviceUrls(const QString &id);
void UnmountDevice(const QString &id);
public slots:
void UpdateDeviceFreeSpace(const QString &id);
protected:
void Init();
private slots:
void DBusInterfaceAdded(const QDBusObjectPath &path, const InterfacesAndProperties &ifaces);
void DBusInterfaceRemoved(const QDBusObjectPath &path, const QStringList &ifaces);
private:
bool isPendingJob(const QDBusObjectPath &path);
void RemoveDevice(const QDBusObjectPath &path);
QMutex jobs_lock_;
QMap<QString, QList<QDBusObjectPath>> mounting_jobs_;
private:
class PartitionData {
public:
QString unique_id() const;
QString dbus_path;
QString friendly_name;
// Device
QString serial;
QString vendor;
QString model;
quint64 capacity = 0;
QString dbus_drive_path;
// Paritition
QString label;
QString uuid;
quint64 free_space = 0;
QStringList mount_paths;
};
PartitionData ReadPartitionData(const QDBusObjectPath &path, bool beingMounted);
QReadWriteLock device_data_lock_;
QMap<QString, PartitionData> device_data_;
private:
std::unique_ptr<OrgFreedesktopDBusObjectManagerInterface> udisks2_interface_;
static const QString udisks2service_;
};