From 83e10aac270c36fe365e2f1ef267719587d15b47 Mon Sep 17 00:00:00 2001 From: Jonas Kvinge Date: Sun, 15 Sep 2019 01:12:05 +0200 Subject: [PATCH] Fix exiting macos devicelister --- src/device/devicelister.h | 6 +-- src/device/macosdevicelister.h | 51 ++++++++++++++++------- src/device/macosdevicelister.mm | 74 ++++++++++++++++++++++++++++----- 3 files changed, 103 insertions(+), 28 deletions(-) diff --git a/src/device/devicelister.h b/src/device/devicelister.h index b4fa4cb6a..602e9697a 100644 --- a/src/device/devicelister.h +++ b/src/device/devicelister.h @@ -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); diff --git a/src/device/macosdevicelister.h b/src/device/macosdevicelister.h index 38a359ddf..87ac236b5 100644 --- a/src/device/macosdevicelister.h +++ b/src/device/macosdevicelister.h @@ -1,3 +1,23 @@ +/* + * Strawberry Music Player + * This file was part of Clementine. + * Copyright 2010, David Sansome + * + * 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 . + * + */ + #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 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 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); diff --git a/src/device/macosdevicelister.mm b/src/device/macosdevicelister.mm index 9939ece6a..7e7611399 100644 --- a/src/device/macosdevicelister.mm +++ b/src/device/macosdevicelister.mm @@ -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 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 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 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 serial((NSString*) GetPropertyForDevice(device, CFSTR(kUSBSerialNumberString))); return QString(QString("MTP/") + QString::fromUtf8([serial UTF8String])); + } QString FindDeviceProperty(const QString& bsd_name, CFStringRef property) { + ScopedCFTypeRef session(DASessionCreate(kCFAllocatorDefault)); ScopedCFTypeRef 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(context); scoped_nsobject 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 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(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(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(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 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 MacOsDeviceLister::MakeDeviceUrls(const QString& serial) { + if (IsMTPSerial(serial)) { const MTPDevice& device = mtp_devices_[serial]; QString str; @@ -694,12 +735,13 @@ QList MacOsDeviceLister::MakeDeviceUrls(const QString& serial) { ScopedCFTypeRef disk(DADiskCreateFromBSDName(kCFAllocatorDefault, session.get(), bsd_name.toLatin1().constData())); scoped_nsobject properties((NSDictionary*)DADiskCopyDescription(disk.get())); - scoped_nsobject volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]); + scoped_nsobject 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() << 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 properties((NSDictionary*)DADiskCopyDescription(disk)); - scoped_nsobject volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]); + scoped_nsobject 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 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 urls = MakeDeviceUrls(serial); return mtp_devices_[serial].free_space; @@ -780,7 +827,7 @@ quint64 MacOsDeviceLister::DeviceFreeSpace(const QString& serial) { ScopedCFTypeRef disk(DADiskCreateFromBSDName(kCFAllocatorDefault, session.get(), bsd_name.toLatin1().constData())); scoped_nsobject properties((NSDictionary*)DADiskCopyDescription(disk)); - scoped_nsobject volume_path([[properties objectForKey:(NSString*)kDADiskDescriptionVolumePathKey] copy]); + scoped_nsobject 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 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 urls = MakeDeviceUrls(serial); @@ -823,4 +876,5 @@ void MacOsDeviceLister::UpdateDeviceFreeSpace(const QString& serial) { } } emit DeviceChanged(serial); + }