Chromium Code Reviews| Index: chrome/browser/extensions/api/system_info_storage/storage_info_provider.cc |
| diff --git a/chrome/browser/extensions/api/system_info_storage/storage_info_provider.cc b/chrome/browser/extensions/api/system_info_storage/storage_info_provider.cc |
| index 33c9ace5e64eddb8bcdc54c9a693b44232c3acd6..ebf0a25a0b890d07abdb5edcdecb45bbb000bde6 100644 |
| --- a/chrome/browser/extensions/api/system_info_storage/storage_info_provider.cc |
| +++ b/chrome/browser/extensions/api/system_info_storage/storage_info_provider.cc |
| @@ -5,6 +5,7 @@ |
| #include "chrome/browser/extensions/api/system_info_storage/storage_info_provider.h" |
| #include "base/stl_util.h" |
| +#include "base/sys_info.h" |
| #include "base/threading/sequenced_worker_pool.h" |
| #include "base/utf_string_conversions.h" |
| #include "chrome/browser/storage_monitor/storage_monitor.h" |
| @@ -14,6 +15,10 @@ namespace extensions { |
| using content::BrowserThread; |
| using api::experimental_system_info_storage::StorageUnitInfo; |
| +using api::experimental_system_info_storage::ParseStorageUnitType; |
| +using api::experimental_system_info_storage::STORAGE_UNIT_TYPE_FIXED; |
| +using api::experimental_system_info_storage::STORAGE_UNIT_TYPE_REMOVABLE; |
| +using chrome::StorageMonitor; |
| namespace systeminfo { |
| @@ -21,127 +26,175 @@ const char kStorageTypeUnknown[] = "unknown"; |
| const char kStorageTypeFixed[] = "fixed"; |
| const char kStorageTypeRemovable[] = "removable"; |
| +void BuildStorageUnitInfo(const chrome::StorageInfo& info, |
| + StorageUnitInfo* unit) { |
| + unit->id = info.device_id(); |
|
vandebo (ex-Chrome)
2013/05/28 20:00:29
you need to get the transient id. StorageInfo::de
Greg Billock
2013/06/26 21:00:14
Do this with chrome::TransientDeviceIds::GetTransi
|
| +#if defined(OS_POSIX) |
| + unit->location = info.location(); |
| +#elif defined(OS_WIN) |
| + unit->location = WideToUTF8(info.location()); |
| +#endif |
| + // TODO(hmin): Might need to take MTP device into consideration. |
|
vandebo (ex-Chrome)
2013/05/28 20:00:29
If needed, we could an IsMassStorage predicate.
Hongbo Min
2013/05/29 01:05:38
In that sense, MTP device can be categoried as rem
Lei Zhang
2013/05/29 01:35:14
Yes, MTP devices are certainly removable devices.
Greg Billock
2013/06/26 21:00:14
IsRemovableDevice should convey what you want here
|
| + unit->type = chrome::StorageInfo::IsRemovableDevice(info.device_id()) ? |
| + STORAGE_UNIT_TYPE_REMOVABLE : STORAGE_UNIT_TYPE_FIXED; |
| + unit->vendor_name = UTF16ToUTF8(info.vendor_name()); |
| + unit->model_name = UTF16ToUTF8(info.model_name()); |
| + unit->capacity = static_cast<double>(info.total_size_in_bytes()); |
| + unit->available_capacity = 0; |
| +} |
| + |
| } // namespace systeminfo |
| const int kDefaultPollingIntervalMs = 1000; |
| const char kWatchingTokenName[] = "_storage_info_watching_token_"; |
| StorageInfoProvider::StorageInfoProvider() |
| - : observers_(new ObserverListThreadSafe<StorageInfoObserver>()), |
| + : observers_(new ObserverListThreadSafe<StorageFreeSpaceObserver>()), |
| watching_interval_(kDefaultPollingIntervalMs) { |
| - DCHECK(chrome::StorageMonitor::GetInstance()); |
| - chrome::StorageMonitor::GetInstance()->AddObserver(this); |
| } |
| StorageInfoProvider::~StorageInfoProvider() { |
| - // Note that StorageInfoProvider is defined as a LazyInstance which would be |
| - // destroyed at process exiting, so its lifetime should be longer than the |
| - // StorageMonitor instance that would be destroyed before |
| - // StorageInfoProvider. |
| - if (chrome::StorageMonitor::GetInstance()) |
| - chrome::StorageMonitor::GetInstance()->RemoveObserver(this); |
| } |
| -void StorageInfoProvider::AddObserver(StorageInfoObserver* obs) { |
| - observers_->AddObserver(obs); |
| +void StorageInfoProvider::StartQueryInfoImpl() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + // Register a callback to notify UI thread that StorageMonitor finishes the |
| + // storage metadata retrieval on FILE thread. See the comments of |
| + // StorageMonitor::Initialize about when the callback gets run. |
| + StorageMonitor::GetInstance()->RegisterInitCompletedCallback( |
| + base::Bind(&StorageInfoProvider::QueryInfoImplOnUIThread, this)); |
| } |
| -void StorageInfoProvider::RemoveObserver(StorageInfoObserver* obs) { |
| - observers_->RemoveObserver(obs); |
| +void StorageInfoProvider::QueryInfoImplOnUIThread() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + // At this point, we can call StorageMonitor::GetAllAvailableStorages to |
| + // get the correct results. |
| + QueryInfo(&info_); |
| + // The amount of available capacity should be queried on blocking pool. |
| + PostQueryTaskToBlockingPool(FROM_HERE, |
| + base::Bind(&StorageInfoProvider::QueryAvailableCapacityOnBlockingPool, |
| + this)); |
| } |
| -void StorageInfoProvider::OnRemovableStorageAttached( |
| - const chrome::StorageInfo& info) { |
| - // Since the storage API uses the location as identifier, e.g. |
| - // the drive letter on Windows while mount point on Posix. Here we has to |
| - // convert the |info.location| to UTF-8 encoding. |
| - // |
| - // TODO(hongbo): use |info.device_id| instead of using |info.location| |
| - // to keep the id persisting between device attachments, like |
| - // StorageMonitor does. |
| -#if defined(OS_POSIX) |
| - std::string id = info.location(); |
| -#elif defined(OS_WIN) |
| - std::string id = UTF16ToUTF8(info.location()); |
| -#endif |
| - // Post a task to blocking pool for querying the information. |
| - BrowserThread::PostBlockingPoolTask(FROM_HERE, |
| - base::Bind(&StorageInfoProvider::QueryAttachedStorageInfoOnBlockingPool, |
| - this, id)); |
| +std::vector<chrome::StorageInfo> StorageInfoProvider::GetAllStorages() const { |
| + return StorageMonitor::GetInstance()->GetAllAvailableStorages(); |
| +} |
| + |
| +bool StorageInfoProvider::QueryInfo(StorageUnitList* info) { |
| + std::vector<chrome::StorageInfo> storage_list = GetAllStorages(); |
| + |
| + StorageUnitList results; |
| + std::vector<chrome::StorageInfo>::const_iterator it = storage_list.begin(); |
| + for (; it != storage_list.end(); ++it) { |
| + linked_ptr<StorageUnitInfo> unit(new StorageUnitInfo()); |
| + systeminfo::BuildStorageUnitInfo(*it, unit.get()); |
| + results.push_back(unit); |
| + } |
| + info->swap(results); |
| + |
| + return true; |
| +} |
| + |
| +void StorageInfoProvider::AddObserver(StorageFreeSpaceObserver* obs) { |
| + observers_->AddObserver(obs); |
| +} |
| + |
| +void StorageInfoProvider::RemoveObserver(StorageFreeSpaceObserver* obs) { |
| + observers_->RemoveObserver(obs); |
| } |
| -void StorageInfoProvider::QueryAttachedStorageInfoOnBlockingPool( |
| - const std::string& id) { |
| +void StorageInfoProvider::QueryAvailableCapacityOnBlockingPool() { |
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| + for (StorageUnitList::iterator it = info_.begin(); it != info_.end(); ++it) { |
| + int64 amount = GetStorageFreeSpace( |
| + base::FilePath::FromUTF8Unsafe((*it)->location)); |
| + if (amount > 0) |
| + (*it)->available_capacity = static_cast<double>(amount); |
| + } |
| - StorageUnitInfo info; |
| - if (!QueryUnitInfo(id, &info)) |
| - return; |
| - observers_->Notify(&StorageInfoObserver::OnStorageAttached, |
| - info.id, |
| - info.type, |
| - info.capacity, |
| - info.available_capacity); |
| + // Notify UI thread that the querying operation is already completed. |
| + BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
| + base::Bind(&StorageInfoProvider::OnQueryCompleted, this, true)); |
| } |
| -void StorageInfoProvider::OnRemovableStorageDetached( |
| - const chrome::StorageInfo& info) { |
| - // TODO(hongbo): Use |info.device_id| instead. Same as the above. |
| -#if defined(OS_POSIX) |
| - std::string id = info.location(); |
| -#elif defined(OS_WIN) |
| - std::string id = UTF16ToUTF8(info.location()); |
| -#endif |
| - observers_->Notify(&StorageInfoObserver::OnStorageDetached, id); |
| +int64 StorageInfoProvider::GetStorageFreeSpace( |
| + const base::FilePath& mount_path) { |
| + return base::SysInfo::AmountOfFreeDiskSpace(mount_path); |
| } |
| void StorageInfoProvider::StartWatching(const std::string& id) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + base::FilePath mount_path; |
| + if (!GetStoragePathFromId(id, &mount_path)) |
| + return; |
| + |
| BrowserThread::PostBlockingPoolSequencedTask( |
| kWatchingTokenName, |
| FROM_HERE, |
| base::Bind(&StorageInfoProvider::AddWatchedStorageOnBlockingPool, |
| - this, id)); |
| + this, mount_path)); |
| } |
| void StorageInfoProvider::StopWatching(const std::string& id) { |
| + base::FilePath mount_path; |
| + |
| + if (!GetStoragePathFromId(id, &mount_path)) |
| + return; |
| + |
| BrowserThread::PostBlockingPoolSequencedTask( |
| kWatchingTokenName, |
| FROM_HERE, |
| base::Bind(&StorageInfoProvider::RemoveWatchedStorageOnBlockingPool, |
| - this, id)); |
| + this, mount_path)); |
| +} |
| + |
| +bool StorageInfoProvider::GetStoragePathFromId(const std::string& id, |
| + base::FilePath* mount_path) { |
| + std::vector<chrome::StorageInfo> storage_list = GetAllStorages(); |
| + std::vector<chrome::StorageInfo>::const_iterator it = storage_list.begin(); |
| + // Lookup the matched storage info by |id|. |
| + for (; it != storage_list.end() && it->device_id() != id; ++it); |
| + |
| + // No matched StorageInfo is found. |
| + if (it == storage_list.end()) |
| + return false; |
| + |
| + *mount_path = base::FilePath(it->location()); |
| + return true; |
| } |
| void StorageInfoProvider::AddWatchedStorageOnBlockingPool( |
| - const std::string& id) { |
| + const base::FilePath& mount_path) { |
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| // If the storage |id| is already being watched. |
| - if (ContainsKey(storage_id_to_size_map_, id)) |
| + if (ContainsKey(storage_location_to_size_map_, mount_path)) |
| return; |
| - StorageUnitInfo info; |
| - if (!QueryUnitInfo(id, &info)) |
| + int64 available_bytes = GetStorageFreeSpace(mount_path); |
| + if (available_bytes < 0) |
| return; |
| - storage_id_to_size_map_[id] = info.available_capacity; |
| + storage_location_to_size_map_[mount_path] = available_bytes; |
| // If it is the first storage to be watched, we need to start the watching |
| // timer. |
| - if (storage_id_to_size_map_.size() == 1) { |
| + if (storage_location_to_size_map_.size() == 1) { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::Bind(&StorageInfoProvider::StartWatchingTimerOnUIThread, this)); |
| } |
| } |
| void StorageInfoProvider::RemoveWatchedStorageOnBlockingPool( |
| - const std::string& id) { |
| + const base::FilePath& mount_path) { |
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| - if (!ContainsKey(storage_id_to_size_map_, id)) |
| + if (!ContainsKey(storage_location_to_size_map_, mount_path)) |
| return; |
| - storage_id_to_size_map_.erase(id); |
| + storage_location_to_size_map_.erase(mount_path); |
| // Stop watching timer if there is no storage to be watched. |
| - if (storage_id_to_size_map_.empty()) { |
| + if (storage_location_to_size_map_.empty()) { |
| BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| base::Bind(&StorageInfoProvider::StopWatchingTimerOnUIThread, this)); |
| } |
| @@ -149,38 +202,47 @@ void StorageInfoProvider::RemoveWatchedStorageOnBlockingPool( |
| void StorageInfoProvider::CheckWatchedStorages() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| - BrowserThread::PostBlockingPoolSequencedTask(kWatchingTokenName, FROM_HERE, |
| - base::Bind(&StorageInfoProvider::CheckWatchedStoragesOnBlockingPool, |
| - this)); |
| + |
| + std::vector<chrome::StorageInfo> storage_list = GetAllStorages(); |
| + for (std::vector<chrome::StorageInfo>::iterator it = storage_list.begin(); |
| + it != storage_list.end(); ++it) { |
| + BrowserThread::PostBlockingPoolSequencedTask( |
| + kWatchingTokenName, |
| + FROM_HERE, |
| + base::Bind(&StorageInfoProvider::CheckWatchedStorageOnBlockingPool, |
| + this, it->device_id(), base::FilePath(it->location()))); |
| + } |
| + |
| } |
| -void StorageInfoProvider::CheckWatchedStoragesOnBlockingPool() { |
| +void StorageInfoProvider::CheckWatchedStorageOnBlockingPool( |
| + const std::string& id, const base::FilePath& mount_path ) { |
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread()); |
| - for (StorageIDToSizeMap::iterator it = storage_id_to_size_map_.begin(); |
| - it != storage_id_to_size_map_.end(); ) { |
| - StorageUnitInfo info; |
| - if (!QueryUnitInfo(it->first, &info)) { |
| - storage_id_to_size_map_.erase(it++); |
| - continue; |
| - } |
| - if (it->second != info.available_capacity) { |
| - observers_->Notify(&StorageInfoObserver::OnStorageFreeSpaceChanged, |
| - it->first, /* storage id */ |
| - it->second, /* old free space value */ |
| - info.available_capacity /* new value */); |
| - it->second = info.available_capacity; |
| - } |
| - ++it; |
| - } |
| - if (storage_id_to_size_map_.size() == 0) { |
| + if (!ContainsKey(storage_location_to_size_map_, mount_path)) |
| + return; |
| + |
| + double available_bytes = static_cast<double>(GetStorageFreeSpace(mount_path)); |
| + if (available_bytes < 0) |
| + storage_location_to_size_map_.erase(mount_path); |
| + |
| + if (storage_location_to_size_map_.size() == 0) { |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| - base::Bind(&StorageInfoProvider::StopWatchingTimerOnUIThread, |
| - this)); |
| - return; |
| + base::Bind(&StorageInfoProvider::StopWatchingTimerOnUIThread, this)); |
| + } |
| + |
| + double old_available_bytes = storage_location_to_size_map_[mount_path]; |
| + if (old_available_bytes != available_bytes) { |
| + // Ignore free space change event if the old available capacity is 0. |
| + if (old_available_bytes > 0) { |
| + observers_->Notify(&StorageFreeSpaceObserver::OnFreeSpaceChanged, |
| + id, /* storage id */ |
| + old_available_bytes, /* old value */ |
| + available_bytes /* new value */); |
| + } |
| + storage_location_to_size_map_[mount_path] = available_bytes; |
| } |
| - OnCheckWatchedStoragesFinishedForTesting(); |
| } |
| void StorageInfoProvider::StartWatchingTimerOnUIThread() { |
| @@ -198,4 +260,9 @@ void StorageInfoProvider::StopWatchingTimerOnUIThread() { |
| watching_timer_.Stop(); |
| } |
| +// static |
| +StorageInfoProvider* StorageInfoProvider::Get() { |
| + return StorageInfoProvider::GetInstance<StorageInfoProvider>(); |
| +} |
| + |
| } // namespace extensions |