Home | History | Annotate | Download | only in media_galleries
      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.
      4 
      5 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
      6 
      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/media_galleries/fileapi/iapps_finder.h"
     22 #include "chrome/browser/media_galleries/fileapi/picasa_finder.h"
     23 #include "chrome/browser/media_galleries/imported_media_gallery_registry.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/common/chrome_paths.h"
     28 #include "chrome/common/pref_names.h"
     29 #include "chrome/grit/generated_resources.h"
     30 #include "components/crx_file/id_util.h"
     31 #include "components/pref_registry/pref_registry_syncable.h"
     32 #include "components/storage_monitor/media_storage_util.h"
     33 #include "components/storage_monitor/storage_monitor.h"
     34 #include "content/public/browser/browser_thread.h"
     35 #include "extensions/browser/extension_prefs.h"
     36 #include "extensions/browser/extension_system.h"
     37 #include "extensions/browser/pref_names.h"
     38 #include "extensions/common/extension_set.h"
     39 #include "extensions/common/permissions/api_permission.h"
     40 #include "extensions/common/permissions/media_galleries_permission.h"
     41 #include "extensions/common/permissions/permissions_data.h"
     42 #include "ui/base/l10n/l10n_util.h"
     43 
     44 using base::DictionaryValue;
     45 using base::ListValue;
     46 using extensions::ExtensionPrefs;
     47 using storage_monitor::MediaStorageUtil;
     48 using storage_monitor::StorageInfo;
     49 using storage_monitor::StorageMonitor;
     50 
     51 namespace {
     52 
     53 // Pref key for the list of media gallery permissions.
     54 const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
     55 // Pref key for Media Gallery ID.
     56 const char kMediaGalleryIdKey[] = "id";
     57 // Pref key for Media Gallery Permission Value.
     58 const char kMediaGalleryHasPermissionKey[] = "has_permission";
     59 
     60 const char kMediaGalleriesDeviceIdKey[] = "deviceId";
     61 const char kMediaGalleriesDisplayNameKey[] = "displayName";
     62 const char kMediaGalleriesPathKey[] = "path";
     63 const char kMediaGalleriesPrefIdKey[] = "prefId";
     64 const char kMediaGalleriesTypeKey[] = "type";
     65 const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
     66 const char kMediaGalleriesVendorNameKey[] = "vendorName";
     67 const char kMediaGalleriesModelNameKey[] = "modelName";
     68 const char kMediaGalleriesSizeKey[] = "totalSize";
     69 const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
     70 const char kMediaGalleriesScanAudioCountKey[] = "audioCount";
     71 const char kMediaGalleriesScanImageCountKey[] = "imageCount";
     72 const char kMediaGalleriesScanVideoCountKey[] = "videoCount";
     73 
     74 const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
     75 const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
     76 const char kMediaGalleriesTypeRemovedScanValue[] = "removedScan";
     77 const char kMediaGalleriesTypeScanResultValue[] = "scanResult";
     78 const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
     79 
     80 const char kMediaGalleriesDefaultGalleryTypeNotDefaultValue[] = "notDefault";
     81 const char kMediaGalleriesDefaultGalleryTypeMusicDefaultValue[] = "music";
     82 const char kMediaGalleriesDefaultGalleryTypePicturesDefaultValue[] = "pictures";
     83 const char kMediaGalleriesDefaultGalleryTypeVideosDefaultValue[] = "videos";
     84 
     85 const char kIPhotoGalleryName[] = "iPhoto";
     86 const char kITunesGalleryName[] = "iTunes";
     87 const char kPicasaGalleryName[] = "Picasa";
     88 
     89 const int kCurrentPrefsVersion = 3;
     90 
     91 int NumberExtensionsUsingMediaGalleries(Profile* profile) {
     92   int count = 0;
     93   if (!profile)
     94     return count;
     95   ExtensionService* extension_service =
     96       extensions::ExtensionSystem::Get(profile)->extension_service();
     97   if (!extension_service)
     98     return count;
     99 
    100   const extensions::ExtensionSet* extensions = extension_service->extensions();
    101   for (extensions::ExtensionSet::const_iterator i = extensions->begin();
    102        i != extensions->end(); ++i) {
    103     const extensions::PermissionsData* permissions_data =
    104         (*i)->permissions_data();
    105     if (permissions_data->HasAPIPermission(
    106             extensions::APIPermission::kMediaGalleries) ||
    107         permissions_data->HasAPIPermission(
    108             extensions::APIPermission::kMediaGalleriesPrivate)) {
    109       count++;
    110     }
    111   }
    112   return count;
    113 }
    114 
    115 bool GetPrefId(const base::DictionaryValue& dict, MediaGalleryPrefId* value) {
    116   std::string string_id;
    117   if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
    118       !base::StringToUint64(string_id, value)) {
    119     return false;
    120   }
    121 
    122   return true;
    123 }
    124 
    125 bool GetType(const base::DictionaryValue& dict,
    126              MediaGalleryPrefInfo::Type* type) {
    127   std::string string_type;
    128   if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
    129     return false;
    130 
    131   if (string_type == kMediaGalleriesTypeUserAddedValue) {
    132     *type = MediaGalleryPrefInfo::kUserAdded;
    133     return true;
    134   }
    135   if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
    136     *type = MediaGalleryPrefInfo::kAutoDetected;
    137     return true;
    138   }
    139   if (string_type == kMediaGalleriesTypeBlackListedValue) {
    140     *type = MediaGalleryPrefInfo::kBlackListed;
    141     return true;
    142   }
    143   if (string_type == kMediaGalleriesTypeScanResultValue) {
    144     *type = MediaGalleryPrefInfo::kScanResult;
    145     return true;
    146   }
    147   if (string_type == kMediaGalleriesTypeRemovedScanValue) {
    148     *type = MediaGalleryPrefInfo::kRemovedScan;
    149     return true;
    150   }
    151 
    152   return false;
    153 }
    154 
    155 const char* TypeToStringValue(MediaGalleryPrefInfo::Type type) {
    156   const char* result = NULL;
    157   switch (type) {
    158     case MediaGalleryPrefInfo::kUserAdded:
    159       result = kMediaGalleriesTypeUserAddedValue;
    160       break;
    161     case MediaGalleryPrefInfo::kAutoDetected:
    162       result = kMediaGalleriesTypeAutoDetectedValue;
    163       break;
    164     case MediaGalleryPrefInfo::kBlackListed:
    165       result = kMediaGalleriesTypeBlackListedValue;
    166       break;
    167     case MediaGalleryPrefInfo::kScanResult:
    168       result = kMediaGalleriesTypeScanResultValue;
    169       break;
    170     case MediaGalleryPrefInfo::kRemovedScan:
    171       result = kMediaGalleriesTypeRemovedScanValue;
    172       break;
    173     default:
    174       NOTREACHED();
    175       break;
    176   }
    177   return result;
    178 }
    179 
    180 MediaGalleryPrefInfo::DefaultGalleryType GetDefaultGalleryType(
    181     const base::DictionaryValue& dict) {
    182   std::string default_gallery_type_string;
    183   if (!dict.GetString(
    184           kMediaGalleriesDefaultGalleryTypeKey, &default_gallery_type_string))
    185     return MediaGalleryPrefInfo::kNotDefault;
    186 
    187   if (default_gallery_type_string ==
    188       kMediaGalleriesDefaultGalleryTypeMusicDefaultValue) {
    189     return MediaGalleryPrefInfo::kMusicDefault;
    190   }
    191   if (default_gallery_type_string ==
    192       kMediaGalleriesDefaultGalleryTypePicturesDefaultValue) {
    193     return MediaGalleryPrefInfo::kPicturesDefault;
    194   }
    195   if (default_gallery_type_string ==
    196       kMediaGalleriesDefaultGalleryTypeVideosDefaultValue) {
    197     return MediaGalleryPrefInfo::kVideosDefault;
    198   }
    199   return MediaGalleryPrefInfo::kNotDefault;
    200 }
    201 
    202 const char* DefaultGalleryTypeToStringValue(
    203     MediaGalleryPrefInfo::DefaultGalleryType default_gallery_type) {
    204   const char* result = NULL;
    205   switch (default_gallery_type) {
    206     case MediaGalleryPrefInfo::kNotDefault:
    207       result = kMediaGalleriesDefaultGalleryTypeNotDefaultValue;
    208       break;
    209     case MediaGalleryPrefInfo::kMusicDefault:
    210       result = kMediaGalleriesDefaultGalleryTypeMusicDefaultValue;
    211       break;
    212     case MediaGalleryPrefInfo::kPicturesDefault:
    213       result = kMediaGalleriesDefaultGalleryTypePicturesDefaultValue;
    214       break;
    215     case MediaGalleryPrefInfo::kVideosDefault:
    216       result = kMediaGalleriesDefaultGalleryTypeVideosDefaultValue;
    217       break;
    218     default:
    219       NOTREACHED();
    220       break;
    221   }
    222   return result;
    223 }
    224 
    225 bool PopulateGalleryPrefInfoFromDictionary(
    226     const base::DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
    227   MediaGalleryPrefId pref_id;
    228   base::string16 display_name;
    229   std::string device_id;
    230   base::FilePath::StringType path;
    231   MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kInvalidType;
    232   base::string16 volume_label;
    233   base::string16 vendor_name;
    234   base::string16 model_name;
    235   double total_size_in_bytes = 0.0;
    236   double last_attach_time = 0.0;
    237   bool volume_metadata_valid = false;
    238   int audio_count = 0;
    239   int image_count = 0;
    240   int video_count = 0;
    241   int prefs_version = 0;
    242 
    243   if (!GetPrefId(dict, &pref_id) ||
    244       !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
    245       !dict.GetString(kMediaGalleriesPathKey, &path) ||
    246       !GetType(dict, &type)) {
    247     return false;
    248   }
    249 
    250   dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
    251   dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
    252 
    253   if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
    254       dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
    255       dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
    256       dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
    257       dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
    258     volume_metadata_valid = true;
    259   }
    260 
    261   if (dict.GetInteger(kMediaGalleriesScanAudioCountKey, &audio_count) &&
    262       dict.GetInteger(kMediaGalleriesScanImageCountKey, &image_count) &&
    263       dict.GetInteger(kMediaGalleriesScanVideoCountKey, &video_count)) {
    264     out_gallery_info->audio_count = audio_count;
    265     out_gallery_info->image_count = image_count;
    266     out_gallery_info->video_count = video_count;
    267   } else {
    268     out_gallery_info->audio_count = 0;
    269     out_gallery_info->image_count = 0;
    270     out_gallery_info->video_count = 0;
    271   }
    272 
    273   out_gallery_info->pref_id = pref_id;
    274   out_gallery_info->display_name = display_name;
    275   out_gallery_info->device_id = device_id;
    276   out_gallery_info->path = base::FilePath(path);
    277   out_gallery_info->type = type;
    278   out_gallery_info->volume_label = volume_label;
    279   out_gallery_info->vendor_name = vendor_name;
    280   out_gallery_info->model_name = model_name;
    281   out_gallery_info->total_size_in_bytes = total_size_in_bytes;
    282   out_gallery_info->last_attach_time =
    283       base::Time::FromInternalValue(last_attach_time);
    284   out_gallery_info->volume_metadata_valid = volume_metadata_valid;
    285   out_gallery_info->prefs_version = prefs_version;
    286   out_gallery_info->default_gallery_type = GetDefaultGalleryType(dict);
    287   return true;
    288 }
    289 
    290 base::DictionaryValue* CreateGalleryPrefInfoDictionary(
    291     const MediaGalleryPrefInfo& gallery) {
    292   base::DictionaryValue* dict = new base::DictionaryValue();
    293   dict->SetString(kMediaGalleriesPrefIdKey,
    294                   base::Uint64ToString(gallery.pref_id));
    295   dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
    296   dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
    297   dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(gallery.type));
    298 
    299   if (gallery.default_gallery_type != MediaGalleryPrefInfo::kNotDefault) {
    300     dict->SetString(kMediaGalleriesDefaultGalleryTypeKey,
    301                     DefaultGalleryTypeToStringValue(
    302                         gallery.default_gallery_type));
    303   }
    304 
    305   if (gallery.volume_metadata_valid) {
    306     dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
    307     dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
    308     dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
    309     dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
    310     dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
    311                     gallery.last_attach_time.ToInternalValue());
    312   } else {
    313     dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
    314   }
    315 
    316   if (gallery.audio_count || gallery.image_count || gallery.video_count) {
    317     dict->SetInteger(kMediaGalleriesScanAudioCountKey, gallery.audio_count);
    318     dict->SetInteger(kMediaGalleriesScanImageCountKey, gallery.image_count);
    319     dict->SetInteger(kMediaGalleriesScanVideoCountKey, gallery.video_count);
    320   }
    321 
    322   // Version 0 of the prefs format was that the display_name was always
    323   // used to show the user-visible name of the gallery. Version 1 means
    324   // that there is an optional display_name, and when it is present, it
    325   // overrides the name that would be built from the volume metadata, path,
    326   // or whatever other data. So if we see a display_name with version 0, it
    327   // means it may be overwritten simply by getting new volume metadata.
    328   // A display_name with version 1 should not be overwritten.
    329   dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
    330 
    331   return dict;
    332 }
    333 
    334 bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
    335   extensions::MediaGalleriesPermission::CheckParam param(
    336       extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
    337   return extension.permissions_data()->CheckAPIPermissionWithParam(
    338       extensions::APIPermission::kMediaGalleries, &param);
    339 }
    340 
    341 // Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
    342 // failure.
    343 bool GetMediaGalleryPermissionFromDictionary(
    344     const base::DictionaryValue* dict,
    345     MediaGalleryPermission* out_permission) {
    346   std::string string_id;
    347   if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
    348       base::StringToUint64(string_id, &out_permission->pref_id) &&
    349       dict->GetBoolean(kMediaGalleryHasPermissionKey,
    350                        &out_permission->has_permission)) {
    351     return true;
    352   }
    353   NOTREACHED();
    354   return false;
    355 }
    356 
    357 // For a device with |device_name| and a relative path |sub_folder|, construct
    358 // a display name. If |sub_folder| is empty, then just return |device_name|.
    359 base::string16 GetDisplayNameForSubFolder(const base::string16& device_name,
    360                                           const base::FilePath& sub_folder) {
    361   if (sub_folder.empty())
    362     return device_name;
    363   return (sub_folder.BaseName().LossyDisplayName() +
    364           base::ASCIIToUTF16(" - ") +
    365           device_name);
    366 }
    367 
    368 void InitializeImportedMediaGalleryRegistryOnFileThread() {
    369   DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
    370   ImportedMediaGalleryRegistry::GetInstance()->Initialize();
    371 }
    372 
    373 }  // namespace
    374 
    375 MediaGalleryPrefInfo::MediaGalleryPrefInfo()
    376     : pref_id(kInvalidMediaGalleryPrefId),
    377       type(kInvalidType),
    378       total_size_in_bytes(0),
    379       volume_metadata_valid(false),
    380       audio_count(0),
    381       image_count(0),
    382       video_count(0),
    383       default_gallery_type(kNotDefault),
    384       prefs_version(0) {
    385 }
    386 
    387 MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
    388 
    389 base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
    390   base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
    391   DCHECK(!path.IsAbsolute());
    392   return base_path.empty() ? base_path : base_path.Append(path);
    393 }
    394 
    395 bool MediaGalleryPrefInfo::IsBlackListedType() const {
    396   return type == kBlackListed || type == kRemovedScan;
    397 }
    398 
    399 base::string16 MediaGalleryPrefInfo::GetGalleryDisplayName() const {
    400   if (!StorageInfo::IsRemovableDevice(device_id)) {
    401     // For fixed storage, the default name is the fully qualified directory
    402     // name, or in the case of a root directory, the root directory name.
    403     // Exception: ChromeOS -- the full pathname isn't visible there, so only
    404     // the directory name is used.
    405     base::FilePath path = AbsolutePath();
    406     if (!display_name.empty())
    407       return display_name;
    408 
    409 #if defined(OS_CHROMEOS)
    410     // See chrome/browser/chromeos/fileapi/file_system_backend.cc
    411     base::FilePath download_path;
    412     if (PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS_SAFE, &download_path)) {
    413       base::FilePath relative;
    414       if (download_path.AppendRelativePath(path, &relative))
    415         return relative.LossyDisplayName();
    416     }
    417     return path.BaseName().LossyDisplayName();
    418 #else
    419     return path.LossyDisplayName();
    420 #endif
    421   }
    422 
    423   StorageInfo info(device_id,
    424                    MediaStorageUtil::FindDevicePathById(device_id).value(),
    425                    volume_label, vendor_name, model_name, total_size_in_bytes);
    426   base::string16 name = info.GetDisplayNameWithOverride(display_name, true);
    427   if (!path.empty())
    428     name = GetDisplayNameForSubFolder(name, path);
    429   return name;
    430 }
    431 
    432 base::string16 MediaGalleryPrefInfo::GetGalleryTooltip() const {
    433   return AbsolutePath().LossyDisplayName();
    434 }
    435 
    436 base::string16 MediaGalleryPrefInfo::GetGalleryAdditionalDetails() const {
    437   base::string16 attached;
    438   if (StorageInfo::IsRemovableDevice(device_id)) {
    439     if (MediaStorageUtil::IsRemovableStorageAttached(device_id)) {
    440       attached = l10n_util::GetStringUTF16(
    441           IDS_MEDIA_GALLERIES_DIALOG_DEVICE_ATTACHED);
    442     } else if (!last_attach_time.is_null()) {
    443       attached = l10n_util::GetStringFUTF16(
    444           IDS_MEDIA_GALLERIES_LAST_ATTACHED,
    445           base::TimeFormatShortDateNumeric(last_attach_time));
    446     } else {
    447       attached = l10n_util::GetStringUTF16(
    448           IDS_MEDIA_GALLERIES_DIALOG_DEVICE_NOT_ATTACHED);
    449     }
    450   }
    451 
    452   return attached;
    453 }
    454 
    455 bool MediaGalleryPrefInfo::IsGalleryAvailable() const {
    456   return !StorageInfo::IsRemovableDevice(device_id) ||
    457          MediaStorageUtil::IsRemovableStorageAttached(device_id);
    458 }
    459 
    460 MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
    461 
    462 MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
    463     : initialized_(false),
    464       pre_initialization_callbacks_waiting_(0),
    465       profile_(profile),
    466       extension_prefs_for_testing_(NULL),
    467       weak_factory_(this) {
    468 }
    469 
    470 MediaGalleriesPreferences::~MediaGalleriesPreferences() {
    471   if (StorageMonitor::GetInstance())
    472     StorageMonitor::GetInstance()->RemoveObserver(this);
    473 }
    474 
    475 void MediaGalleriesPreferences::EnsureInitialized(base::Closure callback) {
    476   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    477 
    478   if (IsInitialized()) {
    479     if (!callback.is_null())
    480       callback.Run();
    481     return;
    482   }
    483 
    484   on_initialize_callbacks_.push_back(callback);
    485   if (on_initialize_callbacks_.size() > 1)
    486     return;
    487 
    488   // This counter must match the number of async methods dispatched below.
    489   // It cannot be incremented inline with each callback, as some may return
    490   // synchronously, decrement the counter to 0, and prematurely trigger
    491   // FinishInitialization.
    492   pre_initialization_callbacks_waiting_ = 4;
    493 
    494   // Check whether we should be initializing -- are there any extensions that
    495   // are using media galleries?
    496   media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED);
    497   if (NumberExtensionsUsingMediaGalleries(profile_) == 0) {
    498     media_galleries::UsageCount(media_galleries::PREFS_INITIALIZED_ERROR);
    499   }
    500 
    501   // We determine the freshness of the profile here, before any of the finders
    502   // return and add media galleries to it (hence why the APIHasBeenUsed check
    503   // needs to happen here rather than inside OnStorageMonitorInit itself).
    504   StorageMonitor::GetInstance()->EnsureInitialized(
    505       base::Bind(&MediaGalleriesPreferences::OnStorageMonitorInit,
    506                  weak_factory_.GetWeakPtr(),
    507                  APIHasBeenUsed(profile_)));
    508 
    509   // Look for optional default galleries every time.
    510   iapps::FindITunesLibrary(
    511       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
    512                  weak_factory_.GetWeakPtr()));
    513 
    514   picasa::FindPicasaDatabase(
    515       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
    516                  weak_factory_.GetWeakPtr()));
    517 
    518   iapps::FindIPhotoLibrary(
    519       base::Bind(&MediaGalleriesPreferences::OnFinderDeviceID,
    520                  weak_factory_.GetWeakPtr()));
    521 }
    522 
    523 bool MediaGalleriesPreferences::IsInitialized() const { return initialized_; }
    524 
    525 Profile* MediaGalleriesPreferences::profile() { return profile_; }
    526 
    527 void MediaGalleriesPreferences::OnInitializationCallbackReturned() {
    528   DCHECK(!IsInitialized());
    529   DCHECK_GT(pre_initialization_callbacks_waiting_, 0);
    530   if (--pre_initialization_callbacks_waiting_ == 0)
    531     FinishInitialization();
    532 }
    533 
    534 void MediaGalleriesPreferences::FinishInitialization() {
    535   DCHECK(!IsInitialized());
    536 
    537   initialized_ = true;
    538 
    539   StorageMonitor* monitor = StorageMonitor::GetInstance();
    540   DCHECK(monitor->IsInitialized());
    541 
    542   InitFromPrefs();
    543 
    544   StorageMonitor::GetInstance()->AddObserver(this);
    545 
    546   std::vector<StorageInfo> existing_devices =
    547       monitor->GetAllAvailableStorages();
    548   for (size_t i = 0; i < existing_devices.size(); i++) {
    549     if (!(StorageInfo::IsMediaDevice(existing_devices[i].device_id()) &&
    550           StorageInfo::IsRemovableDevice(existing_devices[i].device_id())))
    551       continue;
    552     AddGallery(existing_devices[i].device_id(),
    553                base::FilePath(),
    554                MediaGalleryPrefInfo::kAutoDetected,
    555                existing_devices[i].storage_label(),
    556                existing_devices[i].vendor_name(),
    557                existing_devices[i].model_name(),
    558                existing_devices[i].total_size_in_bytes(),
    559                base::Time::Now(), 0, 0, 0);
    560   }
    561 
    562   for (std::vector<base::Closure>::iterator iter =
    563            on_initialize_callbacks_.begin();
    564        iter != on_initialize_callbacks_.end();
    565        ++iter) {
    566     iter->Run();
    567   }
    568   on_initialize_callbacks_.clear();
    569 }
    570 
    571 void MediaGalleriesPreferences::AddDefaultGalleries() {
    572   const struct DefaultTypes {
    573     int directory_key;
    574     MediaGalleryPrefInfo::DefaultGalleryType default_gallery_type;
    575   } kDirectories[] = {
    576     {chrome::DIR_USER_MUSIC, MediaGalleryPrefInfo::kMusicDefault},
    577     {chrome::DIR_USER_PICTURES, MediaGalleryPrefInfo::kPicturesDefault},
    578     {chrome::DIR_USER_VIDEOS, MediaGalleryPrefInfo::kVideosDefault},
    579   };
    580 
    581   for (size_t i = 0; i < arraysize(kDirectories); ++i) {
    582     base::FilePath path;
    583     if (!PathService::Get(kDirectories[i].directory_key, &path))
    584       continue;
    585 
    586     base::FilePath relative_path;
    587     StorageInfo info;
    588     if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
    589       MediaGalleryPrefInfo::DefaultGalleryType default_gallery_type =
    590           kDirectories[i].default_gallery_type;
    591       DCHECK_NE(default_gallery_type, MediaGalleryPrefInfo::kNotDefault);
    592 
    593       AddOrUpdateGalleryInternal(
    594           info.device_id(),
    595           base::string16(),
    596           relative_path,
    597           MediaGalleryPrefInfo::kAutoDetected,
    598           info.storage_label(),
    599           info.vendor_name(),
    600           info.model_name(),
    601           info.total_size_in_bytes(),
    602           base::Time(),
    603           true,
    604           0,
    605           0,
    606           0,
    607           kCurrentPrefsVersion,
    608           default_gallery_type);
    609     }
    610   }
    611 }
    612 
    613 bool MediaGalleriesPreferences::UpdateDeviceIDForSingletonType(
    614     const std::string& device_id) {
    615   StorageInfo::Type singleton_type;
    616   if (!StorageInfo::CrackDeviceId(device_id, &singleton_type, NULL))
    617     return false;
    618 
    619   PrefService* prefs = profile_->GetPrefs();
    620   scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
    621       prefs, prefs::kMediaGalleriesRememberedGalleries));
    622   base::ListValue* list = update->Get();
    623   for (base::ListValue::iterator iter = list->begin();
    624        iter != list->end(); ++iter) {
    625     // All of these calls should succeed, but preferences file can be corrupt.
    626     base::DictionaryValue* dict;
    627     if (!(*iter)->GetAsDictionary(&dict))
    628       continue;
    629     std::string this_device_id;
    630     if (!dict->GetString(kMediaGalleriesDeviceIdKey, &this_device_id))
    631       continue;
    632     if (this_device_id == device_id)
    633       return true;  // No update is necessary.
    634     StorageInfo::Type device_type;
    635     if (!StorageInfo::CrackDeviceId(this_device_id, &device_type, NULL))
    636       continue;
    637 
    638     if (device_type == singleton_type) {
    639       dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
    640       update.reset();  // commits the update.
    641       InitFromPrefs();
    642       MediaGalleryPrefId pref_id;
    643       if (GetPrefId(*dict, &pref_id)) {
    644         FOR_EACH_OBSERVER(GalleryChangeObserver,
    645                           gallery_change_observers_,
    646                           OnGalleryInfoUpdated(this, pref_id));
    647       }
    648       return true;
    649     }
    650   }
    651   return false;
    652 }
    653 
    654 void MediaGalleriesPreferences::OnStorageMonitorInit(
    655     bool api_has_been_used) {
    656   if (api_has_been_used)
    657     UpdateDefaultGalleriesPaths();
    658 
    659   // Invoke this method even if the API has been used before, in order to ensure
    660   // we upgrade (migrate) prefs for galleries with prefs version prior to 3.
    661   AddDefaultGalleries();
    662 
    663   OnInitializationCallbackReturned();
    664 }
    665 
    666 void MediaGalleriesPreferences::OnFinderDeviceID(const std::string& device_id) {
    667   if (!device_id.empty()) {
    668     std::string gallery_name;
    669     if (StorageInfo::IsIPhotoDevice(device_id))
    670       gallery_name = kIPhotoGalleryName;
    671     else if (StorageInfo::IsITunesDevice(device_id))
    672       gallery_name = kITunesGalleryName;
    673     else if (StorageInfo::IsPicasaDevice(device_id))
    674       gallery_name = kPicasaGalleryName;
    675 
    676     if (!gallery_name.empty()) {
    677       pre_initialization_callbacks_waiting_++;
    678       content::BrowserThread::PostTaskAndReply(
    679           content::BrowserThread::FILE,
    680           FROM_HERE,
    681           base::Bind(&InitializeImportedMediaGalleryRegistryOnFileThread),
    682           base::Bind(
    683               &MediaGalleriesPreferences::OnInitializationCallbackReturned,
    684               weak_factory_.GetWeakPtr()));
    685     }
    686 
    687     if (!UpdateDeviceIDForSingletonType(device_id)) {
    688       DCHECK(!gallery_name.empty());
    689       AddOrUpdateGalleryInternal(
    690           device_id,
    691           base::ASCIIToUTF16(gallery_name),
    692           base::FilePath(),
    693           MediaGalleryPrefInfo::kAutoDetected,
    694           base::string16(),
    695           base::string16(),
    696           base::string16(),
    697           0,
    698           base::Time(),
    699           false,
    700           0,
    701           0,
    702           0,
    703           kCurrentPrefsVersion,
    704           MediaGalleryPrefInfo::kNotDefault);
    705     }
    706   }
    707 
    708   OnInitializationCallbackReturned();
    709 }
    710 
    711 void MediaGalleriesPreferences::InitFromPrefs() {
    712   known_galleries_.clear();
    713   device_map_.clear();
    714 
    715   PrefService* prefs = profile_->GetPrefs();
    716   const base::ListValue* list = prefs->GetList(
    717       prefs::kMediaGalleriesRememberedGalleries);
    718   if (list) {
    719     for (base::ListValue::const_iterator it = list->begin();
    720          it != list->end(); ++it) {
    721       const base::DictionaryValue* dict = NULL;
    722       if (!(*it)->GetAsDictionary(&dict))
    723         continue;
    724 
    725       MediaGalleryPrefInfo gallery_info;
    726       if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
    727         continue;
    728 
    729       known_galleries_[gallery_info.pref_id] = gallery_info;
    730       device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
    731     }
    732   }
    733 }
    734 
    735 void MediaGalleriesPreferences::AddGalleryChangeObserver(
    736     GalleryChangeObserver* observer) {
    737   DCHECK(IsInitialized());
    738   gallery_change_observers_.AddObserver(observer);
    739 }
    740 
    741 void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
    742     GalleryChangeObserver* observer) {
    743   DCHECK(IsInitialized());
    744   gallery_change_observers_.RemoveObserver(observer);
    745 }
    746 
    747 void MediaGalleriesPreferences::OnRemovableStorageAttached(
    748     const StorageInfo& info) {
    749   DCHECK(IsInitialized());
    750   if (!StorageInfo::IsMediaDevice(info.device_id()))
    751     return;
    752 
    753   AddGallery(info.device_id(), base::FilePath(),
    754              MediaGalleryPrefInfo::kAutoDetected, info.storage_label(),
    755              info.vendor_name(), info.model_name(), info.total_size_in_bytes(),
    756              base::Time::Now(), 0, 0, 0);
    757 }
    758 
    759 bool MediaGalleriesPreferences::LookUpGalleryByPath(
    760     const base::FilePath& path,
    761     MediaGalleryPrefInfo* gallery_info) const {
    762   DCHECK(IsInitialized());
    763 
    764   // First check if the path matches an imported gallery.
    765   for (MediaGalleriesPrefInfoMap::const_iterator it =
    766            known_galleries_.begin(); it != known_galleries_.end(); ++it) {
    767     const std::string& device_id = it->second.device_id;
    768     if (iapps::PathIndicatesIPhotoLibrary(device_id, path) ||
    769         iapps::PathIndicatesITunesLibrary(device_id, path)) {
    770       *gallery_info = it->second;
    771       return true;
    772     }
    773   }
    774 
    775   StorageInfo info;
    776   base::FilePath relative_path;
    777   if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
    778     if (gallery_info)
    779       *gallery_info = MediaGalleryPrefInfo();
    780     return false;
    781   }
    782 
    783   relative_path = relative_path.NormalizePathSeparators();
    784   MediaGalleryPrefIdSet galleries_on_device =
    785       LookUpGalleriesByDeviceId(info.device_id());
    786   for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
    787        it != galleries_on_device.end();
    788        ++it) {
    789     const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
    790     if (gallery.path != relative_path)
    791       continue;
    792 
    793     if (gallery_info)
    794       *gallery_info = gallery;
    795     return true;
    796   }
    797 
    798   // This method is called by controller::FilesSelected when the user
    799   // adds a new gallery. Control reaches here when the selected gallery is
    800   // on a volume we know about, but have no gallery already for. Returns
    801   // hypothetical data to the caller about what the prefs will look like
    802   // if the gallery is added.
    803   // TODO(gbillock): split this out into another function so it doesn't
    804   // conflate LookUp.
    805   if (gallery_info) {
    806     gallery_info->pref_id = kInvalidMediaGalleryPrefId;
    807     gallery_info->device_id = info.device_id();
    808     gallery_info->path = relative_path;
    809     gallery_info->type = MediaGalleryPrefInfo::kInvalidType;
    810     gallery_info->volume_label = info.storage_label();
    811     gallery_info->vendor_name = info.vendor_name();
    812     gallery_info->model_name = info.model_name();
    813     gallery_info->total_size_in_bytes = info.total_size_in_bytes();
    814     gallery_info->last_attach_time = base::Time::Now();
    815     gallery_info->volume_metadata_valid = true;
    816     gallery_info->prefs_version = kCurrentPrefsVersion;
    817   }
    818   return false;
    819 }
    820 
    821 MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
    822     const std::string& device_id) const {
    823   DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
    824   if (found == device_map_.end())
    825     return MediaGalleryPrefIdSet();
    826   return found->second;
    827 }
    828 
    829 base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
    830     MediaGalleryPrefId gallery_id,
    831     const extensions::Extension* extension,
    832     bool include_unpermitted_galleries) {
    833   DCHECK(IsInitialized());
    834   DCHECK(extension);
    835   if (!include_unpermitted_galleries &&
    836       !ContainsKey(GalleriesForExtension(*extension), gallery_id))
    837     return base::FilePath();
    838 
    839   MediaGalleriesPrefInfoMap::const_iterator it =
    840       known_galleries_.find(gallery_id);
    841   if (it == known_galleries_.end())
    842     return base::FilePath();
    843   return MediaStorageUtil::FindDevicePathById(it->second.device_id);
    844 }
    845 
    846 MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
    847     const std::string& device_id,
    848     const base::FilePath& relative_path,
    849     MediaGalleryPrefInfo::Type type,
    850     const base::string16& volume_label,
    851     const base::string16& vendor_name,
    852     const base::string16& model_name,
    853     uint64 total_size_in_bytes,
    854     base::Time last_attach_time,
    855     int audio_count,
    856     int image_count,
    857     int video_count) {
    858   DCHECK(IsInitialized());
    859   return AddOrUpdateGalleryInternal(
    860       device_id,
    861       base::string16(),
    862       relative_path,
    863       type,
    864       volume_label,
    865       vendor_name,
    866       model_name,
    867       total_size_in_bytes,
    868       last_attach_time,
    869       true,
    870       audio_count,
    871       image_count,
    872       video_count,
    873       kCurrentPrefsVersion,
    874       MediaGalleryPrefInfo::kNotDefault);
    875 }
    876 
    877 MediaGalleryPrefId MediaGalleriesPreferences::AddOrUpdateGalleryInternal(
    878     const std::string& device_id, const base::string16& display_name,
    879     const base::FilePath& relative_path, MediaGalleryPrefInfo::Type type,
    880     const base::string16& volume_label, const base::string16& vendor_name,
    881     const base::string16& model_name, uint64 total_size_in_bytes,
    882     base::Time last_attach_time, bool volume_metadata_valid,
    883     int audio_count, int image_count, int video_count, int prefs_version,
    884     MediaGalleryPrefInfo::DefaultGalleryType default_gallery_type) {
    885   DCHECK(type == MediaGalleryPrefInfo::kUserAdded ||
    886          type == MediaGalleryPrefInfo::kAutoDetected ||
    887          type == MediaGalleryPrefInfo::kScanResult);
    888   base::FilePath normalized_relative_path =
    889       relative_path.NormalizePathSeparators();
    890   MediaGalleryPrefIdSet galleries_on_device =
    891     LookUpGalleriesByDeviceId(device_id);
    892 
    893   for (MediaGalleryPrefIdSet::const_iterator pref_id_it =
    894            galleries_on_device.begin();
    895        pref_id_it != galleries_on_device.end();
    896        ++pref_id_it) {
    897     const MediaGalleryPrefInfo& existing =
    898         known_galleries_.find(*pref_id_it)->second;
    899     if (existing.path != normalized_relative_path)
    900       continue;
    901 
    902     bool update_gallery_type = false;
    903     MediaGalleryPrefInfo::Type new_type = existing.type;
    904     if (type == MediaGalleryPrefInfo::kUserAdded) {
    905       if (existing.type == MediaGalleryPrefInfo::kBlackListed) {
    906         new_type = MediaGalleryPrefInfo::kAutoDetected;
    907         update_gallery_type = true;
    908       }
    909       if (existing.type == MediaGalleryPrefInfo::kRemovedScan) {
    910         new_type = MediaGalleryPrefInfo::kUserAdded;
    911         update_gallery_type = true;
    912       }
    913     }
    914 
    915     // Status quo: In M27 and M28, galleries added manually use version 0,
    916     // and galleries added automatically (including default galleries) use
    917     // version 1. The name override is used by default galleries as well
    918     // as all device attach events.
    919     // We want to upgrade the name if the existing version is < 2. Leave it
    920     // alone if the existing display name is set with version >= 2 and the
    921     // proposed new name is empty.
    922     bool update_gallery_name = existing.display_name != display_name;
    923     if (existing.prefs_version >= 2 && !existing.display_name.empty() &&
    924         display_name.empty()) {
    925       update_gallery_name = false;
    926     }
    927 
    928     // Version 3 adds the default_gallery_type field.
    929     bool update_default_gallery_type =
    930          existing.prefs_version <= 2 &&
    931          default_gallery_type != existing.default_gallery_type;
    932 
    933     bool update_gallery_metadata = volume_metadata_valid &&
    934         ((existing.volume_label != volume_label) ||
    935          (existing.vendor_name != vendor_name) ||
    936          (existing.model_name != model_name) ||
    937          (existing.total_size_in_bytes != total_size_in_bytes) ||
    938          (existing.last_attach_time != last_attach_time));
    939 
    940     bool update_scan_counts =
    941       new_type != MediaGalleryPrefInfo::kRemovedScan &&
    942       new_type != MediaGalleryPrefInfo::kBlackListed &&
    943       (audio_count > 0 || image_count > 0 || video_count > 0 ||
    944        existing.audio_count || existing.image_count || existing.video_count);
    945 
    946     if (!update_gallery_name && !update_gallery_type &&
    947         !update_gallery_metadata && !update_scan_counts &&
    948         !update_default_gallery_type)
    949       return *pref_id_it;
    950 
    951     PrefService* prefs = profile_->GetPrefs();
    952     scoped_ptr<ListPrefUpdate> update(
    953         new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
    954     base::ListValue* list = update->Get();
    955 
    956     for (base::ListValue::const_iterator list_iter = list->begin();
    957          list_iter != list->end();
    958          ++list_iter) {
    959       base::DictionaryValue* dict;
    960       MediaGalleryPrefId iter_id;
    961       if ((*list_iter)->GetAsDictionary(&dict) &&
    962           GetPrefId(*dict, &iter_id) &&
    963           *pref_id_it == iter_id) {
    964         if (update_gallery_type)
    965           dict->SetString(kMediaGalleriesTypeKey, TypeToStringValue(new_type));
    966         if (update_gallery_name)
    967           dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
    968         if (update_gallery_metadata) {
    969           dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
    970           dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
    971           dict->SetString(kMediaGalleriesModelNameKey, model_name);
    972           dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
    973           dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
    974                           last_attach_time.ToInternalValue());
    975         }
    976         if (update_scan_counts) {
    977           dict->SetInteger(kMediaGalleriesScanAudioCountKey, audio_count);
    978           dict->SetInteger(kMediaGalleriesScanImageCountKey, image_count);
    979           dict->SetInteger(kMediaGalleriesScanVideoCountKey, video_count);
    980         }
    981         if (update_default_gallery_type) {
    982           dict->SetString(
    983               kMediaGalleriesDefaultGalleryTypeKey,
    984               DefaultGalleryTypeToStringValue(default_gallery_type));
    985         }
    986         dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
    987         break;
    988       }
    989     }
    990 
    991     // Commits the prefs update.
    992     update.reset();
    993 
    994     InitFromPrefs();
    995     FOR_EACH_OBSERVER(GalleryChangeObserver, gallery_change_observers_,
    996                       OnGalleryInfoUpdated(this, *pref_id_it));
    997     return *pref_id_it;
    998   }
    999 
   1000   PrefService* prefs = profile_->GetPrefs();
   1001 
   1002   MediaGalleryPrefInfo gallery_info;
   1003   gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
   1004   prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
   1005   gallery_info.display_name = display_name;
   1006   gallery_info.device_id = device_id;
   1007   gallery_info.path = normalized_relative_path;
   1008   gallery_info.type = type;
   1009   gallery_info.volume_label = volume_label;
   1010   gallery_info.vendor_name = vendor_name;
   1011   gallery_info.model_name = model_name;
   1012   gallery_info.total_size_in_bytes = total_size_in_bytes;
   1013   gallery_info.last_attach_time = last_attach_time;
   1014   gallery_info.volume_metadata_valid = volume_metadata_valid;
   1015   gallery_info.audio_count = audio_count;
   1016   gallery_info.image_count = image_count;
   1017   gallery_info.video_count = video_count;
   1018   gallery_info.prefs_version = prefs_version;
   1019   gallery_info.default_gallery_type = default_gallery_type;
   1020 
   1021   {
   1022     ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
   1023     base::ListValue* list = update.Get();
   1024     list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
   1025   }
   1026   InitFromPrefs();
   1027   FOR_EACH_OBSERVER(GalleryChangeObserver,
   1028                     gallery_change_observers_,
   1029                     OnGalleryAdded(this, gallery_info.pref_id));
   1030 
   1031   return gallery_info.pref_id;
   1032 }
   1033 
   1034 
   1035 void MediaGalleriesPreferences::UpdateDefaultGalleriesPaths() {
   1036   base::FilePath music_path;
   1037   base::FilePath pictures_path;
   1038   base::FilePath videos_path;
   1039   bool got_music_path = PathService::Get(chrome::DIR_USER_MUSIC, &music_path);
   1040   bool got_pictures_path =
   1041       PathService::Get(chrome::DIR_USER_PICTURES, &pictures_path);
   1042   bool got_videos_path =
   1043       PathService::Get(chrome::DIR_USER_VIDEOS, &videos_path);
   1044 
   1045   PrefService* prefs = profile_->GetPrefs();
   1046   scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
   1047       prefs, prefs::kMediaGalleriesRememberedGalleries));
   1048   base::ListValue* list = update->Get();
   1049 
   1050   std::vector<MediaGalleryPrefId> pref_ids;
   1051 
   1052   for (base::ListValue::iterator iter = list->begin();
   1053        iter != list->end();
   1054        ++iter) {
   1055     base::DictionaryValue* dict;
   1056     MediaGalleryPrefId pref_id;
   1057 
   1058     if (!((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &pref_id)))
   1059       continue;
   1060 
   1061     std::string default_gallery_type_string;
   1062 
   1063     // If the "default gallery type" key is set, just update the paths in place.
   1064     // If it's not set, then AddOrUpdateGalleryInternal will take care of
   1065     // setting it as part of migration to prefs version 3.
   1066     if (dict->GetString(kMediaGalleriesDefaultGalleryTypeKey,
   1067                         &default_gallery_type_string)) {
   1068       std::string device_id;
   1069       if (got_music_path &&
   1070           default_gallery_type_string ==
   1071               kMediaGalleriesDefaultGalleryTypeMusicDefaultValue) {
   1072         device_id = StorageInfo::MakeDeviceId(
   1073             StorageInfo::Type::FIXED_MASS_STORAGE,
   1074             music_path.AsUTF8Unsafe());
   1075       } else if (got_pictures_path &&
   1076                  default_gallery_type_string ==
   1077                      kMediaGalleriesDefaultGalleryTypePicturesDefaultValue) {
   1078         device_id = StorageInfo::MakeDeviceId(
   1079             StorageInfo::Type::FIXED_MASS_STORAGE,
   1080             pictures_path.AsUTF8Unsafe());
   1081       } else if (got_videos_path &&
   1082                  default_gallery_type_string ==
   1083                      kMediaGalleriesDefaultGalleryTypeVideosDefaultValue) {
   1084         device_id = StorageInfo::MakeDeviceId(
   1085             StorageInfo::Type::FIXED_MASS_STORAGE,
   1086             videos_path.AsUTF8Unsafe());
   1087       }
   1088 
   1089       if (!device_id.empty())
   1090         dict->SetString(kMediaGalleriesDeviceIdKey, device_id);
   1091     }
   1092 
   1093     pref_ids.push_back(pref_id);
   1094   }
   1095 
   1096   // Commit the prefs update.
   1097   update.reset();
   1098   InitFromPrefs();
   1099 
   1100   for (std::vector<MediaGalleryPrefId>::iterator iter = pref_ids.begin();
   1101        iter != pref_ids.end();
   1102        ++iter) {
   1103     FOR_EACH_OBSERVER(GalleryChangeObserver,
   1104                       gallery_change_observers_,
   1105                       OnGalleryInfoUpdated(this, *iter));
   1106   }
   1107 }
   1108 
   1109 
   1110 MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
   1111     const base::FilePath& path, MediaGalleryPrefInfo::Type type) {
   1112   DCHECK(IsInitialized());
   1113   MediaGalleryPrefInfo gallery_info;
   1114   if (LookUpGalleryByPath(path, &gallery_info) &&
   1115       !gallery_info.IsBlackListedType()) {
   1116     return gallery_info.pref_id;
   1117   }
   1118   return AddOrUpdateGalleryInternal(gallery_info.device_id,
   1119                             gallery_info.display_name,
   1120                             gallery_info.path,
   1121                             type,
   1122                             gallery_info.volume_label,
   1123                             gallery_info.vendor_name,
   1124                             gallery_info.model_name,
   1125                             gallery_info.total_size_in_bytes,
   1126                             gallery_info.last_attach_time,
   1127                             gallery_info.volume_metadata_valid,
   1128                             0, 0, 0,
   1129                             kCurrentPrefsVersion,
   1130                             MediaGalleryPrefInfo::kNotDefault);
   1131 }
   1132 
   1133 void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId id) {
   1134   EraseOrBlacklistGalleryById(id, false);
   1135 }
   1136 
   1137 void MediaGalleriesPreferences::EraseGalleryById(MediaGalleryPrefId id) {
   1138   EraseOrBlacklistGalleryById(id, true);
   1139 }
   1140 
   1141 void MediaGalleriesPreferences::EraseOrBlacklistGalleryById(
   1142     MediaGalleryPrefId id, bool erase) {
   1143   DCHECK(IsInitialized());
   1144   PrefService* prefs = profile_->GetPrefs();
   1145   scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
   1146       prefs, prefs::kMediaGalleriesRememberedGalleries));
   1147   base::ListValue* list = update->Get();
   1148 
   1149   if (!ContainsKey(known_galleries_, id))
   1150     return;
   1151 
   1152   for (base::ListValue::iterator iter = list->begin();
   1153        iter != list->end(); ++iter) {
   1154     base::DictionaryValue* dict;
   1155     MediaGalleryPrefId iter_id;
   1156     if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
   1157         id == iter_id) {
   1158       RemoveGalleryPermissionsFromPrefs(id);
   1159       MediaGalleryPrefInfo::Type type;
   1160       if (!erase && GetType(*dict, &type) &&
   1161           (type == MediaGalleryPrefInfo::kAutoDetected ||
   1162            type == MediaGalleryPrefInfo::kScanResult)) {
   1163         if (type == MediaGalleryPrefInfo::kAutoDetected) {
   1164           dict->SetString(kMediaGalleriesTypeKey,
   1165                           kMediaGalleriesTypeBlackListedValue);
   1166         } else {
   1167           dict->SetString(kMediaGalleriesTypeKey,
   1168                           kMediaGalleriesTypeRemovedScanValue);
   1169           dict->SetInteger(kMediaGalleriesScanAudioCountKey, 0);
   1170           dict->SetInteger(kMediaGalleriesScanImageCountKey, 0);
   1171           dict->SetInteger(kMediaGalleriesScanVideoCountKey, 0);
   1172         }
   1173       } else {
   1174         list->Erase(iter, NULL);
   1175       }
   1176       update.reset(NULL);  // commits the update.
   1177 
   1178       InitFromPrefs();
   1179       FOR_EACH_OBSERVER(GalleryChangeObserver,
   1180                         gallery_change_observers_,
   1181                         OnGalleryRemoved(this, id));
   1182       return;
   1183     }
   1184   }
   1185 }
   1186 
   1187 bool MediaGalleriesPreferences::NonAutoGalleryHasPermission(
   1188     MediaGalleryPrefId id) const {
   1189   DCHECK(IsInitialized());
   1190   DCHECK(!ContainsKey(known_galleries_, id) ||
   1191          known_galleries_.find(id)->second.type !=
   1192              MediaGalleryPrefInfo::kAutoDetected);
   1193   ExtensionPrefs* prefs = GetExtensionPrefs();
   1194   const base::DictionaryValue* extensions =
   1195       prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
   1196   if (!extensions)
   1197     return true;
   1198 
   1199   for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
   1200        iter.Advance()) {
   1201     if (!crx_file::id_util::IdIsValid(iter.key())) {
   1202       NOTREACHED();
   1203       continue;
   1204     }
   1205     std::vector<MediaGalleryPermission> permissions =
   1206         GetGalleryPermissionsFromPrefs(iter.key());
   1207     for (std::vector<MediaGalleryPermission>::const_iterator it =
   1208              permissions.begin(); it != permissions.end(); ++it) {
   1209       if (it->pref_id == id) {
   1210         if (it->has_permission)
   1211           return true;
   1212         break;
   1213       }
   1214     }
   1215   }
   1216   return false;
   1217 }
   1218 
   1219 MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
   1220     const extensions::Extension& extension) {
   1221   DCHECK(IsInitialized());
   1222   MediaGalleryPrefIdSet result;
   1223 
   1224   if (HasAutoDetectedGalleryPermission(extension)) {
   1225     for (MediaGalleriesPrefInfoMap::const_iterator it =
   1226              known_galleries_.begin(); it != known_galleries_.end(); ++it) {
   1227       if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
   1228         result.insert(it->second.pref_id);
   1229     }
   1230   }
   1231 
   1232   std::vector<MediaGalleryPermission> stored_permissions =
   1233       GetGalleryPermissionsFromPrefs(extension.id());
   1234   for (std::vector<MediaGalleryPermission>::const_iterator it =
   1235            stored_permissions.begin(); it != stored_permissions.end(); ++it) {
   1236     if (!it->has_permission) {
   1237       result.erase(it->pref_id);
   1238     } else {
   1239       MediaGalleriesPrefInfoMap::const_iterator gallery =
   1240           known_galleries_.find(it->pref_id);
   1241 
   1242       // Handle a stored permission for an erased gallery. This should never
   1243       // happen but, has caused crashes in the wild. http://crbug.com/374330.
   1244       if (gallery == known_galleries_.end()) {
   1245         RemoveGalleryPermissionsFromPrefs(it->pref_id);
   1246         continue;
   1247       }
   1248 
   1249       if (!gallery->second.IsBlackListedType()) {
   1250         result.insert(it->pref_id);
   1251       } else {
   1252         NOTREACHED() << gallery->second.device_id;
   1253       }
   1254     }
   1255   }
   1256   return result;
   1257 }
   1258 
   1259 bool MediaGalleriesPreferences::SetGalleryPermissionForExtension(
   1260     const extensions::Extension& extension,
   1261     MediaGalleryPrefId pref_id,
   1262     bool has_permission) {
   1263   DCHECK(IsInitialized());
   1264   // The gallery may not exist anymore if the user opened a second config
   1265   // surface concurrently and removed it. Drop the permission update if so.
   1266   MediaGalleriesPrefInfoMap::const_iterator gallery_info =
   1267       known_galleries_.find(pref_id);
   1268   if (gallery_info == known_galleries_.end())
   1269     return false;
   1270 
   1271   bool default_permission = false;
   1272   if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected)
   1273     default_permission = HasAutoDetectedGalleryPermission(extension);
   1274   // When the permission matches the default, we don't need to remember it.
   1275   if (has_permission == default_permission) {
   1276     if (!UnsetGalleryPermissionInPrefs(extension.id(), pref_id))
   1277       // If permission wasn't set, assume nothing has changed.
   1278       return false;
   1279   } else {
   1280     if (!SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission))
   1281       return false;
   1282   }
   1283   if (has_permission)
   1284     FOR_EACH_OBSERVER(GalleryChangeObserver,
   1285                       gallery_change_observers_,
   1286                       OnPermissionAdded(this, extension.id(), pref_id));
   1287   else
   1288     FOR_EACH_OBSERVER(GalleryChangeObserver,
   1289                       gallery_change_observers_,
   1290                       OnPermissionRemoved(this, extension.id(), pref_id));
   1291   return true;
   1292 }
   1293 
   1294 const MediaGalleriesPrefInfoMap& MediaGalleriesPreferences::known_galleries()
   1295     const {
   1296   DCHECK(IsInitialized());
   1297   return known_galleries_;
   1298 }
   1299 
   1300 base::Time MediaGalleriesPreferences::GetLastScanCompletionTime() const {
   1301   int64 last_scan_time_internal =
   1302       profile_->GetPrefs()->GetInt64(prefs::kMediaGalleriesLastScanTime);
   1303   return base::Time::FromInternalValue(last_scan_time_internal);
   1304 }
   1305 
   1306 void MediaGalleriesPreferences::SetLastScanCompletionTime(
   1307     const base::Time& time) {
   1308   profile_->GetPrefs()->SetInt64(prefs::kMediaGalleriesLastScanTime,
   1309                                  time.ToInternalValue());
   1310 }
   1311 
   1312 void MediaGalleriesPreferences::Shutdown() {
   1313   weak_factory_.InvalidateWeakPtrs();
   1314   profile_ = NULL;
   1315 }
   1316 
   1317 // static
   1318 bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
   1319   MediaGalleryPrefId current_id =
   1320       profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
   1321   return current_id != kInvalidMediaGalleryPrefId + 1;
   1322 }
   1323 
   1324 // static
   1325 void MediaGalleriesPreferences::RegisterProfilePrefs(
   1326     user_prefs::PrefRegistrySyncable* registry) {
   1327   registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
   1328                              user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   1329   registry->RegisterUint64Pref(
   1330       prefs::kMediaGalleriesUniqueId,
   1331       kInvalidMediaGalleryPrefId + 1,
   1332       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   1333   registry->RegisterInt64Pref(
   1334       prefs::kMediaGalleriesLastScanTime,
   1335       base::Time().ToInternalValue(),
   1336       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
   1337 }
   1338 
   1339 bool MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
   1340     const std::string& extension_id,
   1341     MediaGalleryPrefId gallery_id,
   1342     bool has_access) {
   1343   DCHECK(IsInitialized());
   1344   ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
   1345                                           extension_id,
   1346                                           kMediaGalleriesPermissions);
   1347   base::ListValue* permissions = update.Get();
   1348   if (!permissions) {
   1349     permissions = update.Create();
   1350   } else {
   1351     // If the gallery is already in the list, update the permission...
   1352     for (base::ListValue::iterator iter = permissions->begin();
   1353          iter != permissions->end(); ++iter) {
   1354       base::DictionaryValue* dict = NULL;
   1355       if (!(*iter)->GetAsDictionary(&dict))
   1356         continue;
   1357       MediaGalleryPermission perm;
   1358       if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
   1359         continue;
   1360       if (perm.pref_id == gallery_id) {
   1361         if (has_access != perm.has_permission) {
   1362           dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
   1363           return true;
   1364         } else {
   1365           return false;
   1366         }
   1367       }
   1368     }
   1369   }
   1370   // ...Otherwise, add a new entry for the gallery.
   1371   base::DictionaryValue* dict = new base::DictionaryValue;
   1372   dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
   1373   dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
   1374   permissions->Append(dict);
   1375   return true;
   1376 }
   1377 
   1378 bool MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
   1379     const std::string& extension_id,
   1380     MediaGalleryPrefId gallery_id) {
   1381   DCHECK(IsInitialized());
   1382   ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
   1383                                           extension_id,
   1384                                           kMediaGalleriesPermissions);
   1385   base::ListValue* permissions = update.Get();
   1386   if (!permissions)
   1387     return false;
   1388 
   1389   for (base::ListValue::iterator iter = permissions->begin();
   1390        iter != permissions->end(); ++iter) {
   1391     const base::DictionaryValue* dict = NULL;
   1392     if (!(*iter)->GetAsDictionary(&dict))
   1393       continue;
   1394     MediaGalleryPermission perm;
   1395     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
   1396       continue;
   1397     if (perm.pref_id == gallery_id) {
   1398       permissions->Erase(iter, NULL);
   1399       return true;
   1400     }
   1401   }
   1402   return false;
   1403 }
   1404 
   1405 std::vector<MediaGalleryPermission>
   1406 MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
   1407     const std::string& extension_id) const {
   1408   DCHECK(IsInitialized());
   1409   std::vector<MediaGalleryPermission> result;
   1410   const base::ListValue* permissions;
   1411   if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
   1412                                            kMediaGalleriesPermissions,
   1413                                            &permissions)) {
   1414     return result;
   1415   }
   1416 
   1417   for (base::ListValue::const_iterator iter = permissions->begin();
   1418        iter != permissions->end(); ++iter) {
   1419     base::DictionaryValue* dict = NULL;
   1420     if (!(*iter)->GetAsDictionary(&dict))
   1421       continue;
   1422     MediaGalleryPermission perm;
   1423     if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
   1424       continue;
   1425     result.push_back(perm);
   1426   }
   1427 
   1428   return result;
   1429 }
   1430 
   1431 void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
   1432     MediaGalleryPrefId gallery_id) {
   1433   DCHECK(IsInitialized());
   1434   ExtensionPrefs* prefs = GetExtensionPrefs();
   1435   const base::DictionaryValue* extensions =
   1436       prefs->pref_service()->GetDictionary(extensions::pref_names::kExtensions);
   1437   if (!extensions)
   1438     return;
   1439 
   1440   for (base::DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
   1441        iter.Advance()) {
   1442     if (!crx_file::id_util::IdIsValid(iter.key())) {
   1443       NOTREACHED();
   1444       continue;
   1445     }
   1446     UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
   1447   }
   1448 }
   1449 
   1450 ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
   1451   DCHECK(IsInitialized());
   1452   if (extension_prefs_for_testing_)
   1453     return extension_prefs_for_testing_;
   1454   return extensions::ExtensionPrefs::Get(profile_);
   1455 }
   1456 
   1457 void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
   1458     extensions::ExtensionPrefs* extension_prefs) {
   1459   DCHECK(IsInitialized());
   1460   extension_prefs_for_testing_ = extension_prefs;
   1461 }
   1462