1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
7 #include "base/base_paths_posix.h"
8 #include "base/callback.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/path_service.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string16.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/media_galleries/fileapi/iapps_finder.h"
23 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h"
24 #include "chrome/browser/media_galleries/media_file_system_registry.h"
25 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/storage_monitor/media_storage_util.h"
28 #include "chrome/browser/storage_monitor/storage_monitor.h"
29 #include "chrome/common/chrome_paths.h"
30 #include "chrome/common/extensions/extension.h"
31 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
32 #include "chrome/common/extensions/permissions/permissions_data.h"
33 #include "chrome/common/pref_names.h"
34 #include "components/user_prefs/pref_registry_syncable.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "extensions/common/permissions/api_permission.h"
37 #include "grit/generated_resources.h"
38 #include "ui/base/l10n/l10n_util.h"
39 #include "ui/base/text/bytes_formatting.h"
41 using base::DictionaryValue;
42 using base::ListValue;
43 using extensions::ExtensionPrefs;
47 // Pref key for the list of media gallery permissions.
48 const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
49 // Pref key for Media Gallery ID.
50 const char kMediaGalleryIdKey[] = "id";
51 // Pref key for Media Gallery Permission Value.
52 const char kMediaGalleryHasPermissionKey[] = "has_permission";
54 const char kMediaGalleriesDeviceIdKey[] = "deviceId";
55 const char kMediaGalleriesDisplayNameKey[] = "displayName";
56 const char kMediaGalleriesPathKey[] = "path";
57 const char kMediaGalleriesPrefIdKey[] = "prefId";
58 const char kMediaGalleriesTypeKey[] = "type";
59 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
60 const char kMediaGalleriesVendorNameKey[] = "vendorName";
61 const char kMediaGalleriesModelNameKey[] = "modelName";
62 const char kMediaGalleriesSizeKey[] = "totalSize";
63 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
64 const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
66 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
67 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
68 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
70 const char kIPhotoGalleryName[] = "iPhoto";
71 const char kITunesGalleryName[] = "iTunes";
72 const char kPicasaGalleryName[] = "Picasa";
74 int NumberExtensionsUsingMediaGalleries(Profile* profile) {
78 ExtensionService* extension_service =
79 extensions::ExtensionSystem::Get(profile)->extension_service();
80 if (!extension_service)
83 const ExtensionSet* extensions = extension_service->extensions();
84 for (ExtensionSet::const_iterator i = extensions->begin();
85 i != extensions->end(); ++i) {
86 if (extensions::PermissionsData::HasAPIPermission(
87 *i, extensions::APIPermission::kMediaGalleries) ||
88 extensions::PermissionsData::HasAPIPermission(
89 *i, extensions::APIPermission::kMediaGalleriesPrivate)) {
96 bool GetPrefId(const DictionaryValue& dict, MediaGalleryPrefId* value) {
97 std::string string_id;
98 if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
99 !base::StringToUint64(string_id, value)) {
106 bool GetType(const DictionaryValue& dict, MediaGalleryPrefInfo::Type* type) {
107 std::string string_type;
108 if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
111 if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
112 *type = MediaGalleryPrefInfo::kAutoDetected;
115 if (string_type == kMediaGalleriesTypeUserAddedValue) {
116 *type = MediaGalleryPrefInfo::kUserAdded;
119 if (string_type == kMediaGalleriesTypeBlackListedValue) {
120 *type = MediaGalleryPrefInfo::kBlackListed;
127 bool PopulateGalleryPrefInfoFromDictionary(
128 const DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
129 MediaGalleryPrefId pref_id;
130 string16 display_name;
131 std::string device_id;
132 base::FilePath::StringType path;
133 MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kAutoDetected;
134 string16 volume_label;
135 string16 vendor_name;
137 double total_size_in_bytes = 0.0;
138 double last_attach_time = 0.0;
139 bool volume_metadata_valid = false;
140 int prefs_version = 0;
142 if (!GetPrefId(dict, &pref_id) ||
143 !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
144 !dict.GetString(kMediaGalleriesPathKey, &path) ||
145 !GetType(dict, &type)) {
149 dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
150 dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
152 if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
153 dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
154 dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
155 dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
156 dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
157 volume_metadata_valid = true;
160 out_gallery_info->pref_id = pref_id;
161 out_gallery_info->display_name = display_name;
162 out_gallery_info->device_id = device_id;
163 out_gallery_info->path = base::FilePath(path);
164 out_gallery_info->type = type;
165 out_gallery_info->volume_label = volume_label;
166 out_gallery_info->vendor_name = vendor_name;
167 out_gallery_info->model_name = model_name;
168 out_gallery_info->total_size_in_bytes = total_size_in_bytes;
169 out_gallery_info->last_attach_time =
170 base::Time::FromInternalValue(last_attach_time);
171 out_gallery_info->volume_metadata_valid = volume_metadata_valid;
172 out_gallery_info->prefs_version = prefs_version;
177 DictionaryValue* CreateGalleryPrefInfoDictionary(
178 const MediaGalleryPrefInfo& gallery) {
179 DictionaryValue* dict = new DictionaryValue();
180 dict->SetString(kMediaGalleriesPrefIdKey,
181 base::Uint64ToString(gallery.pref_id));
182 if (!gallery.volume_metadata_valid)
183 dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
184 dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
185 dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
187 const char* type = NULL;
188 switch (gallery.type) {
189 case MediaGalleryPrefInfo::kAutoDetected:
190 type = kMediaGalleriesTypeAutoDetectedValue;
192 case MediaGalleryPrefInfo::kUserAdded:
193 type = kMediaGalleriesTypeUserAddedValue;
195 case MediaGalleryPrefInfo::kBlackListed:
196 type = kMediaGalleriesTypeBlackListedValue;
202 dict->SetString(kMediaGalleriesTypeKey, type);
204 if (gallery.volume_metadata_valid) {
205 dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
206 dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
207 dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
208 dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
209 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
210 gallery.last_attach_time.ToInternalValue());
213 // Version 0 of the prefs format was that the display_name was always
214 // used to show the user-visible name of the gallery. Version 1 means
215 // that there is an optional display_name, and when it is present, it
216 // overrides the name that would be built from the volume metadata, path,
217 // or whatever other data. So if we see a display_name with version 0, it
218 // means it may be overwritten simply by getting new volume metadata.
219 // A display_name with version 1 should not be overwritten.
220 dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
225 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
226 extensions::MediaGalleriesPermission::CheckParam param(
227 extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
228 return extensions::PermissionsData::CheckAPIPermissionWithParam(
229 &extension, extensions::APIPermission::kMediaGalleries, ¶m);
232 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
234 bool GetMediaGalleryPermissionFromDictionary(
235 const DictionaryValue* dict,
236 MediaGalleryPermission* out_permission) {
237 std::string string_id;
238 if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
239 base::StringToUint64(string_id, &out_permission->pref_id) &&
240 dict->GetBoolean(kMediaGalleryHasPermissionKey,
241 &out_permission->has_permission)) {
248 string16 GetDisplayNameForDevice(uint64 storage_size_in_bytes,
249 const string16& name) {
250 DCHECK(!name.empty());
251 return (storage_size_in_bytes == 0) ?
252 name : ui::FormatBytes(storage_size_in_bytes) + ASCIIToUTF16(" ") + name;
255 // For a device with |device_name| and a relative path |sub_folder|, construct
256 // a display name. If |sub_folder| is empty, then just return |device_name|.
257 string16 GetDisplayNameForSubFolder(const string16& device_name,
258 const base::FilePath& sub_folder) {
259 if (sub_folder.empty())
261 return (sub_folder.BaseName().LossyDisplayName() +
262 ASCIIToUTF16(" - ") +
266 string16 GetFullProductName(const string16& vendor_name,
267 const string16& model_name) {
268 if (vendor_name.empty() && model_name.empty())
271 string16 product_name;
272 if (vendor_name.empty())
273 product_name = model_name;
274 else if (model_name.empty())
275 product_name = vendor_name;
276 else if (!vendor_name.empty() && !model_name.empty())
277 product_name = vendor_name + UTF8ToUTF16(", ") + model_name;
284 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
285 : pref_id(kInvalidMediaGalleryPrefId),
287 total_size_in_bytes(0),
288 volume_metadata_valid(false),
292 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
294 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
295 base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
296 DCHECK(!path.IsAbsolute());
297 return base_path.empty() ? base_path : base_path.Append(path);
300 string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const {
301 if (!StorageInfo::IsRemovableDevice(device_id)) {
302 // For fixed storage, the default name is the fully qualified directory
303 // name, or in the case of a root directory, the root directory name.
304 // Exception: ChromeOS -- the full pathname isn't visible there, so only
305 // the directory name is used.
306 base::FilePath path = AbsolutePath();
307 if (!display_name.empty())
310 #if defined(OS_CHROMEOS)
311 // See chrome/browser/chromeos/fileapi/file_system_backend.cc
312 base::FilePath home_path;
313 if (PathService::Get(base::DIR_HOME, &home_path)) {
314 home_path = home_path.AppendASCII("Downloads");
315 base::FilePath relative;
316 if (home_path.AppendRelativePath(path, &relative))
317 return relative.LossyDisplayName();
319 return path.BaseName().LossyDisplayName();
321 return path.LossyDisplayName();
325 string16 name = display_name;
329 name = GetFullProductName(vendor_name, model_name);
331 name = l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNLABELED_DEVICE);
333 name = GetDisplayNameForDevice(total_size_in_bytes, name);
336 name = GetDisplayNameForSubFolder(name, path);
341 string16 MediaGalleryPrefInfo::GetGalleryTooltip() const {
342 return AbsolutePath().LossyDisplayName();
345 string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const {
347 if (StorageInfo::IsRemovableDevice(device_id)) {
348 if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) {
349 attached = l10n_util::GetStringUTF16(
350 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED);
351 } else if (!last_attach_time.is_null()) {
352 attached = l10n_util::GetStringFUTF16(
353 IDS_MEDIA_GALLERIES_LAST_ATTACHED,
354 base::TimeFormatShortDateNumeric(last_attach_time));
356 attached = l10n_util::GetStringUTF16(
357 IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED);
364 bool MediaGalleryPrefInfo::IsGalleryAvailable() const {
365 return !StorageInfo::IsRemovableDevice(device_id) ||
366 MediaStorageUtil::IsRemovableStorageAttached(device_id);
369 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
371 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
372 : initialized_(false),
373 pre_initialization_callbacks_waiting_(0),
375 extension_prefs_for_testing_(NULL),
376 weak_factory_(this) {
379 MediaGalleriesPreferences::~MediaGalleriesPreferences() {
380 if (StorageMonitor::GetInstance())
381 StorageMonitor::GetInstance()->RemoveObserver(this);
384 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) {
385 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
387 if (IsInitialized()) {
388 if (!callback.is_null())
393 on_initialize_callbacks_.push_back(callback);
394 if (on_initialize_callbacks_.size() > 1)
397 // This counter must match the number of async methods dispatched below.
398 // It cannot be incremented inline with each callback, as some may return
399 // synchronously, decrement the counter to 0, and prematurely trigger
400 // FinishInitialization.
401 pre_initialization_callbacks_waiting_ = 3;
403 // Check whether we should be initializing -- are there any extensions that
404 // are using media galleries?
405 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED);
406 if (NumberExtensionsUsingMediaGalleries(profile_) == 0) {
407 media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR);
410 // We determine the freshness of the profile here, before any of the finders
411 // return and add media galleries to it.
412 StorageMonitor::GetInstance()->EnsureInitialized(
413 base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit,
414 weak_factory_.GetWeakPtr(),
415 !APIHasBeenUsed(profile_) /* add_default_galleries */));
417 // Look for optional default galleries every time.
418 iapps::FindITunesLibrary(
419 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
420 weak_factory_.GetWeakPtr()));
422 picasa::FindPicasaDatabase(
423 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
424 weak_factory_.GetWeakPtr()));
427 iapps::FindIPhotoLibrary(
428 base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
429 weak_factory_.GetWeakPtr()));
433 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; }
435 Profile* MediaGalleriesPreferences::profile() { return profile_; }
437 void MediaGalleriesPreferences::OnInitializationCallbackReturned() {
438 DCHECK(!IsInitialized());
439 DCHECK(pre_initialization_callbacks_waiting_ > 0);
440 if (--pre_initialization_callbacks_waiting_ == 0)
441 FinishInitialization();
444 void MediaGalleriesPreferences::FinishInitialization() {
445 DCHECK(!IsInitialized());
449 StorageMonitor* monitor = StorageMonitor::GetInstance();
450 DCHECK(monitor->IsInitialized());
454 StorageMonitor::GetInstance()->AddObserver(this);
456 std::vector<StorageInfo> existing_devices =
457 monitor->GetAllAvailableStorages();
458 for (size_t i = 0; i < existing_devices.size(); i++) {
459 if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
460 StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
462 AddGallery(existing_devices[i].device_id(),
465 existing_devices[i].storage_label(),
466 existing_devices[i].vendor_name(),
467 existing_devices[i].model_name(),
468 existing_devices[i].total_size_in_bytes(),
472 for (std::vector<base::Closure>::iterator iter =
473 on_initialize_callbacks_.begin();
474 iter != on_initialize_callbacks_.end();
478 on_initialize_callbacks_.clear();
481 void MediaGalleriesPreferences::AddDefaultGalleries() {
482 const int kDirectoryKeys[] = {
483 chrome::DIR_USER_MUSIC,
484 chrome::DIR_USER_PICTURES,
485 chrome::DIR_USER_VIDEOS,
488 for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
490 if (!PathService::Get(kDirectoryKeys[i], &path))
493 base::FilePath relative_path;
495 if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
496 AddGalleryInternal(info.device_id(), info.name(), relative_path, false,
497 info.storage_label(), info.vendor_name(),
498 info.model_name(), info.total_size_in_bytes(),
499 base::Time(), true, 2);
504 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType(
505 const std::string& device_id) {
506 StorageInfo::Type singleton_type;
507 if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL))
510 PrefService* prefs = profile_->GetPrefs();
511 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
512 prefs, prefs::kMediaGalleriesRememberedGalleries));
513 ListValue* list = update->Get();
514 for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) {
515 // All of these calls should succeed, but preferences file can be corrupt.
516 DictionaryValue* dict;
517 if (!(*iter)->GetAsDictionary(&dict))
519 std::string this_device_id;
520 if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
522 if (this_device_id == device_id)
523 return true; // No update is necessary.
524 StorageInfo::Type device_type;
525 if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL))
528 if (device_type == singleton_type) {
529 dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
530 update.reset(); // commits the update.
532 MediaGalleryPrefId pref_id;
533 if (GetPrefId(*dict, &pref_id)) {
534 FOR_EACH_OBSERVER(GalleryChangeObserver,
535 gallery_change_observers_,
536 OnGalleryInfoUpdated(this, pref_id));
544 void MediaGalleriesPreferences::OnStorageMonitorInit(
545 bool add_default_galleries) {
546 if (add_default_galleries)
547 AddDefaultGalleries();
548 OnInitializationCallbackReturned();
551 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) {
552 if (!device_id.empty() && !UpdateDeviceIDForSingletonType(device_id)) {
553 std::string gallery_name;
554 if (StorageInfo::IsIPhotoDevice(device_id))
555 gallery_name = kIPhotoGalleryName;
556 else if (StorageInfo::IsITunesDevice(device_id))
557 gallery_name = kITunesGalleryName;
558 else if (StorageInfo::IsPicasaDevice(device_id))
559 gallery_name = kPicasaGalleryName;
563 AddGalleryInternal(device_id, ASCIIToUTF16(gallery_name),
564 base::FilePath(), false /*not user added*/,
565 string16(), string16(), string16(), 0,
566 base::Time(), false, 2);
569 OnInitializationCallbackReturned();
572 void MediaGalleriesPreferences::InitFromPrefs() {
573 known_galleries_.clear();
576 PrefService* prefs = profile_->GetPrefs();
577 const ListValue* list = prefs->GetList(
578 prefs::kMediaGalleriesRememberedGalleries);
580 for (ListValue::const_iterator it = list->begin();
581 it != list->end(); ++it) {
582 const DictionaryValue* dict = NULL;
583 if (!(*it)->GetAsDictionary(&dict))
586 MediaGalleryPrefInfo gallery_info;
587 if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
590 known_galleries_[gallery_info.pref_id] = gallery_info;
591 device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
596 void MediaGalleriesPreferences::AddGalleryChangeObserver(
597 GalleryChangeObserver* observer) {
598 DCHECK(IsInitialized());
599 gallery_change_observers_.AddObserver(observer);
602 void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
603 GalleryChangeObserver* observer) {
604 DCHECK(IsInitialized());
605 gallery_change_observers_.RemoveObserver(observer);
608 void MediaGalleriesPreferences::OnRemovableStorageAttached(
609 const StorageInfo& info) {
610 DCHECK(IsInitialized());
611 if (!StorageInfo::IsMediaDevice(info.device_id()))
614 AddGallery(info.device_id(), base::FilePath(),
615 false /*not user added*/,
616 info.storage_label(),
619 info.total_size_in_bytes(),
623 bool MediaGalleriesPreferences::LookUpGalleryByPath(
624 const base::FilePath& path,
625 MediaGalleryPrefInfo* gallery_info) const {
626 DCHECK(IsInitialized());
628 base::FilePath relative_path;
629 if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
631 *gallery_info = MediaGalleryPrefInfo();
635 relative_path = relative_path.NormalizePathSeparators();
636 MediaGalleryPrefIdSet galleries_on_device =
637 LookUpGalleriesByDeviceId(info.device_id());
638 for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
639 it != galleries_on_device.end();
641 const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
642 if (gallery.path != relative_path)
646 *gallery_info = gallery;
650 // This method is called by controller::FilesSelected when the user
651 // adds a new gallery. Control reaches here when the selected gallery is
652 // on a volume we know about, but have no gallery already for. Returns
653 // hypothetical data to the caller about what the prefs will look like
654 // if the gallery is added.
655 // TODO(gbillock): split this out into another function so it doesn't
658 gallery_info->pref_id = kInvalidMediaGalleryPrefId;
659 gallery_info->device_id = info.device_id();
660 gallery_info->path = relative_path;
661 gallery_info->type = MediaGalleryPrefInfo::kUserAdded;
662 gallery_info->volume_label = info.storage_label();
663 gallery_info->vendor_name = info.vendor_name();
664 gallery_info->model_name = info.model_name();
665 gallery_info->total_size_in_bytes = info.total_size_in_bytes();
666 gallery_info->last_attach_time = base::Time::Now();
667 gallery_info->volume_metadata_valid = true;
668 gallery_info->prefs_version = 2;
673 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
674 const std::string& device_id) const {
675 DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
676 if (found == device_map_.end())
677 return MediaGalleryPrefIdSet();
678 return found->second;
681 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
682 MediaGalleryPrefId gallery_id,
683 const extensions::Extension* extension,
684 bool include_unpermitted_galleries) {
685 DCHECK(IsInitialized());
687 if (!include_unpermitted_galleries &&
688 !ContainsKey(GalleriesForExtension(*extension), gallery_id))
689 return base::FilePath();
691 MediaGalleriesPrefInfoMap::const_iterator it =
692 known_galleries_.find(gallery_id);
693 if (it == known_galleries_.end())
694 return base::FilePath();
695 return MediaStorageUtil::FindDevicePathById(it->second.device_id);
698 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
699 const std::string& device_id,
700 const base::FilePath& relative_path, bool user_added,
701 const string16& volume_label, const string16& vendor_name,
702 const string16& model_name, uint64 total_size_in_bytes,
703 base::Time last_attach_time) {
704 DCHECK(IsInitialized());
705 return AddGalleryInternal(device_id, string16(), relative_path, user_added,
706 volume_label, vendor_name, model_name,
707 total_size_in_bytes, last_attach_time, true, 2);
710 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
711 const std::string& device_id, const string16& display_name,
712 const base::FilePath& relative_path, bool user_added,
713 const string16& volume_label, const string16& vendor_name,
714 const string16& model_name, uint64 total_size_in_bytes,
715 base::Time last_attach_time,
716 bool volume_metadata_valid,
718 base::FilePath normalized_relative_path =
719 relative_path.NormalizePathSeparators();
720 MediaGalleryPrefIdSet galleries_on_device =
721 LookUpGalleriesByDeviceId(device_id);
722 for (MediaGalleryPrefIdSet::const_iterator pref_id_it =
723 galleries_on_device.begin();
724 pref_id_it != galleries_on_device.end();
726 const MediaGalleryPrefInfo& existing =
727 known_galleries_.find(*pref_id_it)->second;
728 if (existing.path != normalized_relative_path)
731 bool update_gallery_type =
732 user_added && (existing.type == MediaGalleryPrefInfo::kBlackListed);
733 // Status quo: In M27 and M28, galleries added manually use version 0,
734 // and galleries added automatically (including default galleries) use
735 // version 1. The name override is used by default galleries as well
736 // as all device attach events.
737 // We want to upgrade the name if the existing version is < 2. Leave it
738 // alone if the existing display name is set with version == 2 and the
739 // proposed new name is empty.
740 bool update_gallery_name = existing.display_name != display_name;
741 if (existing.prefs_version == 2 && !existing.display_name.empty() &&
742 display_name.empty()) {
743 update_gallery_name = false;
745 bool update_gallery_metadata = volume_metadata_valid &&
746 ((existing.volume_label != volume_label) ||
747 (existing.vendor_name != vendor_name) ||
748 (existing.model_name != model_name) ||
749 (existing.total_size_in_bytes != total_size_in_bytes) ||
750 (existing.last_attach_time != last_attach_time));
752 if (!update_gallery_name && !update_gallery_type &&
753 !update_gallery_metadata)
756 PrefService* prefs = profile_->GetPrefs();
757 scoped_ptr<ListPrefUpdate> update(
758 new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
759 ListValue* list = update->Get();
761 for (ListValue::const_iterator list_iter = list->begin();
762 list_iter != list->end();
764 DictionaryValue* dict;
765 MediaGalleryPrefId iter_id;
766 if ((*list_iter)->GetAsDictionary(&dict) &&
767 GetPrefId(*dict, &iter_id) &&
768 *pref_id_it == iter_id) {
769 if (update_gallery_type) {
770 dict->SetString(kMediaGalleriesTypeKey,
771 kMediaGalleriesTypeAutoDetectedValue);
773 if (update_gallery_name)
774 dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
775 if (update_gallery_metadata) {
776 dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
777 dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
778 dict->SetString(kMediaGalleriesModelNameKey, model_name);
779 dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
780 dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
781 last_attach_time.ToInternalValue());
783 dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
788 // Commits the prefs update.
791 if (update_gallery_name || update_gallery_metadata ||
792 update_gallery_type) {
794 FOR_EACH_OBSERVER(GalleryChangeObserver,
795 gallery_change_observers_,
796 OnGalleryInfoUpdated(this, *pref_id_it));
801 PrefService* prefs = profile_->GetPrefs();
803 MediaGalleryPrefInfo gallery_info;
804 gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
805 prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
806 gallery_info.display_name = display_name;
807 gallery_info.device_id = device_id;
808 gallery_info.path = normalized_relative_path;
809 gallery_info.type = MediaGalleryPrefInfo::kAutoDetected;
811 gallery_info.type = MediaGalleryPrefInfo::kUserAdded;
812 if (volume_metadata_valid) {
813 gallery_info.volume_label = volume_label;
814 gallery_info.vendor_name = vendor_name;
815 gallery_info.model_name = model_name;
816 gallery_info.total_size_in_bytes = total_size_in_bytes;
817 gallery_info.last_attach_time = last_attach_time;
819 gallery_info.volume_metadata_valid = volume_metadata_valid;
820 gallery_info.prefs_version = prefs_version;
823 ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
824 ListValue* list = update.Get();
825 list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
828 FOR_EACH_OBSERVER(GalleryChangeObserver,
829 gallery_change_observers_,
830 OnGalleryAdded(this, gallery_info.pref_id));
832 return gallery_info.pref_id;
835 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
836 const base::FilePath& path) {
837 DCHECK(IsInitialized());
838 MediaGalleryPrefInfo gallery_info;
839 if (LookUpGalleryByPath(path, &gallery_info) &&
840 gallery_info.type != MediaGalleryPrefInfo::kBlackListed) {
841 return gallery_info.pref_id;
843 return AddGalleryInternal(gallery_info.device_id,
844 gallery_info.display_name,
847 gallery_info.volume_label,
848 gallery_info.vendor_name,
849 gallery_info.model_name,
850 gallery_info.total_size_in_bytes,
851 gallery_info.last_attach_time,
852 gallery_info.volume_metadata_valid,
853 gallery_info.prefs_version);
856 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) {
857 DCHECK(IsInitialized());
858 PrefService* prefs = profile_->GetPrefs();
859 scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
860 prefs, prefs::kMediaGalleriesRememberedGalleries));
861 ListValue* list = update->Get();
863 if (!ContainsKey(known_galleries_, pref_id))
866 for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) {
867 DictionaryValue* dict;
868 MediaGalleryPrefId iter_id;
869 if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
870 pref_id == iter_id) {
871 RemoveGalleryPermissionsFromPrefs(pref_id);
872 MediaGalleryPrefInfo::Type type;
873 if (GetType(*dict, &type) &&
874 type == MediaGalleryPrefInfo::kAutoDetected) {
875 dict->SetString(kMediaGalleriesTypeKey,
876 kMediaGalleriesTypeBlackListedValue);
878 list->Erase(iter, NULL);
880 update.reset(NULL); // commits the update.
883 FOR_EACH_OBSERVER(GalleryChangeObserver,
884 gallery_change_observers_,
885 OnGalleryRemoved(this, pref_id));
891 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
892 const extensions::Extension& extension) const {
893 DCHECK(IsInitialized());
894 MediaGalleryPrefIdSet result;
896 if (HasAutoDetectedGalleryPermission(extension)) {
897 for (MediaGalleriesPrefInfoMap::const_iterator it =
898 known_galleries_.begin(); it != known_galleries_.end(); ++it) {
899 if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
900 result.insert(it->second.pref_id);
904 std::vector<MediaGalleryPermission> stored_permissions =
905 GetGalleryPermissionsFromPrefs(extension.id());
906 for (std::vector<MediaGalleryPermission>::const_iterator it =
907 stored_permissions.begin(); it != stored_permissions.end(); ++it) {
908 if (!it->has_permission) {
909 result.erase(it->pref_id);
911 MediaGalleriesPrefInfoMap::const_iterator gallery =
912 known_galleries_.find(it->pref_id);
913 DCHECK(gallery != known_galleries_.end());
914 if (gallery->second.type != MediaGalleryPrefInfo::kBlackListed) {
915 result.insert(it->pref_id);
917 NOTREACHED() << gallery->second.device_id;
924 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension(
925 const extensions::Extension& extension,
926 MediaGalleryPrefId pref_id,
927 bool has_permission) {
928 DCHECK(IsInitialized());
929 // The gallery may not exist anymore if the user opened a second config
930 // surface concurrently and removed it. Drop the permission update if so.
931 MediaGalleriesPrefInfoMap::const_iterator gallery_info =
932 known_galleries_.find(pref_id);
933 if (gallery_info == known_galleries_.end())
936 bool default_permission = false;
937 if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected)
938 default_permission = HasAutoDetectedGalleryPermission(extension);
939 // When the permission matches the default, we don't need to remember it.
940 if (has_permission == default_permission) {
941 if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id))
942 // If permission wasn't set, assume nothing has changed.
945 if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission))
949 FOR_EACH_OBSERVER(GalleryChangeObserver,
950 gallery_change_observers_,
951 OnPermissionAdded(this, extension.id(), pref_id));
953 FOR_EACH_OBSERVER(GalleryChangeObserver,
954 gallery_change_observers_,
955 OnPermissionRemoved(this, extension.id(), pref_id));
959 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries()
961 DCHECK(IsInitialized());
962 return known_galleries_;
965 void MediaGalleriesPreferences::Shutdown() {
966 weak_factory_.InvalidateWeakPtrs();
971 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
972 MediaGalleryPrefId current_id =
973 profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
974 return current_id != kInvalidMediaGalleryPrefId + 1;
978 void MediaGalleriesPreferences::RegisterProfilePrefs(
979 user_prefs::PrefRegistrySyncable* registry) {
980 registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
981 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
982 registry->RegisterUint64Pref(
983 prefs::kMediaGalleriesUniqueId,
984 kInvalidMediaGalleryPrefId + 1,
985 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
988 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
989 const std::string& extension_id,
990 MediaGalleryPrefId gallery_id,
992 DCHECK(IsInitialized());
993 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
995 kMediaGalleriesPermissions);
996 ListValue* permissions = update.Get();
998 permissions = update.Create();
1000 // If the gallery is already in the list, update the permission...
1001 for (ListValue::iterator iter = permissions->begin();
1002 iter != permissions->end(); ++iter) {
1003 DictionaryValue* dict = NULL;
1004 if (!(*iter)->GetAsDictionary(&dict))
1006 MediaGalleryPermission perm;
1007 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1009 if (perm.pref_id == gallery_id) {
1010 if (has_access != perm.has_permission) {
1011 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1019 // ...Otherwise, add a new entry for the gallery.
1020 DictionaryValue* dict = new DictionaryValue;
1021 dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
1022 dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
1023 permissions->Append(dict);
1027 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
1028 const std::string& extension_id,
1029 MediaGalleryPrefId gallery_id) {
1030 DCHECK(IsInitialized());
1031 ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
1033 kMediaGalleriesPermissions);
1034 ListValue* permissions = update.Get();
1038 for (ListValue::iterator iter = permissions->begin();
1039 iter != permissions->end(); ++iter) {
1040 const DictionaryValue* dict = NULL;
1041 if (!(*iter)->GetAsDictionary(&dict))
1043 MediaGalleryPermission perm;
1044 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1046 if (perm.pref_id == gallery_id) {
1047 permissions->Erase(iter, NULL);
1054 std::vector<MediaGalleryPermission>
1055 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
1056 const std::string& extension_id) const {
1057 DCHECK(IsInitialized());
1058 std::vector<MediaGalleryPermission> result;
1059 const ListValue* permissions;
1060 if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
1061 kMediaGalleriesPermissions,
1066 for (ListValue::const_iterator iter = permissions->begin();
1067 iter != permissions->end(); ++iter) {
1068 DictionaryValue* dict = NULL;
1069 if (!(*iter)->GetAsDictionary(&dict))
1071 MediaGalleryPermission perm;
1072 if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
1074 result.push_back(perm);
1080 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
1081 MediaGalleryPrefId gallery_id) {
1082 DCHECK(IsInitialized());
1083 ExtensionPrefs* prefs = GetExtensionPrefs();
1084 const DictionaryValue* extensions =
1085 prefs->pref_service()->GetDictionary(prefs::kExtensionsPref);
1089 for (DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
1091 if (!extensions::Extension::IdIsValid(iter.key())) {
1095 UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
1099 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
1100 DCHECK(IsInitialized());
1101 if (extension_prefs_for_testing_)
1102 return extension_prefs_for_testing_;
1103 return extensions::ExtensionPrefs::Get(profile_);
1106 void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
1107 extensions::ExtensionPrefs* extension_prefs) {
1108 DCHECK(IsInitialized());
1109 extension_prefs_for_testing_ = extension_prefs;