This source file includes following definitions.
- PartitionChildScanResults
- SumFilesUnderPath
- AddScanResultsForProfile
- FindContainerScanResults
- CountScanResultsForExtension
- AddObserver
- RemoveObserver
- CancelScansForProfile
- StartScan
- CancelScan
- SetMediaFolderFinderFactory
- Observe
- ScanInProgress
- OnScanCompleted
- OnFoundContainerDirectories
#include "chrome/browser/media_galleries/media_scan_manager.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/time/time.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/media_galleries/media_galleries_preferences.h"
#include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
#include "chrome/browser/media_galleries/media_scan_manager_observer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/media_galleries.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
namespace media_galleries = extensions::api::media_galleries;
namespace {
typedef std::set<std::string > ScanningExtensionIdSet;
const int kContainerDirectoryMinimumPercent = 80;
const int kScanResultsExpiryTimeInHours = 24;
struct LocationInfo {
LocationInfo()
: pref_id(kInvalidMediaGalleryPrefId),
type(MediaGalleryPrefInfo::kInvalidType) {}
LocationInfo(MediaGalleryPrefId pref_id, MediaGalleryPrefInfo::Type type,
base::FilePath path)
: pref_id(pref_id), type(type), path(path) {}
bool operator<(const LocationInfo& rhs) const {
if (path.value() == rhs.path.value()) {
if (type == rhs.type) {
return pref_id > rhs.pref_id;
}
return rhs.type == MediaGalleryPrefInfo::kScanResult;
}
return path.value() < rhs.path.value();
}
MediaGalleryPrefId pref_id;
MediaGalleryPrefInfo::Type type;
base::FilePath path;
MediaGalleryScanResult file_counts;
};
void PartitionChildScanResults(
MediaGalleriesPreferences* preferences,
MediaFolderFinder::MediaFolderFinderResults* found_folders,
MediaFolderFinder::MediaFolderFinderResults* child_folders) {
std::vector<LocationInfo> all_locations;
for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
found_folders->begin(); it != found_folders->end(); ++it) {
all_locations.push_back(LocationInfo(kInvalidMediaGalleryPrefId,
MediaGalleryPrefInfo::kScanResult,
it->first));
all_locations.back().file_counts = it->second;
}
const MediaGalleriesPrefInfoMap& known_galleries =
preferences->known_galleries();
for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin();
it != known_galleries.end();
++it) {
all_locations.push_back(LocationInfo(it->second.pref_id, it->second.type,
it->second.AbsolutePath()));
}
std::sort(all_locations.begin(), all_locations.end());
size_t previous_parent_index = 0;
for (size_t i = 1; i < all_locations.size(); i++) {
const LocationInfo& current = all_locations[i];
const LocationInfo& previous_parent = all_locations[previous_parent_index];
bool is_child = previous_parent.path.IsParent(current.path);
if (current.type == MediaGalleryPrefInfo::kScanResult &&
current.pref_id == kInvalidMediaGalleryPrefId &&
(is_child || previous_parent.path == current.path)) {
(*child_folders)[current.path] = current.file_counts;
found_folders->erase(current.path);
} else if (!is_child) {
previous_parent_index = i;
}
}
}
MediaGalleryScanResult SumFilesUnderPath(
const base::FilePath& path,
const MediaFolderFinder::MediaFolderFinderResults& candidates) {
MediaGalleryScanResult results;
for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
candidates.begin(); it != candidates.end(); ++it) {
if (it->first == path || path.IsParent(it->first)) {
results.audio_count += it->second.audio_count;
results.image_count += it->second.image_count;
results.video_count += it->second.video_count;
}
}
return results;
}
void AddScanResultsForProfile(
MediaGalleriesPreferences* preferences,
const MediaFolderFinder::MediaFolderFinderResults& found_folders) {
MediaGalleryPrefIdSet to_remove;
const MediaGalleriesPrefInfoMap& known_galleries =
preferences->known_galleries();
for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin();
it != known_galleries.end();
++it) {
if (it->second.type == MediaGalleryPrefInfo::kScanResult &&
!preferences->NonAutoGalleryHasPermission(it->first)) {
to_remove.insert(it->first);
}
}
for (MediaGalleryPrefIdSet::const_iterator it = to_remove.begin();
it != to_remove.end();
++it) {
preferences->EraseGalleryById(*it);
}
MediaFolderFinder::MediaFolderFinderResults child_folders;
MediaFolderFinder::MediaFolderFinderResults
unique_found_folders(found_folders);
PartitionChildScanResults(preferences, &unique_found_folders, &child_folders);
std::map<MediaGalleryPrefId, MediaGalleryScanResult> to_update;
for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin();
it != known_galleries.end();
++it) {
const MediaGalleryPrefInfo& gallery = it->second;
if (!gallery.IsBlackListedType()) {
MediaGalleryScanResult file_counts =
SumFilesUnderPath(gallery.AbsolutePath(), child_folders);
if (gallery.audio_count != file_counts.audio_count ||
gallery.image_count != file_counts.image_count ||
gallery.video_count != file_counts.video_count) {
to_update[it->first] = file_counts;
}
}
}
for (std::map<MediaGalleryPrefId,
MediaGalleryScanResult>::const_iterator it = to_update.begin();
it != to_update.end();
++it) {
const MediaGalleryPrefInfo& gallery =
preferences->known_galleries().find(it->first)->second;
preferences->AddGallery(gallery.device_id, gallery.path, gallery.type,
gallery.volume_label, gallery.vendor_name,
gallery.model_name, gallery.total_size_in_bytes,
gallery.last_attach_time,
it->second.audio_count,
it->second.image_count,
it->second.video_count);
}
for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
unique_found_folders.begin();
it != unique_found_folders.end();
++it) {
MediaGalleryScanResult file_counts =
SumFilesUnderPath(it->first, child_folders);
file_counts.audio_count += it->second.audio_count;
file_counts.image_count += it->second.image_count;
file_counts.video_count += it->second.video_count;
MediaGalleryPrefInfo gallery;
bool existing = preferences->LookUpGalleryByPath(it->first, &gallery);
DCHECK(!existing);
preferences->AddGallery(gallery.device_id, gallery.path,
MediaGalleryPrefInfo::kScanResult,
gallery.volume_label, gallery.vendor_name,
gallery.model_name, gallery.total_size_in_bytes,
gallery.last_attach_time, file_counts.audio_count,
file_counts.image_count, file_counts.video_count);
}
UMA_HISTOGRAM_COUNTS_10000("MediaGalleries.ScanGalleriesPopulated",
unique_found_folders.size() + to_update.size());
}
MediaFolderFinder::MediaFolderFinderResults FindContainerScanResults(
const MediaFolderFinder::MediaFolderFinderResults& found_folders,
const std::vector<base::FilePath>& sensitive_locations) {
DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
std::vector<base::FilePath> abs_sensitive_locations;
for (size_t i = 0; i < sensitive_locations.size(); ++i) {
base::FilePath path = base::MakeAbsoluteFilePath(sensitive_locations[i]);
if (!path.empty())
abs_sensitive_locations.push_back(path);
}
typedef std::map<base::FilePath, int > ContainerCandidates;
ContainerCandidates candidates;
for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
found_folders.begin(); it != found_folders.end(); ++it) {
base::FilePath parent_directory = it->first.DirName();
bool is_sensitive = false;
base::FilePath abs_parent_directory =
base::MakeAbsoluteFilePath(parent_directory);
if (abs_parent_directory.empty())
continue;
for (size_t i = 0; i < abs_sensitive_locations.size(); ++i) {
if (abs_parent_directory == abs_sensitive_locations[i] ||
abs_parent_directory.IsParent(abs_sensitive_locations[i])) {
is_sensitive = true;
continue;
}
}
if (is_sensitive)
continue;
ContainerCandidates::iterator existing = candidates.find(parent_directory);
if (existing == candidates.end()) {
candidates[parent_directory] = 1;
} else {
existing->second++;
}
}
MediaFolderFinder::MediaFolderFinderResults result;
for (ContainerCandidates::const_iterator it = candidates.begin();
it != candidates.end();
++it) {
if (it->second <= 1)
continue;
base::FileEnumerator dir_counter(it->first, false ,
base::FileEnumerator::DIRECTORIES);
base::FileEnumerator::FileInfo info;
int count = 0;
for (base::FilePath name = dir_counter.Next();
!name.empty();
name = dir_counter.Next()) {
if (!base::IsLink(name))
count++;
}
if (it->second * 100 / count >= kContainerDirectoryMinimumPercent)
result[it->first] = MediaGalleryScanResult();
}
return result;
}
int CountScanResultsForExtension(MediaGalleriesPreferences* preferences,
const extensions::Extension* extension,
MediaGalleryScanResult* file_counts) {
int gallery_count = 0;
MediaGalleryPrefIdSet permitted_galleries =
preferences->GalleriesForExtension(*extension);
const MediaGalleriesPrefInfoMap& known_galleries =
preferences->known_galleries();
for (MediaGalleriesPrefInfoMap::const_iterator it = known_galleries.begin();
it != known_galleries.end();
++it) {
if (it->second.type == MediaGalleryPrefInfo::kScanResult &&
!ContainsKey(permitted_galleries, it->first)) {
gallery_count++;
file_counts->audio_count += it->second.audio_count;
file_counts->image_count += it->second.image_count;
file_counts->video_count += it->second.video_count;
}
}
return gallery_count;
}
}
MediaScanManager::MediaScanManager() : weak_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
MediaScanManager::~MediaScanManager() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
}
void MediaScanManager::AddObserver(Profile* profile,
MediaScanManagerObserver* observer) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!ContainsKey(observers_, profile));
observers_[profile].observer = observer;
}
void MediaScanManager::RemoveObserver(Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
bool scan_in_progress = ScanInProgress();
observers_.erase(profile);
DCHECK_EQ(scan_in_progress, ScanInProgress());
}
void MediaScanManager::CancelScansForProfile(Profile* profile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
observers_[profile].scanning_extensions.clear();
if (!ScanInProgress())
folder_finder_.reset();
}
void MediaScanManager::StartScan(Profile* profile,
const extensions::Extension* extension,
bool user_gesture) {
DCHECK(extension);
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ScanObserverMap::iterator scans_for_profile = observers_.find(profile);
DCHECK(scans_for_profile != observers_.end());
bool scan_in_progress = ScanInProgress();
ScanningExtensionIdSet* scanning_extensions;
scanning_extensions = &scans_for_profile->second.scanning_extensions;
if (scan_in_progress && ContainsKey(*scanning_extensions, extension->id()))
return;
MediaGalleriesPreferences* preferences =
MediaGalleriesPreferencesFactory::GetForProfile(profile);
base::TimeDelta time_since_last_scan =
base::Time::Now() - preferences->GetLastScanCompletionTime();
if (!scan_in_progress && !user_gesture && time_since_last_scan <
base::TimeDelta::FromHours(kScanResultsExpiryTimeInHours)) {
MediaGalleryScanResult file_counts;
int gallery_count =
CountScanResultsForExtension(preferences, extension, &file_counts);
scans_for_profile->second.observer->OnScanStarted(extension->id());
scans_for_profile->second.observer->OnScanFinished(extension->id(),
gallery_count,
file_counts);
return;
}
if (scanning_extensions->empty()) {
registrar_.Add(
this,
chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
content::Source<Profile>(profile));
}
scanning_extensions->insert(extension->id());
scans_for_profile->second.observer->OnScanStarted(extension->id());
if (folder_finder_)
return;
MediaFolderFinder::MediaFolderFinderResultsCallback callback =
base::Bind(&MediaScanManager::OnScanCompleted,
weak_factory_.GetWeakPtr());
if (testing_folder_finder_factory_.is_null()) {
folder_finder_.reset(new MediaFolderFinder(callback));
} else {
folder_finder_.reset(testing_folder_finder_factory_.Run(callback));
}
scan_start_time_ = base::Time::Now();
folder_finder_->StartScan();
}
void MediaScanManager::CancelScan(Profile* profile,
const extensions::Extension* extension) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
ScanObserverMap::iterator scans_for_profile = observers_.find(profile);
if (scans_for_profile == observers_.end() ||
!scans_for_profile->second.scanning_extensions.erase(extension->id())) {
return;
}
scans_for_profile->second.observer->OnScanCancelled(extension->id());
if (scans_for_profile->second.scanning_extensions.empty()) {
registrar_.Remove(
this,
chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
content::Source<Profile>(profile));
}
if (!ScanInProgress()) {
folder_finder_.reset();
DCHECK(!scan_start_time_.is_null());
UMA_HISTOGRAM_LONG_TIMES("MediaGalleries.ScanCancelTime",
base::Time::Now() - scan_start_time_);
scan_start_time_ = base::Time();
}
}
void MediaScanManager::SetMediaFolderFinderFactory(
const MediaFolderFinderFactory& factory) {
testing_folder_finder_factory_ = factory;
}
MediaScanManager::ScanObservers::ScanObservers() : observer(NULL) {}
MediaScanManager::ScanObservers::~ScanObservers() {}
void MediaScanManager::Observe(
int type, const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
switch (type) {
case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: {
Profile* profile = content::Source<Profile>(source).ptr();
extensions::Extension* extension = const_cast<extensions::Extension*>(
content::Details<extensions::UnloadedExtensionInfo>(
details)->extension);
DCHECK(extension);
CancelScan(profile, extension);
break;
}
default:
NOTREACHED();
}
}
bool MediaScanManager::ScanInProgress() const {
for (ScanObserverMap::const_iterator it = observers_.begin();
it != observers_.end();
++it) {
if (!it->second.scanning_extensions.empty())
return true;
}
return false;
}
void MediaScanManager::OnScanCompleted(
bool success,
const MediaFolderFinder::MediaFolderFinderResults& found_folders) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!folder_finder_ || !success) {
folder_finder_.reset();
return;
}
UMA_HISTOGRAM_COUNTS_10000("MediaGalleries.ScanDirectoriesFound",
found_folders.size());
DCHECK(!scan_start_time_.is_null());
UMA_HISTOGRAM_LONG_TIMES("MediaGalleries.ScanFinishedTime",
base::Time::Now() - scan_start_time_);
scan_start_time_ = base::Time();
content::BrowserThread::PostTaskAndReplyWithResult(
content::BrowserThread::FILE, FROM_HERE,
base::Bind(FindContainerScanResults,
found_folders,
folder_finder_->graylisted_folders()),
base::Bind(&MediaScanManager::OnFoundContainerDirectories,
weak_factory_.GetWeakPtr(),
found_folders));
}
void MediaScanManager::OnFoundContainerDirectories(
const MediaFolderFinder::MediaFolderFinderResults& found_folders,
const MediaFolderFinder::MediaFolderFinderResults& container_folders) {
MediaFolderFinder::MediaFolderFinderResults folders;
folders.insert(found_folders.begin(), found_folders.end());
folders.insert(container_folders.begin(), container_folders.end());
for (ScanObserverMap::iterator scans_for_profile = observers_.begin();
scans_for_profile != observers_.end();
++scans_for_profile) {
if (scans_for_profile->second.scanning_extensions.empty())
continue;
Profile* profile = scans_for_profile->first;
MediaGalleriesPreferences* preferences =
MediaGalleriesPreferencesFactory::GetForProfile(profile);
ExtensionService* extension_service =
extensions::ExtensionSystem::Get(profile)->extension_service();
if (!extension_service)
continue;
AddScanResultsForProfile(preferences, folders);
ScanningExtensionIdSet* scanning_extensions =
&scans_for_profile->second.scanning_extensions;
for (ScanningExtensionIdSet::const_iterator extension_id_it =
scanning_extensions->begin();
extension_id_it != scanning_extensions->end();
++extension_id_it) {
const extensions::Extension* extension =
extension_service->GetExtensionById(*extension_id_it, false);
if (extension) {
MediaGalleryScanResult file_counts;
int gallery_count = CountScanResultsForExtension(preferences, extension,
&file_counts);
scans_for_profile->second.observer->OnScanFinished(*extension_id_it,
gallery_count,
file_counts);
}
}
scanning_extensions->clear();
preferences->SetLastScanCompletionTime(base::Time::Now());
}
registrar_.RemoveAll();
folder_finder_.reset();
}