Chromium Code Reviews| 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 |