| 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 b29afa09bb4b775392070e88523654f633290cc6..54f47654bb5e0f1c1ac7b4a6c18e5cd5c5d3e61a 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
|
| @@ -6,6 +6,7 @@
|
|
|
| #include "base/stl_util.h"
|
| #include "base/strings/utf_string_conversions.h"
|
| +#include "base/sys_info.h"
|
| #include "base/threading/sequenced_worker_pool.h"
|
| #include "chrome/browser/storage_monitor/storage_monitor.h"
|
| #include "content/public/browser/browser_thread.h"
|
| @@ -13,7 +14,10 @@
|
| namespace extensions {
|
|
|
| using content::BrowserThread;
|
| +using chrome::StorageMonitor;
|
| using api::experimental_system_info_storage::StorageUnitInfo;
|
| +using api::experimental_system_info_storage::STORAGE_UNIT_TYPE_FIXED;
|
| +using api::experimental_system_info_storage::STORAGE_UNIT_TYPE_REMOVABLE;
|
|
|
| namespace systeminfo {
|
|
|
| @@ -21,127 +25,173 @@ 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();
|
| +#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.
|
| + unit->type = chrome::StorageInfo::IsRemovableDevice(unit->id) ?
|
| + STORAGE_UNIT_TYPE_REMOVABLE : STORAGE_UNIT_TYPE_FIXED;
|
| + 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()->Initialize(
|
| + 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));
|
| +void StorageInfoProvider::QueryAvailableCapacityOnBlockingPool() {
|
| + DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| + for (StorageInfo::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);
|
| + }
|
| +
|
| + // 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::QueryAttachedStorageInfoOnBlockingPool(
|
| - const std::string& id) {
|
| - DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| +std::vector<chrome::StorageInfo> StorageInfoProvider::GetAllStorages() const {
|
| + return StorageMonitor::GetInstance()->GetAllAvailableStorages();
|
| +}
|
|
|
| - StorageUnitInfo info;
|
| - if (!QueryUnitInfo(id, &info))
|
| - return;
|
| - observers_->Notify(&StorageInfoObserver::OnStorageAttached,
|
| - info.id,
|
| - info.type,
|
| - info.capacity,
|
| - info.available_capacity);
|
| +bool StorageInfoProvider::QueryInfo(StorageInfo* info) {
|
| + std::vector<chrome::StorageInfo> storage_list = GetAllStorages();
|
| + StorageInfo 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::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);
|
| +void StorageInfoProvider::AddObserver(StorageFreeSpaceObserver* obs) {
|
| + observers_->AddObserver(obs);
|
| +}
|
| +
|
| +void StorageInfoProvider::RemoveObserver(StorageFreeSpaceObserver* obs) {
|
| + observers_->RemoveObserver(obs);
|
| +}
|
| +
|
| +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();
|
| +
|
| + // Lookup the matched storage info by |id|.
|
| + for (std::vector<chrome::StorageInfo>::const_iterator it =
|
| + storage_list.begin();
|
| + it != storage_list.end(); ++it) {
|
| + if (id == it->device_id()) {
|
| + *mount_path = base::FilePath(it->location());
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + // No matched StorageInfo is found.
|
| + return false;
|
| }
|
|
|
| void StorageInfoProvider::AddWatchedStorageOnBlockingPool(
|
| - const std::string& id) {
|
| + const base::FilePath 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_, path))
|
| return;
|
|
|
| - StorageUnitInfo info;
|
| - if (!QueryUnitInfo(id, &info))
|
| + int64 available_bytes = GetStorageFreeSpace(path);
|
| + if (available_bytes < 0)
|
| return;
|
|
|
| - storage_id_to_size_map_[id] = info.available_capacity;
|
| + storage_location_to_size_map_[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 path) {
|
| DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
|
| - if (!ContainsKey(storage_id_to_size_map_, id))
|
| + if (!ContainsKey(storage_location_to_size_map_, path))
|
| return;
|
|
|
| - storage_id_to_size_map_.erase(id);
|
| + storage_location_to_size_map_.erase(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 +199,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 (!ContainsKey(storage_location_to_size_map_, mount_path)) {
|
| + return;
|
| }
|
|
|
| - if (storage_id_to_size_map_.size() == 0) {
|
| + 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));
|
| }
|
| - OnCheckWatchedStoragesFinishedForTesting();
|
| +
|
| + 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;
|
| + }
|
| }
|
|
|
| void StorageInfoProvider::StartWatchingTimerOnUIThread() {
|
| @@ -198,4 +257,9 @@ void StorageInfoProvider::StopWatchingTimerOnUIThread() {
|
| watching_timer_.Stop();
|
| }
|
|
|
| +// static
|
| +StorageInfoProvider* StorageInfoProvider::Get() {
|
| + return StorageInfoProvider::GetInstance<StorageInfoProvider>();
|
| +}
|
| +
|
| } // namespace extensions
|
|
|