Fix exiting macos devicelister

This commit is contained in:
Jonas Kvinge
2019-09-15 01:12:05 +02:00
parent 9972124aa1
commit 83e10aac27
3 changed files with 103 additions and 28 deletions

View File

@@ -43,7 +43,7 @@ class DeviceLister : public QObject {
// Tries to start the thread and initialise the engine. This object will be moved to the new thread.
void Start();
void ExitAsync();
virtual void ExitAsync();
// If two listers know about the same device, then the metadata will get taken from the one with the highest priority.
virtual int priority() const { return 100; }
@@ -70,14 +70,12 @@ class DeviceLister : public QObject {
// Do whatever needs to be done to safely remove the device.
virtual void UnmountDeviceAsync(const QString &id);
private slots:
void Exit();
public slots:
virtual void UpdateDeviceFreeSpace(const QString &id) = 0;
virtual void ShutDown() {}
virtual void MountDevice(const QString &id, const int ret);
virtual void UnmountDevice(const QString &id) {}
virtual void Exit();
signals:
void DeviceAdded(const QString &id);

View File

@@ -1,3 +1,23 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* Strawberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Strawberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Strawberry. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef MACDEVICELISTER_H
#define MACDEVICELISTER_H
@@ -20,22 +40,23 @@
class MacOsDeviceLister : public DeviceLister {
Q_OBJECT
public:
MacOsDeviceLister();
~MacOsDeviceLister();
virtual QStringList DeviceUniqueIDs();
virtual QVariantList DeviceIcons(const QString &id);
virtual QString DeviceManufacturer(const QString &id);
virtual QString DeviceModel(const QString &id);
virtual quint64 DeviceCapacity(const QString &id);
virtual quint64 DeviceFreeSpace(const QString &id);
virtual QVariantMap DeviceHardwareInfo(const QString &id);
virtual bool AskForScan(const QString &serial) const;
virtual QString MakeFriendlyName(const QString &id);
virtual QList<QUrl> MakeDeviceUrls(const QString &id);
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);
bool AskForScan(const QString &serial) const;
QString MakeFriendlyName(const QString &id);
QList<QUrl> MakeDeviceUrls(const QString &id);
virtual void UpdateDeviceFreeSpace(const QString &id);
void UpdateDeviceFreeSpace(const QString &id);
struct MTPDevice {
MTPDevice() : capacity(0), free_space(0) {}
@@ -52,12 +73,14 @@ class MacOsDeviceLister : public DeviceLister {
quint64 free_space;
};
void ExitAsync();
public slots:
virtual void UnmountDevice(const QString &id);
virtual void ShutDown();
void UnmountDevice(const QString &id);
void ShutDown();
private:
virtual bool Init();
bool Init();
static void DiskAddedCallback(DADiskRef disk, void* context);
static void DiskRemovedCallback(DADiskRef disk, void* context);

View File

@@ -109,6 +109,7 @@ MacOsDeviceLister::MacOsDeviceLister() {}
MacOsDeviceLister::~MacOsDeviceLister() { CFRelease(loop_session_); }
bool MacOsDeviceLister::Init() {
ScopedNSAutoreleasePool pool;
// Populate MTP Device list.
@@ -188,6 +189,11 @@ bool MacOsDeviceLister::Init() {
CFRunLoopRun();
return true;
}
void MacOsDeviceLister::ExitAsync() {
emit ExitFinished();
}
void MacOsDeviceLister::ShutDown() { CFRunLoopStop(run_loop_); }
@@ -233,6 +239,7 @@ QString GetUSBRegistryEntryString(io_object_t device, CFStringRef key) {
}
NSObject* GetPropertyForDevice(io_object_t device, CFStringRef key) {
CFMutableDictionaryRef properties;
kern_return_t ret = IORegistryEntryCreateCFProperties(device, &properties, kCFAllocatorDefault, 0);
@@ -255,9 +262,11 @@ NSObject* GetPropertyForDevice(io_object_t device, CFStringRef key) {
}
return nil;
}
int GetUSBDeviceClass(io_object_t device) {
ScopedCFTypeRef<CFTypeRef> interface_class(IORegistryEntrySearchCFProperty(
device,
kIOServicePlane,
@@ -270,9 +279,11 @@ int GetUSBDeviceClass(io_object_t device) {
return ret;
}
return 0;
}
QString GetIconForDevice(io_object_t device) {
scoped_nsobject<NSDictionary> media_icon((NSDictionary*)GetPropertyForDevice(device, CFSTR("IOMediaIcon")));
if (media_icon) {
NSString* bundle = (NSString*)[media_icon objectForKey:@"CFBundleIdentifier"];
@@ -280,39 +291,48 @@ QString GetIconForDevice(io_object_t device) {
scoped_nsobject<NSURL> bundle_url((NSURL*)KextManagerCreateURLForBundleIdentifier(kCFAllocatorDefault, (CFStringRef)bundle));
QString path = QString::fromUtf8([[bundle_url path] UTF8String]);
QString path = QString::fromUtf8([ [bundle_url path] UTF8String]);
path += "/Contents/Resources/";
path += QString::fromUtf8([file UTF8String]);
return path;
}
return QString();
}
QString GetSerialForDevice(io_object_t device) {
QString serial = GetUSBRegistryEntryString(device, CFSTR(kUSBSerialNumberString));
if (!serial.isEmpty()) {
return "USB/" + serial;
}
return QString();
}
QString GetSerialForMTPDevice(io_object_t device) {
scoped_nsobject<NSString> serial((NSString*) GetPropertyForDevice(device, CFSTR(kUSBSerialNumberString)));
return QString(QString("MTP/") + QString::fromUtf8([serial UTF8String]));
}
QString FindDeviceProperty(const QString& bsd_name, CFStringRef property) {
ScopedCFTypeRef<DASessionRef> session(DASessionCreate(kCFAllocatorDefault));
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(kCFAllocatorDefault, session.get(), bsd_name.toLatin1().constData()));
ScopedIOObject device(DADiskCopyIOMedia(disk.get()));
QString ret = GetUSBRegistryEntryString(device.get(), property);
return ret;
}
}
} // namespace
quint64 MacOsDeviceLister::GetFreeSpace(const QUrl& url) {
QMutexLocker l(&libmtp_mutex_);
MtpConnection connection(url);
if (!connection.is_valid()) {
@@ -326,9 +346,11 @@ quint64 MacOsDeviceLister::GetFreeSpace(const QUrl& url) {
storage = storage->next;
}
return free_bytes;
}
quint64 MacOsDeviceLister::GetCapacity(const QUrl& url) {
QMutexLocker l(&libmtp_mutex_);
MtpConnection connection(url);
if (!connection.is_valid()) {
@@ -342,9 +364,11 @@ quint64 MacOsDeviceLister::GetCapacity(const QUrl& url) {
storage = storage->next;
}
return capacity_bytes;
}
void MacOsDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
MacOsDeviceLister* me = reinterpret_cast<MacOsDeviceLister*>(context);
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
@@ -360,8 +384,7 @@ void MacOsDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
}
#endif
NSURL* volume_path =
[[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy];
NSURL* volume_path = [ [properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy];
if (volume_path) {
ScopedIOObject device(DADiskCopyIOMedia(disk));
@@ -375,7 +398,7 @@ void MacOsDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
if (ret == KERN_SUCCESS) {
scoped_nsobject<NSDictionary> dict((NSDictionary*)cf_properties); // Takes ownership.
if ([[dict objectForKey:@"Removable"] intValue] == 1) {
if ([ [dict objectForKey:@"Removable"] intValue] == 1) {
QString serial = GetSerialForDevice(device.get());
if (!serial.isEmpty()) {
me->current_devices_[serial] = QString(DADiskGetBSDName(disk));
@@ -385,9 +408,11 @@ void MacOsDeviceLister::DiskAddedCallback(DADiskRef disk, void* context) {
}
}
}
}
void MacOsDeviceLister::DiskRemovedCallback(DADiskRef disk, void* context) {
MacOsDeviceLister* me = reinterpret_cast<MacOsDeviceLister*>(context);
// We cannot access the USB tree when the disk is removed but we still get
// the BSD disk name.
@@ -405,6 +430,7 @@ void MacOsDeviceLister::DiskRemovedCallback(DADiskRef disk, void* context) {
break;
}
}
}
bool DeviceRequest(IOUSBDeviceInterface** dev,
@@ -416,6 +442,7 @@ bool DeviceRequest(IOUSBDeviceInterface** dev,
quint16 index,
quint16 length,
QByteArray* data) {
IOUSBDevRequest req;
req.bmRequestType = USBmakebmRequestType(direction, type, recipient);
req.bRequest = request_code;
@@ -430,9 +457,11 @@ bool DeviceRequest(IOUSBDeviceInterface** dev,
}
data->resize(req.wLenDone);
return true;
}
int GetBusNumber(io_object_t o) {
io_iterator_t it;
kern_return_t err = IORegistryEntryGetParentIterator(o, kIOServicePlane, &it);
if (err != KERN_SUCCESS) {
@@ -447,9 +476,11 @@ int GetBusNumber(io_object_t o) {
}
return -1;
}
void MacOsDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
MacOsDeviceLister* me = reinterpret_cast<MacOsDeviceLister*>(refcon);
io_object_t object;
@@ -584,9 +615,11 @@ void MacOsDeviceLister::USBDeviceAddedCallback(void* refcon, io_iterator_t it) {
}
}
}
}
void MacOsDeviceLister::USBDeviceRemovedCallback(void* refcon, io_iterator_t it) {
MacOsDeviceLister* me = reinterpret_cast<MacOsDeviceLister*>(refcon);
io_object_t object;
while ((object = IOIteratorNext(it))) {
@@ -610,17 +643,21 @@ void MacOsDeviceLister::USBDeviceRemovedCallback(void* refcon, io_iterator_t it)
me->RemovedMTPDevice(serial);
}
}
}
void MacOsDeviceLister::RemovedMTPDevice(const QString& serial) {
int count = mtp_devices_.remove(serial);
if (count) {
qLog(Debug) << "MTP device removed:" << serial;
emit DeviceRemoved(serial);
}
}
void MacOsDeviceLister::FoundMTPDevice(const MTPDevice& device, const QString& serial) {
qLog(Debug) << "New MTP device detected!" << device.bus << device.address;
mtp_devices_[serial] = device;
QList<QUrl> urls = MakeDeviceUrls(serial);
@@ -628,6 +665,7 @@ void MacOsDeviceLister::FoundMTPDevice(const MTPDevice& device, const QString& s
d->capacity = GetCapacity(urls[0]);
d->free_space = GetFreeSpace(urls[0]);
emit DeviceAdded(serial);
}
bool IsMTPSerial(const QString& serial) { return serial.startsWith("MTP"); }
@@ -637,6 +675,7 @@ bool MacOsDeviceLister::IsCDDevice(const QString& serial) const {
}
QString MacOsDeviceLister::MakeFriendlyName(const QString& serial) {
if (IsMTPSerial(serial)) {
const MTPDevice& device = mtp_devices_[serial];
if (device.vendor.isEmpty()) {
@@ -667,9 +706,11 @@ QString MacOsDeviceLister::MakeFriendlyName(const QString& serial) {
return product;
}
return vendor + " " + product;
}
QList<QUrl> MacOsDeviceLister::MakeDeviceUrls(const QString& serial) {
if (IsMTPSerial(serial)) {
const MTPDevice& device = mtp_devices_[serial];
QString str;
@@ -694,12 +735,13 @@ QList<QUrl> MacOsDeviceLister::MakeDeviceUrls(const QString& serial) {
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(kCFAllocatorDefault, session.get(), bsd_name.toLatin1().constData()));
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk.get()));
scoped_nsobject<NSURL> volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSURL> volume_path([ [properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
QString path = QString::fromUtf8([[volume_path path] UTF8String]);
QString path = QString::fromUtf8([ [volume_path path] UTF8String]);
QUrl ret = MakeUrlFromLocalPath(path);
return QList<QUrl>() << ret;
}
QStringList MacOsDeviceLister::DeviceUniqueIDs() {
@@ -707,6 +749,7 @@ QStringList MacOsDeviceLister::DeviceUniqueIDs() {
}
QVariantList MacOsDeviceLister::DeviceIcons(const QString& serial) {
if (IsMTPSerial(serial)) {
return QVariantList();
}
@@ -723,9 +766,9 @@ QVariantList MacOsDeviceLister::DeviceIcons(const QString& serial) {
QString icon = GetIconForDevice(device.get());
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSURL> volume_path([ [properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
QString path = QString::fromUtf8([[volume_path path] UTF8String]);
QString path = QString::fromUtf8([ [volume_path path] UTF8String]);
QVariantList ret;
ret << GuessIconForPath(path);
@@ -734,6 +777,7 @@ QVariantList MacOsDeviceLister::DeviceIcons(const QString& serial) {
ret << icon;
}
return ret;
}
QString MacOsDeviceLister::DeviceManufacturer(const QString& serial) {
@@ -751,6 +795,7 @@ QString MacOsDeviceLister::DeviceModel(const QString& serial) {
}
quint64 MacOsDeviceLister::DeviceCapacity(const QString& serial) {
if (IsMTPSerial(serial)) {
QList<QUrl> urls = MakeDeviceUrls(serial);
return mtp_devices_[serial].capacity;
@@ -768,9 +813,11 @@ quint64 MacOsDeviceLister::DeviceCapacity(const QString& serial) {
IOObjectRelease(device);
return ret;
}
quint64 MacOsDeviceLister::DeviceFreeSpace(const QString& serial) {
if (IsMTPSerial(serial)) {
QList<QUrl> urls = MakeDeviceUrls(serial);
return mtp_devices_[serial].free_space;
@@ -780,7 +827,7 @@ quint64 MacOsDeviceLister::DeviceFreeSpace(const QString& serial) {
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(kCFAllocatorDefault, session.get(), bsd_name.toLatin1().constData()));
scoped_nsobject<NSDictionary> properties((NSDictionary*)DADiskCopyDescription(disk));
scoped_nsobject<NSURL> volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
scoped_nsobject<NSURL> volume_path([ [properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]);
NSNumber* value = nil;
NSError* error = nil;
@@ -788,6 +835,7 @@ quint64 MacOsDeviceLister::DeviceFreeSpace(const QString& serial) {
return [value unsignedLongLongValue];
}
return 0;
}
QVariantMap MacOsDeviceLister::DeviceHardwareInfo(const QString& serial){return QVariantMap();}
@@ -797,24 +845,29 @@ bool MacOsDeviceLister::AskForScan(const QString& serial) const {
}
void MacOsDeviceLister::UnmountDevice(const QString& serial) {
if (IsMTPSerial(serial)) return;
QString bsd_name = current_devices_[serial];
ScopedCFTypeRef<DADiskRef> disk(DADiskCreateFromBSDName(kCFAllocatorDefault, loop_session_, bsd_name.toLatin1().constData()));
DADiskUnmount(disk, kDADiskUnmountOptionDefault, &DiskUnmountCallback, this);
}
void MacOsDeviceLister::DiskUnmountCallback(DADiskRef disk, DADissenterRef dissenter, void* context) {
if (dissenter) {
qLog(Warning) << "Another app blocked the unmount";
}
else {
DiskRemovedCallback(disk, context);
}
}
void MacOsDeviceLister::UpdateDeviceFreeSpace(const QString& serial) {
if (IsMTPSerial(serial)) {
if (mtp_devices_.contains(serial)) {
QList<QUrl> urls = MakeDeviceUrls(serial);
@@ -823,4 +876,5 @@ void MacOsDeviceLister::UpdateDeviceFreeSpace(const QString& serial) {
}
}
emit DeviceChanged(serial);
}