media_galleries_preferences.cc revision 868fa2fe829687343ffae624259930155e16dbd8
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/path_service.h"
8#include "base/prefs/pref_service.h"
9#include "base/stl_util.h"
10#include "base/strings/string16.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/values.h"
14#include "chrome/browser/browser_process.h"
15#include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_state_tracker.h"
16#include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h"
17#include "chrome/browser/extensions/extension_prefs.h"
18#include "chrome/browser/extensions/extension_service.h"
19#include "chrome/browser/extensions/extension_system.h"
20#include "chrome/browser/media_galleries/fileapi/itunes_finder.h"
21#include "chrome/browser/media_galleries/fileapi/picasa/picasa_finder.h"
22#include "chrome/browser/media_galleries/media_file_system_registry.h"
23#include "chrome/browser/prefs/scoped_user_pref_update.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/storage_monitor/media_storage_util.h"
26#include "chrome/browser/storage_monitor/storage_monitor.h"
27#include "chrome/common/chrome_paths.h"
28#include "chrome/common/extensions/extension.h"
29#include "chrome/common/extensions/permissions/api_permission.h"
30#include "chrome/common/extensions/permissions/media_galleries_permission.h"
31#include "chrome/common/extensions/permissions/permissions_data.h"
32#include "chrome/common/pref_names.h"
33#include "components/user_prefs/pref_registry_syncable.h"
34
35using base::DictionaryValue;
36using base::ListValue;
37using extensions::ExtensionPrefs;
38
39namespace chrome {
40
41namespace {
42
43// Pref key for the list of media gallery permissions.
44const char kMediaGalleriesPermissions[] = "media_galleries_permissions";
45// Pref key for Media Gallery ID.
46const char kMediaGalleryIdKey[] = "id";
47// Pref key for Media Gallery Permission Value.
48const char kMediaGalleryHasPermissionKey[] = "has_permission";
49
50const char kMediaGalleriesDeviceIdKey[] = "deviceId";
51const char kMediaGalleriesDisplayNameKey[] = "displayName";
52const char kMediaGalleriesPathKey[] = "path";
53const char kMediaGalleriesPrefIdKey[] = "prefId";
54const char kMediaGalleriesTypeKey[] = "type";
55const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
56const char kMediaGalleriesVendorNameKey[] = "vendorName";
57const char kMediaGalleriesModelNameKey[] = "modelName";
58const char kMediaGalleriesSizeKey[] = "totalSize";
59const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
60const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
61
62const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
63const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
64const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
65
66const char kITunesGalleryName[] = "iTunes";
67const char kPicasaGalleryName[] = "Picasa";
68
69bool GetPrefId(const DictionaryValue& dict, MediaGalleryPrefId* value) {
70  std::string string_id;
71  if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
72      !base::StringToUint64(string_id, value)) {
73    return false;
74  }
75
76  return true;
77}
78
79bool GetType(const DictionaryValue& dict, MediaGalleryPrefInfo::Type* type) {
80  std::string string_type;
81  if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
82    return false;
83
84  if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
85    *type = MediaGalleryPrefInfo::kAutoDetected;
86    return true;
87  }
88  if (string_type == kMediaGalleriesTypeUserAddedValue) {
89    *type = MediaGalleryPrefInfo::kUserAdded;
90    return true;
91  }
92  if (string_type == kMediaGalleriesTypeBlackListedValue) {
93    *type = MediaGalleryPrefInfo::kBlackListed;
94    return true;
95  }
96
97  return false;
98}
99
100bool PopulateGalleryPrefInfoFromDictionary(
101    const DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
102  MediaGalleryPrefId pref_id;
103  string16 display_name;
104  std::string device_id;
105  base::FilePath::StringType path;
106  MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kAutoDetected;
107  string16 volume_label;
108  string16 vendor_name;
109  string16 model_name;
110  double total_size_in_bytes = 0.0;
111  double last_attach_time = 0.0;
112  bool volume_metadata_valid = false;
113  int prefs_version = 0;
114
115  if (!GetPrefId(dict, &pref_id) ||
116      !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
117      !dict.GetString(kMediaGalleriesPathKey, &path) ||
118      !GetType(dict, &type)) {
119    return false;
120  }
121
122  dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
123  dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
124
125  if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
126      dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
127      dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
128      dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
129      dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
130    volume_metadata_valid = true;
131  }
132
133  out_gallery_info->pref_id = pref_id;
134  out_gallery_info->display_name = display_name;
135  out_gallery_info->device_id = device_id;
136  out_gallery_info->path = base::FilePath(path);
137  out_gallery_info->type = type;
138  out_gallery_info->volume_label = volume_label;
139  out_gallery_info->vendor_name = vendor_name;
140  out_gallery_info->model_name = model_name;
141  out_gallery_info->total_size_in_bytes = total_size_in_bytes;
142  out_gallery_info->last_attach_time =
143      base::Time::FromInternalValue(last_attach_time);
144  out_gallery_info->volume_metadata_valid = volume_metadata_valid;
145  out_gallery_info->prefs_version = prefs_version;
146
147  return true;
148}
149
150DictionaryValue* CreateGalleryPrefInfoDictionary(
151    const MediaGalleryPrefInfo& gallery) {
152  DictionaryValue* dict = new DictionaryValue();
153  dict->SetString(kMediaGalleriesPrefIdKey,
154                  base::Uint64ToString(gallery.pref_id));
155  if (!gallery.volume_metadata_valid)
156    dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
157  dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
158  dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
159
160  const char* type = NULL;
161  switch (gallery.type) {
162    case MediaGalleryPrefInfo::kAutoDetected:
163      type = kMediaGalleriesTypeAutoDetectedValue;
164      break;
165    case MediaGalleryPrefInfo::kUserAdded:
166      type = kMediaGalleriesTypeUserAddedValue;
167      break;
168    case MediaGalleryPrefInfo::kBlackListed:
169      type = kMediaGalleriesTypeBlackListedValue;
170      break;
171    default:
172      NOTREACHED();
173      break;
174  }
175  dict->SetString(kMediaGalleriesTypeKey, type);
176
177  if (gallery.volume_metadata_valid) {
178    dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
179    dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
180    dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
181    dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
182    dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
183                    gallery.last_attach_time.ToInternalValue());
184  }
185
186  // Version 0 of the prefs format was that the display_name was always
187  // used to show the user-visible name of the gallery. Version 1 means
188  // that there is an optional display_name, and when it is present, it
189  // overrides the name that would be built from the volume metadata, path,
190  // or whatever other data. So if we see a display_name with version 0, it
191  // means it may be overwritten simply by getting new volume metadata.
192  // A display_name with version 1 should not be overwritten.
193  dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
194
195  return dict;
196}
197
198bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
199  extensions::MediaGalleriesPermission::CheckParam param(
200      extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
201  return extensions::PermissionsData::CheckAPIPermissionWithParam(
202      &extension, extensions::APIPermission::kMediaGalleries, &param);
203}
204
205// Retrieves the MediaGalleryPermission from the given dictionary; DCHECKs on
206// failure.
207bool GetMediaGalleryPermissionFromDictionary(
208    const DictionaryValue* dict,
209    MediaGalleryPermission* out_permission) {
210  std::string string_id;
211  if (dict->GetString(kMediaGalleryIdKey, &string_id) &&
212      base::StringToUint64(string_id, &out_permission->pref_id) &&
213      dict->GetBoolean(kMediaGalleryHasPermissionKey,
214                       &out_permission->has_permission)) {
215    return true;
216  }
217  NOTREACHED();
218  return false;
219}
220
221}  // namespace
222
223MediaGalleryPrefInfo::MediaGalleryPrefInfo()
224    : pref_id(kInvalidMediaGalleryPrefId),
225      type(kInvalidType),
226      total_size_in_bytes(0),
227      volume_metadata_valid(false),
228      prefs_version(0) {
229}
230
231MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
232
233base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
234  base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
235  DCHECK(!path.IsAbsolute());
236  return base_path.empty() ? base_path : base_path.Append(path);
237}
238
239MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
240
241MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
242    : weak_factory_(this),
243      profile_(profile),
244      extension_prefs_for_testing_(NULL) {
245  AddDefaultGalleriesIfFreshProfile();
246
247  // Look for optional default galleries every time.
248  itunes::ITunesFinder::FindITunesLibrary(
249      base::Bind(&MediaGalleriesPreferences::OnITunesDeviceID,
250                 weak_factory_.GetWeakPtr()));
251
252  // TODO(tommycli): Turn on when Picasa code is ready.
253#if 0
254  picasa::PicasaFinder::FindPicasaDatabaseOnUIThread(
255      base::Bind(&MediaGalleriesPreferences::OnPicasaDeviceID,
256                 weak_factory_.GetWeakPtr()));
257#endif
258
259  InitFromPrefs(false /*no notification*/);
260
261  StorageMonitor::GetInstance()->AddObserver(this);
262}
263
264MediaGalleriesPreferences::~MediaGalleriesPreferences() {
265  StorageMonitor::GetInstance()->RemoveObserver(this);
266}
267
268void MediaGalleriesPreferences::AddDefaultGalleriesIfFreshProfile() {
269  // Only add defaults the first time.
270  if (APIHasBeenUsed(profile_))
271    return;
272
273  // Fresh profile case.
274  const int kDirectoryKeys[] = {
275    DIR_USER_MUSIC,
276    DIR_USER_PICTURES,
277    DIR_USER_VIDEOS,
278  };
279
280  for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
281    base::FilePath path;
282    if (!PathService::Get(kDirectoryKeys[i], &path))
283      continue;
284
285    base::FilePath relative_path;
286    StorageInfo info;
287    if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
288      // TODO(gbillock): Add in the volume metadata here when available.
289      AddGalleryWithName(info.device_id(), info.name(), relative_path,
290                         false /*user added*/);
291    }
292  }
293}
294
295void MediaGalleriesPreferences::OnITunesDeviceID(const std::string& device_id) {
296  DCHECK(!device_id.empty());
297  // TODO(vandebo): Since we only want to support one iTunes location (and
298  // it is possible for it to move), but we want to preserve the user's
299  // permissions for "the" iTunes gallery, we need to Amend any existing
300  // iTunes galleries instead of adding a new one.
301  AddGalleryWithName(device_id, ASCIIToUTF16(kITunesGalleryName),
302                     base::FilePath(), false /*not user added*/);
303}
304
305void MediaGalleriesPreferences::OnPicasaDeviceID(const std::string& device_id) {
306  // TODO(tommycli): Implement support for location moves.
307  DCHECK(!device_id.empty());
308  AddGalleryWithName(device_id, ASCIIToUTF16(kPicasaGalleryName),
309                     base::FilePath(), false /*not user added*/);
310}
311
312void MediaGalleriesPreferences::InitFromPrefs(bool notify_observers) {
313  known_galleries_.clear();
314  device_map_.clear();
315
316  PrefService* prefs = profile_->GetPrefs();
317  const ListValue* list = prefs->GetList(
318      prefs::kMediaGalleriesRememberedGalleries);
319  if (list) {
320    for (ListValue::const_iterator it = list->begin();
321         it != list->end(); ++it) {
322      const DictionaryValue* dict = NULL;
323      if (!(*it)->GetAsDictionary(&dict))
324        continue;
325
326      MediaGalleryPrefInfo gallery_info;
327      if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
328        continue;
329
330      known_galleries_[gallery_info.pref_id] = gallery_info;
331      device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
332    }
333  }
334  if (notify_observers)
335    NotifyChangeObservers(std::string());
336}
337
338void MediaGalleriesPreferences::NotifyChangeObservers(
339    const std::string& extension_id) {
340  FOR_EACH_OBSERVER(GalleryChangeObserver,
341                    gallery_change_observers_,
342                    OnGalleryChanged(this, extension_id));
343}
344
345void MediaGalleriesPreferences::AddGalleryChangeObserver(
346    GalleryChangeObserver* observer) {
347  gallery_change_observers_.AddObserver(observer);
348}
349
350void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
351    GalleryChangeObserver* observer) {
352  gallery_change_observers_.RemoveObserver(observer);
353}
354
355void MediaGalleriesPreferences::OnRemovableStorageAttached(
356    const StorageInfo& info) {
357  if (!StorageInfo::IsMediaDevice(info.device_id()))
358    return;
359
360  if (info.name().empty()) {
361    AddGallery(info.device_id(),
362               base::FilePath(),
363               false /*not user added*/,
364               info.storage_label(),
365               info.vendor_name(),
366               info.model_name(),
367               info.total_size_in_bytes(),
368               base::Time::Now());
369  } else {
370    // TODO(gbillock): get rid of this code path.
371    AddGalleryWithName(info.device_id(), info.name(), base::FilePath(), false);
372  }
373}
374
375bool MediaGalleriesPreferences::LookUpGalleryByPath(
376    const base::FilePath& path,
377    MediaGalleryPrefInfo* gallery_info) const {
378  StorageInfo info;
379  base::FilePath relative_path;
380  if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
381    if (gallery_info)
382      *gallery_info = MediaGalleryPrefInfo();
383    return false;
384  }
385
386  relative_path = relative_path.NormalizePathSeparators();
387  MediaGalleryPrefIdSet galleries_on_device =
388      LookUpGalleriesByDeviceId(info.device_id());
389  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
390       it != galleries_on_device.end();
391       ++it) {
392    const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
393    if (gallery.path != relative_path)
394      continue;
395
396    if (gallery_info)
397      *gallery_info = gallery;
398    return true;
399  }
400
401  // This method is called by controller::FilesSelected when the user
402  // adds a new gallery. Control reaches here when the selected gallery is
403  // on a volume we know about, but have no gallery already for. Returns
404  // hypothetical data to the caller about what the prefs will look like
405  // if the gallery is added.
406  // TODO(gbillock): split this out into another function so it doesn't
407  // conflate LookUp.
408  if (gallery_info) {
409    gallery_info->pref_id = kInvalidMediaGalleryPrefId;
410    gallery_info->display_name = info.name();
411    gallery_info->device_id = info.device_id();
412    gallery_info->path = relative_path;
413    gallery_info->type = MediaGalleryPrefInfo::kUserAdded;
414    // TODO(gbillock): Need to add volume metadata here from |info|.
415  }
416  return false;
417}
418
419MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
420    const std::string& device_id) const {
421  DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
422  if (found == device_map_.end())
423    return MediaGalleryPrefIdSet();
424  return found->second;
425}
426
427base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
428    MediaGalleryPrefId gallery_id,
429    const extensions::Extension* extension,
430    bool include_unpermitted_galleries) {
431  DCHECK(extension);
432  if (!include_unpermitted_galleries &&
433      !ContainsKey(GalleriesForExtension(*extension), gallery_id))
434    return base::FilePath();
435
436  MediaGalleriesPrefInfoMap::const_iterator it =
437      known_galleries_.find(gallery_id);
438  if (it == known_galleries_.end())
439    return base::FilePath();
440  return MediaStorageUtil::FindDevicePathById(it->second.device_id);
441}
442
443MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
444    const std::string& device_id,
445    const base::FilePath& relative_path, bool user_added,
446    const string16& volume_label, const string16& vendor_name,
447    const string16& model_name, uint64 total_size_in_bytes,
448    base::Time last_attach_time) {
449  return AddGalleryInternal(device_id, string16(), relative_path, user_added,
450                            volume_label, vendor_name, model_name,
451                            total_size_in_bytes, last_attach_time, true, 1);
452}
453
454MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryWithName(
455    const std::string& device_id, const string16& display_name,
456    const base::FilePath& relative_path, bool user_added) {
457  return AddGalleryInternal(device_id, display_name, relative_path, user_added,
458                            string16(), string16(), string16(),
459                            0, base::Time(), false, 1);
460}
461
462MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
463    const std::string& device_id, const string16& display_name,
464    const base::FilePath& relative_path, bool user_added,
465    const string16& volume_label, const string16& vendor_name,
466    const string16& model_name, uint64 total_size_in_bytes,
467    base::Time last_attach_time,
468    bool volume_metadata_valid,
469    int prefs_version) {
470  base::FilePath normalized_relative_path =
471      relative_path.NormalizePathSeparators();
472  MediaGalleryPrefIdSet galleries_on_device =
473    LookUpGalleriesByDeviceId(device_id);
474  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
475       it != galleries_on_device.end();
476       ++it) {
477    const MediaGalleryPrefInfo& existing = known_galleries_.find(*it)->second;
478    if (existing.path != normalized_relative_path)
479      continue;
480
481    bool update_gallery_type =
482        user_added && (existing.type == MediaGalleryPrefInfo::kBlackListed);
483    // TODO(gbillock): Once we have all updates adding the device metadata,
484    // we'll change this to always update the gallery name if we have device
485    // metadata.
486    // Status quo: In M27 and M28, galleries added manually use version 0,
487    // and galleries added automatically (including default galleries) use
488    // version 1. The name override is used by default galleries as well
489    // as all device attach events.
490    bool update_gallery_name = existing.display_name != display_name;
491    bool update_gallery_metadata = volume_metadata_valid &&
492        ((existing.volume_label != volume_label) ||
493         (existing.vendor_name != vendor_name) ||
494         (existing.model_name != model_name) ||
495         (existing.total_size_in_bytes != total_size_in_bytes) ||
496         (existing.last_attach_time != last_attach_time));
497
498    if (!update_gallery_name && !update_gallery_type &&
499        !update_gallery_metadata)
500      return *it;
501
502    PrefService* prefs = profile_->GetPrefs();
503    scoped_ptr<ListPrefUpdate> update(
504        new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
505    ListValue* list = update->Get();
506
507    for (ListValue::const_iterator list_iter = list->begin();
508         list_iter != list->end();
509         ++list_iter) {
510      DictionaryValue* dict;
511      MediaGalleryPrefId iter_id;
512      if ((*list_iter)->GetAsDictionary(&dict) &&
513          GetPrefId(*dict, &iter_id) &&
514          *it == iter_id) {
515        if (update_gallery_type) {
516          dict->SetString(kMediaGalleriesTypeKey,
517                          kMediaGalleriesTypeAutoDetectedValue);
518        }
519        if (update_gallery_name)
520          dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
521        if (update_gallery_metadata) {
522          dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
523          dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
524          dict->SetString(kMediaGalleriesModelNameKey, model_name);
525          dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
526          dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
527                          last_attach_time.ToInternalValue());
528        }
529        dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
530        break;
531      }
532    }
533
534    // Commits the prefs update.
535    update.reset();
536
537    if (update_gallery_name || update_gallery_metadata || update_gallery_type)
538      InitFromPrefs(true /* notify observers */);
539    return *it;
540  }
541
542  PrefService* prefs = profile_->GetPrefs();
543
544  MediaGalleryPrefInfo gallery_info;
545  gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
546  prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
547  gallery_info.display_name = display_name;
548  gallery_info.device_id = device_id;
549  gallery_info.path = normalized_relative_path;
550  gallery_info.type = MediaGalleryPrefInfo::kAutoDetected;
551  if (user_added)
552    gallery_info.type = MediaGalleryPrefInfo::kUserAdded;
553  if (volume_metadata_valid) {
554    gallery_info.volume_label = volume_label;
555    gallery_info.vendor_name = vendor_name;
556    gallery_info.model_name = model_name;
557    gallery_info.total_size_in_bytes = total_size_in_bytes;
558    gallery_info.last_attach_time = last_attach_time;
559  }
560  gallery_info.volume_metadata_valid = volume_metadata_valid;
561  gallery_info.prefs_version = prefs_version;
562
563  {
564    ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
565    ListValue* list = update.Get();
566    list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
567  }
568  InitFromPrefs(true /* notify observers */);
569
570  return gallery_info.pref_id;
571}
572
573MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
574    const base::FilePath& path) {
575  MediaGalleryPrefInfo gallery_info;
576  if (LookUpGalleryByPath(path, &gallery_info) &&
577      gallery_info.type != MediaGalleryPrefInfo::kBlackListed) {
578    return gallery_info.pref_id;
579  }
580  return AddGalleryInternal(gallery_info.device_id,
581                            gallery_info.display_name,
582                            gallery_info.path,
583                            true /*user added*/,
584                            gallery_info.volume_label,
585                            gallery_info.vendor_name,
586                            gallery_info.model_name,
587                            gallery_info.total_size_in_bytes,
588                            gallery_info.last_attach_time,
589                            gallery_info.volume_metadata_valid,
590                            gallery_info.prefs_version);
591}
592
593void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) {
594  PrefService* prefs = profile_->GetPrefs();
595  scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
596      prefs, prefs::kMediaGalleriesRememberedGalleries));
597  ListValue* list = update->Get();
598
599  if (!ContainsKey(known_galleries_, pref_id))
600    return;
601
602  for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) {
603    DictionaryValue* dict;
604    MediaGalleryPrefId iter_id;
605    if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
606        pref_id == iter_id) {
607      RemoveGalleryPermissionsFromPrefs(pref_id);
608      MediaGalleryPrefInfo::Type type;
609      if (GetType(*dict, &type) &&
610          type == MediaGalleryPrefInfo::kAutoDetected) {
611        dict->SetString(kMediaGalleriesTypeKey,
612                        kMediaGalleriesTypeBlackListedValue);
613      } else {
614        list->Erase(iter, NULL);
615      }
616      update.reset(NULL);  // commits the update.
617
618      InitFromPrefs(true /* notify observers */);
619      return;
620    }
621  }
622}
623
624MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
625    const extensions::Extension& extension) const {
626  MediaGalleryPrefIdSet result;
627
628  if (HasAutoDetectedGalleryPermission(extension)) {
629    for (MediaGalleriesPrefInfoMap::const_iterator it =
630             known_galleries_.begin(); it != known_galleries_.end(); ++it) {
631      if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
632        result.insert(it->second.pref_id);
633    }
634  }
635
636  std::vector<MediaGalleryPermission> stored_permissions =
637      GetGalleryPermissionsFromPrefs(extension.id());
638  for (std::vector<MediaGalleryPermission>::const_iterator it =
639           stored_permissions.begin(); it != stored_permissions.end(); ++it) {
640    if (!it->has_permission) {
641      result.erase(it->pref_id);
642    } else {
643      MediaGalleriesPrefInfoMap::const_iterator gallery =
644          known_galleries_.find(it->pref_id);
645      DCHECK(gallery != known_galleries_.end());
646      if (gallery->second.type != MediaGalleryPrefInfo::kBlackListed) {
647        result.insert(it->pref_id);
648      } else {
649        NOTREACHED() << gallery->second.device_id;
650      }
651    }
652  }
653  return result;
654}
655
656void MediaGalleriesPreferences::SetGalleryPermissionForExtension(
657    const extensions::Extension& extension,
658    MediaGalleryPrefId pref_id,
659    bool has_permission) {
660  // The gallery may not exist anymore if the user opened a second config
661  // surface concurrently and removed it. Drop the permission update if so.
662  MediaGalleriesPrefInfoMap::const_iterator gallery_info =
663      known_galleries_.find(pref_id);
664  if (gallery_info == known_galleries_.end())
665    return;
666
667#if defined(ENABLE_EXTENSIONS)
668  extensions::GalleryWatchStateTracker* state_tracker =
669      extensions::GalleryWatchStateTracker::GetForProfile(profile_);
670#endif
671  bool all_permission = HasAutoDetectedGalleryPermission(extension);
672  if (has_permission && all_permission) {
673    if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected) {
674      UnsetGalleryPermissionInPrefs(extension.id(), pref_id);
675      NotifyChangeObservers(extension.id());
676#if defined(ENABLE_EXTENSIONS)
677      if (state_tracker) {
678        state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
679                                                  true, this);
680      }
681#endif
682      return;
683    }
684  }
685
686  if (!has_permission && !all_permission) {
687    UnsetGalleryPermissionInPrefs(extension.id(), pref_id);
688  } else {
689    SetGalleryPermissionInPrefs(extension.id(), pref_id, has_permission);
690  }
691  NotifyChangeObservers(extension.id());
692#if defined(ENABLE_EXTENSIONS)
693  if (state_tracker) {
694    state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
695                                              has_permission, this);
696  }
697#endif
698}
699
700void MediaGalleriesPreferences::Shutdown() {
701  weak_factory_.InvalidateWeakPtrs();
702  profile_ = NULL;
703}
704
705// static
706bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
707  MediaGalleryPrefId current_id =
708      profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
709  return current_id != kInvalidMediaGalleryPrefId + 1;
710}
711
712// static
713void MediaGalleriesPreferences::RegisterUserPrefs(
714    user_prefs::PrefRegistrySyncable* registry) {
715  registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
716                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
717  registry->RegisterUint64Pref(
718      prefs::kMediaGalleriesUniqueId,
719      kInvalidMediaGalleryPrefId + 1,
720      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
721}
722
723void MediaGalleriesPreferences::SetGalleryPermissionInPrefs(
724    const std::string& extension_id,
725    MediaGalleryPrefId gallery_id,
726    bool has_access) {
727  ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
728                                          extension_id,
729                                          kMediaGalleriesPermissions);
730  ListValue* permissions = update.Get();
731  if (!permissions) {
732    permissions = update.Create();
733  } else {
734    // If the gallery is already in the list, update the permission...
735    for (ListValue::iterator iter = permissions->begin();
736         iter != permissions->end(); ++iter) {
737      DictionaryValue* dict = NULL;
738      if (!(*iter)->GetAsDictionary(&dict))
739        continue;
740      MediaGalleryPermission perm;
741      if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
742        continue;
743      if (perm.pref_id == gallery_id) {
744        dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
745        return;
746      }
747    }
748  }
749  // ...Otherwise, add a new entry for the gallery.
750  DictionaryValue* dict = new DictionaryValue;
751  dict->SetString(kMediaGalleryIdKey, base::Uint64ToString(gallery_id));
752  dict->SetBoolean(kMediaGalleryHasPermissionKey, has_access);
753  permissions->Append(dict);
754}
755
756void MediaGalleriesPreferences::UnsetGalleryPermissionInPrefs(
757    const std::string& extension_id,
758    MediaGalleryPrefId gallery_id) {
759  ExtensionPrefs::ScopedListUpdate update(GetExtensionPrefs(),
760                                          extension_id,
761                                          kMediaGalleriesPermissions);
762  ListValue* permissions = update.Get();
763  if (!permissions)
764    return;
765
766  for (ListValue::iterator iter = permissions->begin();
767       iter != permissions->end(); ++iter) {
768    const DictionaryValue* dict = NULL;
769    if (!(*iter)->GetAsDictionary(&dict))
770      continue;
771    MediaGalleryPermission perm;
772    if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
773      continue;
774    if (perm.pref_id == gallery_id) {
775      permissions->Erase(iter, NULL);
776      return;
777    }
778  }
779}
780
781std::vector<MediaGalleryPermission>
782MediaGalleriesPreferences::GetGalleryPermissionsFromPrefs(
783    const std::string& extension_id) const {
784  std::vector<MediaGalleryPermission> result;
785  const ListValue* permissions;
786  if (!GetExtensionPrefs()->ReadPrefAsList(extension_id,
787                                           kMediaGalleriesPermissions,
788                                           &permissions)) {
789    return result;
790  }
791
792  for (ListValue::const_iterator iter = permissions->begin();
793       iter != permissions->end(); ++iter) {
794    DictionaryValue* dict = NULL;
795    if (!(*iter)->GetAsDictionary(&dict))
796      continue;
797    MediaGalleryPermission perm;
798    if (!GetMediaGalleryPermissionFromDictionary(dict, &perm))
799      continue;
800    result.push_back(perm);
801  }
802
803  return result;
804}
805
806void MediaGalleriesPreferences::RemoveGalleryPermissionsFromPrefs(
807    MediaGalleryPrefId gallery_id) {
808  ExtensionPrefs* prefs = GetExtensionPrefs();
809  const DictionaryValue* extensions =
810      prefs->pref_service()->GetDictionary(ExtensionPrefs::kExtensionsPref);
811  if (!extensions)
812    return;
813
814  for (DictionaryValue::Iterator iter(*extensions); !iter.IsAtEnd();
815       iter.Advance()) {
816    if (!extensions::Extension::IdIsValid(iter.key())) {
817      NOTREACHED();
818      continue;
819    }
820    UnsetGalleryPermissionInPrefs(iter.key(), gallery_id);
821  }
822}
823
824ExtensionPrefs* MediaGalleriesPreferences::GetExtensionPrefs() const {
825  if (extension_prefs_for_testing_)
826    return extension_prefs_for_testing_;
827  return extensions::ExtensionPrefs::Get(profile_);
828}
829
830void MediaGalleriesPreferences::SetExtensionPrefsForTesting(
831    extensions::ExtensionPrefs* extension_prefs) {
832  extension_prefs_for_testing_ = extension_prefs;
833}
834
835}  // namespace chrome
836