Index: chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc |
diff --git a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc |
index f227dde49e3e4ad9c34922be154df2c1b82a80ba..98b4e568eb91adfff1a4c2efc1638c372e5a4153 100644 |
--- a/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc |
+++ b/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_mac.cc |
@@ -2,13 +2,190 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <IOKit/storage/IOStorageProtocolCharacteristics.h> |
+ |
+#include "base/strings/sys_string_conversions.h" |
#include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h" |
+#include "content/public/browser/browser_thread.h" |
namespace extensions { |
-bool RemovableStorageProvider::PopulateDeviceList( |
+void RemovableStorageProvider::GetAllDevicesImpl( |
+ const DeviceListReadyCallback& callback) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ DAWrapper* da_wrapper = new DAWrapper(); |
Robert Sesek
2014/06/02 19:56:05
Comment about ownership.
Drew Haven
2014/06/03 02:03:23
I wasn't sure how to address this exactly. We get
|
+ da_wrapper->GetDeviceList( |
+ base::Bind(DAWrapper::WrapperDeleter, da_wrapper, callback)); |
+} |
+ |
+DAWrapper::DAWrapper() : updates_pending_(0), callback_() { |
+} |
+ |
+DAWrapper::~DAWrapper() { |
+ if (session_.get()) { |
+ DASessionUnscheduleFromRunLoop( |
+ session_, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); |
+ } |
+} |
+ |
+void DAWrapper::GetDeviceList(const DeviceListReadyCallback& callback) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ |
+ device_list_ = new StorageDeviceList; |
+ callback_ = callback; |
+ |
+ session_.reset(DASessionCreate(NULL)); |
+ |
+ DARegisterDiskAppearedCallback( |
+ session_, kDADiskDescriptionMatchMediaWhole, DiskAppearedCallback, this); |
+ DARegisterDiskDisappearedCallback(session_, |
+ kDADiskDescriptionMatchMediaWhole, |
+ DiskDisappearedCallback, |
+ this); |
+ DASessionScheduleWithRunLoop( |
+ session_, CFRunLoopGetCurrent(), kCFRunLoopCommonModes); |
Robert Sesek
2014/06/02 19:56:05
Really? I thought you said this was going to execu
Drew Haven
2014/06/03 02:03:23
Discussed out of band. See general comment.
|
+} |
+ |
+void DAWrapper::ProcessDisk(base::ScopedCFTypeRef<CFDictionaryRef> dict) { |
+ updates_pending_++; |
+ content::BrowserThread::PostTask( |
Robert Sesek
2014/06/02 19:56:05
Why are you using message passing here? It's all b
Drew Haven
2014/06/03 02:03:23
The reason is because we get a bunch of DiskAppear
Robert Sesek
2014/06/04 19:10:43
But does that actually work as you describe? There
Drew Haven
2014/06/05 01:52:50
As far as I can tell this is entirely undocumented
Drew Haven
2014/06/09 23:13:02
I talked with Greg Billock who did the similar wor
Robert Sesek
2014/06/10 20:52:22
Then what is the rationale for not using storage m
|
+ content::BrowserThread::UI, |
+ FROM_HERE, |
+ base::Bind(&DAWrapper::AddDisk, AsWeakPtr(), dict)); |
+} |
+ |
+void DAWrapper::AddDisk(base::ScopedCFTypeRef<CFDictionaryRef> dict) { |
+ if (DiskIsValidTarget(dict.get())) { |
+ CFStringRef cf_bsd_name = base::mac::GetValueFromDictionary<CFStringRef>( |
+ dict, kDADiskDescriptionMediaBSDNameKey); |
+ CFStringRef cf_vendor = base::mac::GetValueFromDictionary<CFStringRef>( |
+ dict, kDADiskDescriptionDeviceVendorKey); |
+ CFStringRef cf_model = base::mac::GetValueFromDictionary<CFStringRef>( |
+ dict, kDADiskDescriptionDeviceModelKey); |
+ |
+ std::string bsd_name = base::SysCFStringRefToUTF8(cf_bsd_name); |
+ std::string vendor = base::SysCFStringRefToUTF8(cf_vendor); |
+ std::string model = base::SysCFStringRefToUTF8(cf_model); |
+ |
+ CFNumberRef size_number = base::mac::GetValueFromDictionary<CFNumberRef>( |
+ dict, kDADiskDescriptionMediaSizeKey); |
+ uint64 size_in_bytes = 0; |
+ if (size_number) |
+ CFNumberGetValue(size_number, kCFNumberLongLongType, &size_in_bytes); |
+ |
+ linked_ptr<api::image_writer_private::RemovableStorageDevice> device( |
+ new api::image_writer_private::RemovableStorageDevice()); |
+ device->storage_unit_id = bsd_name; |
+ device->capacity = size_in_bytes; |
+ device->vendor = vendor; |
+ device->model = model; |
+ device_list_->data.push_back(device); |
+ } |
+ |
+ updates_pending_--; |
+ |
+ if (updates_pending_ == 0) { |
+ callback_.Run(device_list_, true); |
+ } |
+} |
+ |
+// static |
+void DAWrapper::DiskAppearedCallback(DADiskRef disk, void* context) { |
+ DAWrapper* wrapper = static_cast<DAWrapper*>(context); |
+ base::ScopedCFTypeRef<CFDictionaryRef> dict(DADiskCopyDescription(disk)); |
+ wrapper->ProcessDisk(dict); |
+} |
+ |
+// static |
+void DAWrapper::DiskDisappearedCallback(DADiskRef disk, void* context) { |
+} |
+ |
+// static |
+bool DAWrapper::DiskIsValidTarget(CFDictionaryRef dict) { |
+ CFBooleanRef internal = (CFBooleanRef)CFDictionaryGetValue( |
Robert Sesek
2014/06/02 19:56:05
C-style casts are banned. Use GetValueFromDictiona
Drew Haven
2014/06/03 02:03:23
Done.
|
+ dict, kDADiskDescriptionDeviceInternalKey); |
+ CFStringRef protocol = (CFStringRef)CFDictionaryGetValue( |
+ dict, kDADiskDescriptionDeviceProtocolKey); |
+ CFStringRef io_reg_path = |
+ (CFStringRef)CFDictionaryGetValue(dict, kDADiskDescriptionDevicePathKey); |
+ CFBooleanRef ejectable = (CFBooleanRef)CFDictionaryGetValue( |
+ dict, kDADiskDescriptionMediaEjectableKey); |
+ CFBooleanRef removable = (CFBooleanRef)CFDictionaryGetValue( |
+ dict, kDADiskDescriptionMediaRemovableKey); |
+ CFBooleanRef whole = |
+ (CFBooleanRef)CFDictionaryGetValue(dict, kDADiskDescriptionMediaWholeKey); |
+ CFStringRef kind = |
+ (CFStringRef)CFDictionaryGetValue(dict, kDADiskDescriptionMediaKindKey); |
+ |
+ // A drive is a USB stick iff: |
+ // - it is not internal |
+ // - it is attached to the USB bus |
+ // - it is ejectable (because it will be ejected after written to) |
+ // - it is removable |
+ // - it is the whole drive (although the use of |
+ // kDADiskDescriptionMatchMediaWhole should have ensured this) |
+ // - it is of type IOMedia (external DVD drives and the like are IOCDMedia |
+ // or |
+ // IODVDMedia) |
+ bool isUSBStick = |
Robert Sesek
2014/06/02 19:56:05
naming: is_usb_stick
Drew Haven
2014/06/03 02:03:23
Done.
|
+ !CFBooleanGetValue(internal) && |
+ CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) && |
+ CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) && |
+ CFBooleanGetValue(whole) && |
+ CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo; |
+ |
+ // A drive is an SD card iff: |
+ // - it is attached to the USB bus |
+ // - it is ejectable (because it will be ejected after written to) |
+ // - it is removable |
+ // - it is the whole drive (although the use of |
+ // kDADiskDescriptionMatchMediaWhole should have ensured this) |
+ // - it is of type IOMedia (external DVD drives and the like are IOCDMedia |
+ // or |
+ // IODVDMedia) |
+ // - the IORegistry device path contains "AppleUSBCardReader" |
+ bool isSDCard = |
Robert Sesek
2014/06/02 19:56:05
naming: is_sd_card
Drew Haven
2014/06/03 02:03:23
Done.
|
+ CFEqual(protocol, CFSTR(kIOPropertyPhysicalInterconnectTypeUSB)) && |
+ CFBooleanGetValue(ejectable) && CFBooleanGetValue(removable) && |
+ CFBooleanGetValue(whole) && |
+ CFStringCompare(kind, CFSTR("IOMedia"), 0) == kCFCompareEqualTo && |
+ CFStringFind(io_reg_path, CFSTR("AppleUSBCardReader"), 0).location != |
+ kCFNotFound; |
+ |
+ return isUSBStick || isSDCard; |
+} |
+ |
+// static |
+void DAWrapper::WrapperDeleter(DAWrapper* wrapper, |
+ const DeviceListReadyCallback& callback, |
+ scoped_refptr<StorageDeviceList> device_list, |
+ bool success) { |
+ delete wrapper; |
+ callback.Run(device_list, success); |
+} |
+ |
+DAWrapperSpouse::DAWrapperSpouse(DAWrapper* spouse) : spouse_(spouse) { |
+} |
+DAWrapperSpouse::~DAWrapperSpouse() { |
+} |
+ |
+void DAWrapperSpouse::SetCallback(const DeviceListReadyCallback& callback) { |
+ spouse_->callback_ = callback; |
+} |
+ |
+void DAWrapperSpouse::SetDeviceList( |
scoped_refptr<StorageDeviceList> device_list) { |
- return false; |
+ spouse_->device_list_ = device_list; |
+} |
+ |
+void DAWrapperSpouse::ProcessDisk(base::ScopedCFTypeRef<CFDictionaryRef> disk) { |
+ spouse_->ProcessDisk(disk); |
+} |
+ |
+// static |
+bool DAWrapperSpouse::DiskIsValidTarget(CFDictionaryRef dict) { |
+ return DAWrapper::DiskIsValidTarget(dict); |
} |
} // namespace extensions |