media_galleries_preferences.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/string16.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/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/media_file_system_registry.h"
22#include "chrome/browser/prefs/scoped_user_pref_update.h"
23#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/storage_monitor/media_storage_util.h"
25#include "chrome/browser/storage_monitor/storage_monitor.h"
26#include "chrome/common/chrome_paths.h"
27#include "chrome/common/extensions/extension.h"
28#include "chrome/common/extensions/permissions/api_permission.h"
29#include "chrome/common/extensions/permissions/media_galleries_permission.h"
30#include "chrome/common/pref_names.h"
31#include "components/user_prefs/pref_registry_syncable.h"
32
33namespace chrome {
34
35namespace {
36
37const char kMediaGalleriesDeviceIdKey[] = "deviceId";
38const char kMediaGalleriesDisplayNameKey[] = "displayName";
39const char kMediaGalleriesPathKey[] = "path";
40const char kMediaGalleriesPrefIdKey[] = "prefId";
41const char kMediaGalleriesTypeKey[] = "type";
42const char kMediaGalleriesVolumeLabelKey[] = "volumeLabel";
43const char kMediaGalleriesVendorNameKey[] = "vendorName";
44const char kMediaGalleriesModelNameKey[] = "modelName";
45const char kMediaGalleriesSizeKey[] = "totalSize";
46const char kMediaGalleriesLastAttachTimeKey[] = "lastAttachTime";
47const char kMediaGalleriesPrefsVersionKey[] = "preferencesVersion";
48
49const char kMediaGalleriesTypeAutoDetectedValue[] = "autoDetected";
50const char kMediaGalleriesTypeUserAddedValue[] = "userAdded";
51const char kMediaGalleriesTypeBlackListedValue[] = "blackListed";
52
53const char kITunesGalleryName[] = "iTunes";
54
55bool GetPrefId(const DictionaryValue& dict, MediaGalleryPrefId* value) {
56  std::string string_id;
57  if (!dict.GetString(kMediaGalleriesPrefIdKey, &string_id) ||
58      !base::StringToUint64(string_id, value)) {
59    return false;
60  }
61
62  return true;
63}
64
65bool GetType(const DictionaryValue& dict, MediaGalleryPrefInfo::Type* type) {
66  std::string string_type;
67  if (!dict.GetString(kMediaGalleriesTypeKey, &string_type))
68    return false;
69
70  if (string_type == kMediaGalleriesTypeAutoDetectedValue) {
71    *type = MediaGalleryPrefInfo::kAutoDetected;
72    return true;
73  }
74  if (string_type == kMediaGalleriesTypeUserAddedValue) {
75    *type = MediaGalleryPrefInfo::kUserAdded;
76    return true;
77  }
78  if (string_type == kMediaGalleriesTypeBlackListedValue) {
79    *type = MediaGalleryPrefInfo::kBlackListed;
80    return true;
81  }
82
83  return false;
84}
85
86bool PopulateGalleryPrefInfoFromDictionary(
87    const DictionaryValue& dict, MediaGalleryPrefInfo* out_gallery_info) {
88  MediaGalleryPrefId pref_id;
89  string16 display_name;
90  std::string device_id;
91  base::FilePath::StringType path;
92  MediaGalleryPrefInfo::Type type = MediaGalleryPrefInfo::kAutoDetected;
93  string16 volume_label;
94  string16 vendor_name;
95  string16 model_name;
96  double total_size_in_bytes = 0.0;
97  double last_attach_time = 0.0;
98  bool volume_metadata_valid = false;
99  int prefs_version = 0;
100
101  if (!GetPrefId(dict, &pref_id) ||
102      !dict.GetString(kMediaGalleriesDeviceIdKey, &device_id) ||
103      !dict.GetString(kMediaGalleriesPathKey, &path) ||
104      !GetType(dict, &type)) {
105    return false;
106  }
107
108  dict.GetString(kMediaGalleriesDisplayNameKey, &display_name);
109  dict.GetInteger(kMediaGalleriesPrefsVersionKey, &prefs_version);
110
111  if (dict.GetString(kMediaGalleriesVolumeLabelKey, &volume_label) &&
112      dict.GetString(kMediaGalleriesVendorNameKey, &vendor_name) &&
113      dict.GetString(kMediaGalleriesModelNameKey, &model_name) &&
114      dict.GetDouble(kMediaGalleriesSizeKey, &total_size_in_bytes) &&
115      dict.GetDouble(kMediaGalleriesLastAttachTimeKey, &last_attach_time)) {
116    volume_metadata_valid = true;
117  }
118
119  out_gallery_info->pref_id = pref_id;
120  out_gallery_info->display_name = display_name;
121  out_gallery_info->device_id = device_id;
122  out_gallery_info->path = base::FilePath(path);
123  out_gallery_info->type = type;
124  out_gallery_info->volume_label = volume_label;
125  out_gallery_info->vendor_name = vendor_name;
126  out_gallery_info->model_name = model_name;
127  out_gallery_info->total_size_in_bytes = total_size_in_bytes;
128  out_gallery_info->last_attach_time =
129      base::Time::FromInternalValue(last_attach_time);
130  out_gallery_info->volume_metadata_valid = volume_metadata_valid;
131  out_gallery_info->prefs_version = prefs_version;
132
133  return true;
134}
135
136DictionaryValue* CreateGalleryPrefInfoDictionary(
137    const MediaGalleryPrefInfo& gallery) {
138  DictionaryValue* dict = new DictionaryValue();
139  dict->SetString(kMediaGalleriesPrefIdKey,
140                  base::Uint64ToString(gallery.pref_id));
141  if (!gallery.volume_metadata_valid)
142    dict->SetString(kMediaGalleriesDisplayNameKey, gallery.display_name);
143  dict->SetString(kMediaGalleriesDeviceIdKey, gallery.device_id);
144  dict->SetString(kMediaGalleriesPathKey, gallery.path.value());
145
146  const char* type = NULL;
147  switch (gallery.type) {
148    case MediaGalleryPrefInfo::kAutoDetected:
149      type = kMediaGalleriesTypeAutoDetectedValue;
150      break;
151    case MediaGalleryPrefInfo::kUserAdded:
152      type = kMediaGalleriesTypeUserAddedValue;
153      break;
154    case MediaGalleryPrefInfo::kBlackListed:
155      type = kMediaGalleriesTypeBlackListedValue;
156      break;
157    default:
158      NOTREACHED();
159      break;
160  }
161  dict->SetString(kMediaGalleriesTypeKey, type);
162
163  if (gallery.volume_metadata_valid) {
164    dict->SetString(kMediaGalleriesVolumeLabelKey, gallery.volume_label);
165    dict->SetString(kMediaGalleriesVendorNameKey, gallery.vendor_name);
166    dict->SetString(kMediaGalleriesModelNameKey, gallery.model_name);
167    dict->SetDouble(kMediaGalleriesSizeKey, gallery.total_size_in_bytes);
168    dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
169                    gallery.last_attach_time.ToInternalValue());
170  }
171
172  // Version 0 of the prefs format was that the display_name was always
173  // used to show the user-visible name of the gallery. Version 1 means
174  // that there is an optional display_name, and when it is present, it
175  // overrides the name that would be built from the volume metadata, path,
176  // or whatever other data. So if we see a display_name with version 0, it
177  // means it may be overwritten simply by getting new volume metadata.
178  // A display_name with version 1 should not be overwritten.
179  dict->SetInteger(kMediaGalleriesPrefsVersionKey, gallery.prefs_version);
180
181  return dict;
182}
183
184bool HasAutoDetectedGalleryPermission(const extensions::Extension& extension) {
185  extensions::MediaGalleriesPermission::CheckParam param(
186      extensions::MediaGalleriesPermission::kAllAutoDetectedPermission);
187  return extension.CheckAPIPermissionWithParam(
188      extensions::APIPermission::kMediaGalleries, &param);
189}
190
191}  // namespace
192
193MediaGalleryPrefInfo::MediaGalleryPrefInfo()
194    : pref_id(kInvalidMediaGalleryPrefId),
195      type(kInvalidType),
196      total_size_in_bytes(0),
197      volume_metadata_valid(false),
198      prefs_version(0) {
199}
200
201MediaGalleryPrefInfo::~MediaGalleryPrefInfo() {}
202
203base::FilePath MediaGalleryPrefInfo::AbsolutePath() const {
204  base::FilePath base_path = MediaStorageUtil::FindDevicePathById(device_id);
205  DCHECK(!path.IsAbsolute());
206  return base_path.empty() ? base_path : base_path.Append(path);
207}
208
209MediaGalleriesPreferences::GalleryChangeObserver::~GalleryChangeObserver() {}
210
211MediaGalleriesPreferences::MediaGalleriesPreferences(Profile* profile)
212    : weak_factory_(this),
213      profile_(profile) {
214  AddDefaultGalleriesIfFreshProfile();
215
216  // Look for optional default galleries every time.
217  itunes::ITunesFinder::FindITunesLibrary(
218      base::Bind(&MediaGalleriesPreferences::OnITunesDeviceID,
219                 weak_factory_.GetWeakPtr()));
220
221  InitFromPrefs(false /*no notification*/);
222
223  StorageMonitor* monitor = StorageMonitor::GetInstance();
224  if (monitor)
225    monitor->AddObserver(this);
226}
227
228MediaGalleriesPreferences::~MediaGalleriesPreferences() {
229  StorageMonitor* monitor = StorageMonitor::GetInstance();
230  if (monitor)
231    monitor->RemoveObserver(this);
232}
233
234void MediaGalleriesPreferences::AddDefaultGalleriesIfFreshProfile() {
235  // Only add defaults the first time.
236  if (APIHasBeenUsed(profile_))
237    return;
238
239  // Fresh profile case.
240  const int kDirectoryKeys[] = {
241    DIR_USER_MUSIC,
242    DIR_USER_PICTURES,
243    DIR_USER_VIDEOS,
244  };
245
246  for (size_t i = 0; i < arraysize(kDirectoryKeys); ++i) {
247    base::FilePath path;
248    if (!PathService::Get(kDirectoryKeys[i], &path))
249      continue;
250
251    base::FilePath relative_path;
252    StorageInfo info;
253    if (MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
254      // TODO(gbillock): Add in the volume metadata here when available.
255      AddGalleryWithName(info.device_id, info.name, relative_path,
256                         false /*user added*/);
257    }
258  }
259}
260
261void MediaGalleriesPreferences::OnITunesDeviceID(const std::string& device_id) {
262  DCHECK(!device_id.empty());
263  AddGalleryWithName(device_id, ASCIIToUTF16(kITunesGalleryName),
264                     base::FilePath(), false /*not user added*/);
265}
266
267void MediaGalleriesPreferences::InitFromPrefs(bool notify_observers) {
268  known_galleries_.clear();
269  device_map_.clear();
270
271  PrefService* prefs = profile_->GetPrefs();
272  const ListValue* list = prefs->GetList(
273      prefs::kMediaGalleriesRememberedGalleries);
274  if (list) {
275    for (ListValue::const_iterator it = list->begin();
276         it != list->end(); ++it) {
277      const DictionaryValue* dict = NULL;
278      if (!(*it)->GetAsDictionary(&dict))
279        continue;
280
281      MediaGalleryPrefInfo gallery_info;
282      if (!PopulateGalleryPrefInfoFromDictionary(*dict, &gallery_info))
283        continue;
284
285      known_galleries_[gallery_info.pref_id] = gallery_info;
286      device_map_[gallery_info.device_id].insert(gallery_info.pref_id);
287    }
288  }
289  if (notify_observers)
290    NotifyChangeObservers(std::string());
291}
292
293void MediaGalleriesPreferences::NotifyChangeObservers(
294    const std::string& extension_id) {
295  FOR_EACH_OBSERVER(GalleryChangeObserver,
296                    gallery_change_observers_,
297                    OnGalleryChanged(this, extension_id));
298}
299
300void MediaGalleriesPreferences::AddGalleryChangeObserver(
301    GalleryChangeObserver* observer) {
302  gallery_change_observers_.AddObserver(observer);
303}
304
305void MediaGalleriesPreferences::RemoveGalleryChangeObserver(
306    GalleryChangeObserver* observer) {
307  gallery_change_observers_.RemoveObserver(observer);
308}
309
310void MediaGalleriesPreferences::OnRemovableStorageAttached(
311    const StorageInfo& info) {
312  if (!MediaStorageUtil::IsMediaDevice(info.device_id))
313    return;
314
315  if (info.name.empty()) {
316    AddGallery(info.device_id, base::FilePath(),
317               false /*not user added*/,
318               info.storage_label,
319               info.vendor_name,
320               info.model_name,
321               info.total_size_in_bytes,
322               base::Time::Now());
323  } else {
324    // TODO(gbillock): get rid of this code path.
325    AddGalleryWithName(info.device_id, info.name, base::FilePath(), false);
326  }
327}
328
329bool MediaGalleriesPreferences::LookUpGalleryByPath(
330    const base::FilePath& path,
331    MediaGalleryPrefInfo* gallery_info) const {
332  StorageInfo info;
333  base::FilePath relative_path;
334  if (!MediaStorageUtil::GetDeviceInfoFromPath(path, &info, &relative_path)) {
335    if (gallery_info)
336      *gallery_info = MediaGalleryPrefInfo();
337    return false;
338  }
339
340  relative_path = relative_path.NormalizePathSeparators();
341  MediaGalleryPrefIdSet galleries_on_device =
342      LookUpGalleriesByDeviceId(info.device_id);
343  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
344       it != galleries_on_device.end();
345       ++it) {
346    const MediaGalleryPrefInfo& gallery = known_galleries_.find(*it)->second;
347    if (gallery.path != relative_path)
348      continue;
349
350    if (gallery_info)
351      *gallery_info = gallery;
352    return true;
353  }
354
355  // This method is called by controller::FilesSelected when the user
356  // adds a new gallery. Control reaches here when the selected gallery is
357  // on a volume we know about, but have no gallery already for. Returns
358  // hypothetical data to the caller about what the prefs will look like
359  // if the gallery is added.
360  // TODO(gbillock): split this out into another function so it doesn't
361  // conflate LookUp.
362  if (gallery_info) {
363    gallery_info->pref_id = kInvalidMediaGalleryPrefId;
364    gallery_info->display_name = info.name;
365    gallery_info->device_id = info.device_id;
366    gallery_info->path = relative_path;
367    gallery_info->type = MediaGalleryPrefInfo::kUserAdded;
368    // TODO(gbillock): Need to add volume metadata here from |info|.
369  }
370  return false;
371}
372
373MediaGalleryPrefIdSet MediaGalleriesPreferences::LookUpGalleriesByDeviceId(
374    const std::string& device_id) const {
375  DeviceIdPrefIdsMap::const_iterator found = device_map_.find(device_id);
376  if (found == device_map_.end())
377    return MediaGalleryPrefIdSet();
378  return found->second;
379}
380
381base::FilePath MediaGalleriesPreferences::LookUpGalleryPathForExtension(
382    MediaGalleryPrefId gallery_id,
383    const extensions::Extension* extension,
384    bool include_unpermitted_galleries) {
385  DCHECK(extension);
386  if (!include_unpermitted_galleries &&
387      !ContainsKey(GalleriesForExtension(*extension), gallery_id))
388    return base::FilePath();
389
390  MediaGalleriesPrefInfoMap::const_iterator it =
391      known_galleries_.find(gallery_id);
392  if (it == known_galleries_.end())
393    return base::FilePath();
394  return MediaStorageUtil::FindDevicePathById(it->second.device_id);
395}
396
397MediaGalleryPrefId MediaGalleriesPreferences::AddGallery(
398    const std::string& device_id,
399    const base::FilePath& relative_path, bool user_added,
400    const string16& volume_label, const string16& vendor_name,
401    const string16& model_name, uint64 total_size_in_bytes,
402    base::Time last_attach_time) {
403  return AddGalleryInternal(device_id, string16(), relative_path, user_added,
404                            volume_label, vendor_name, model_name,
405                            total_size_in_bytes, last_attach_time, true, 1);
406}
407
408MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryWithName(
409    const std::string& device_id, const string16& display_name,
410    const base::FilePath& relative_path, bool user_added) {
411  return AddGalleryInternal(device_id, display_name, relative_path, user_added,
412                            string16(), string16(), string16(),
413                            0, base::Time(), false, 1);
414}
415
416MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryInternal(
417    const std::string& device_id, const string16& display_name,
418    const base::FilePath& relative_path, bool user_added,
419    const string16& volume_label, const string16& vendor_name,
420    const string16& model_name, uint64 total_size_in_bytes,
421    base::Time last_attach_time,
422    bool volume_metadata_valid,
423    int prefs_version) {
424  base::FilePath normalized_relative_path =
425      relative_path.NormalizePathSeparators();
426  MediaGalleryPrefIdSet galleries_on_device =
427    LookUpGalleriesByDeviceId(device_id);
428  for (MediaGalleryPrefIdSet::const_iterator it = galleries_on_device.begin();
429       it != galleries_on_device.end();
430       ++it) {
431    const MediaGalleryPrefInfo& existing = known_galleries_.find(*it)->second;
432    if (existing.path != normalized_relative_path)
433      continue;
434
435    bool update_gallery_type =
436        user_added && (existing.type == MediaGalleryPrefInfo::kBlackListed);
437    bool update_gallery_name = existing.display_name != display_name;
438    bool update_gallery_metadata = volume_metadata_valid &&
439        ((existing.volume_label != volume_label) ||
440         (existing.vendor_name != vendor_name) ||
441         (existing.model_name != model_name) ||
442         (existing.total_size_in_bytes != total_size_in_bytes) ||
443         (existing.last_attach_time != last_attach_time));
444
445    if (!update_gallery_name && !update_gallery_type &&
446        !update_gallery_metadata)
447      return *it;
448
449    PrefService* prefs = profile_->GetPrefs();
450    scoped_ptr<ListPrefUpdate> update(
451        new ListPrefUpdate(prefs, prefs::kMediaGalleriesRememberedGalleries));
452    ListValue* list = update->Get();
453
454    for (ListValue::const_iterator list_iter = list->begin();
455         list_iter != list->end();
456         ++list_iter) {
457      DictionaryValue* dict;
458      MediaGalleryPrefId iter_id;
459      if ((*list_iter)->GetAsDictionary(&dict) &&
460          GetPrefId(*dict, &iter_id) &&
461          *it == iter_id) {
462        if (update_gallery_type) {
463          dict->SetString(kMediaGalleriesTypeKey,
464                          kMediaGalleriesTypeAutoDetectedValue);
465        }
466        if (update_gallery_name)
467          dict->SetString(kMediaGalleriesDisplayNameKey, display_name);
468        if (update_gallery_metadata) {
469          dict->SetString(kMediaGalleriesVolumeLabelKey, volume_label);
470          dict->SetString(kMediaGalleriesVendorNameKey, vendor_name);
471          dict->SetString(kMediaGalleriesModelNameKey, model_name);
472          dict->SetDouble(kMediaGalleriesSizeKey, total_size_in_bytes);
473          dict->SetDouble(kMediaGalleriesLastAttachTimeKey,
474                          last_attach_time.ToInternalValue());
475        }
476        dict->SetInteger(kMediaGalleriesPrefsVersionKey, prefs_version);
477        break;
478      }
479    }
480
481    // Commits the prefs update.
482    update.reset();
483
484    if (update_gallery_name || update_gallery_metadata || update_gallery_type)
485      InitFromPrefs(true /* notify observers */);
486    return *it;
487  }
488
489  PrefService* prefs = profile_->GetPrefs();
490
491  MediaGalleryPrefInfo gallery_info;
492  gallery_info.pref_id = prefs->GetUint64(prefs::kMediaGalleriesUniqueId);
493  prefs->SetUint64(prefs::kMediaGalleriesUniqueId, gallery_info.pref_id + 1);
494  gallery_info.display_name = display_name;
495  gallery_info.device_id = device_id;
496  gallery_info.path = normalized_relative_path;
497  gallery_info.type = MediaGalleryPrefInfo::kAutoDetected;
498  if (user_added)
499    gallery_info.type = MediaGalleryPrefInfo::kUserAdded;
500  if (volume_metadata_valid) {
501    gallery_info.volume_label = volume_label;
502    gallery_info.vendor_name = vendor_name;
503    gallery_info.model_name = model_name;
504    gallery_info.total_size_in_bytes = total_size_in_bytes;
505    gallery_info.last_attach_time = last_attach_time;
506  }
507  gallery_info.volume_metadata_valid = volume_metadata_valid;
508  gallery_info.prefs_version = prefs_version;
509
510  {
511    ListPrefUpdate update(prefs, prefs::kMediaGalleriesRememberedGalleries);
512    ListValue* list = update.Get();
513    list->Append(CreateGalleryPrefInfoDictionary(gallery_info));
514  }
515  InitFromPrefs(true /* notify observers */);
516
517  return gallery_info.pref_id;
518}
519
520MediaGalleryPrefId MediaGalleriesPreferences::AddGalleryByPath(
521    const base::FilePath& path) {
522  MediaGalleryPrefInfo gallery_info;
523  if (LookUpGalleryByPath(path, &gallery_info) &&
524      gallery_info.type != MediaGalleryPrefInfo::kBlackListed) {
525    return gallery_info.pref_id;
526  }
527  return AddGalleryInternal(gallery_info.device_id,
528                            gallery_info.display_name,
529                            gallery_info.path,
530                            true /*user added*/,
531                            gallery_info.volume_label,
532                            gallery_info.vendor_name,
533                            gallery_info.model_name,
534                            gallery_info.total_size_in_bytes,
535                            gallery_info.last_attach_time,
536                            gallery_info.volume_metadata_valid,
537                            gallery_info.prefs_version);
538}
539
540void MediaGalleriesPreferences::ForgetGalleryById(MediaGalleryPrefId pref_id) {
541  PrefService* prefs = profile_->GetPrefs();
542  scoped_ptr<ListPrefUpdate> update(new ListPrefUpdate(
543      prefs, prefs::kMediaGalleriesRememberedGalleries));
544  ListValue* list = update->Get();
545
546  if (!ContainsKey(known_galleries_, pref_id))
547    return;
548
549  for (ListValue::iterator iter = list->begin(); iter != list->end(); ++iter) {
550    DictionaryValue* dict;
551    MediaGalleryPrefId iter_id;
552    if ((*iter)->GetAsDictionary(&dict) && GetPrefId(*dict, &iter_id) &&
553        pref_id == iter_id) {
554      extensions::MediaGalleriesPrivateAPI::RemoveMediaGalleryPermissions(
555          GetExtensionPrefs(), pref_id);
556      MediaGalleryPrefInfo::Type type;
557      if (GetType(*dict, &type) &&
558          type == MediaGalleryPrefInfo::kAutoDetected) {
559        dict->SetString(kMediaGalleriesTypeKey,
560                        kMediaGalleriesTypeBlackListedValue);
561      } else {
562        list->Erase(iter, NULL);
563      }
564      update.reset(NULL);  // commits the update.
565
566      InitFromPrefs(true /* notify observers */);
567      return;
568    }
569  }
570}
571
572MediaGalleryPrefIdSet MediaGalleriesPreferences::GalleriesForExtension(
573    const extensions::Extension& extension) const {
574  MediaGalleryPrefIdSet result;
575
576  if (HasAutoDetectedGalleryPermission(extension)) {
577    for (MediaGalleriesPrefInfoMap::const_iterator it =
578             known_galleries_.begin(); it != known_galleries_.end(); ++it) {
579      if (it->second.type == MediaGalleryPrefInfo::kAutoDetected)
580        result.insert(it->second.pref_id);
581    }
582  }
583
584  std::vector<MediaGalleryPermission> stored_permissions =
585      extensions::MediaGalleriesPrivateAPI::GetMediaGalleryPermissions(
586          GetExtensionPrefs(), extension.id());
587  for (std::vector<MediaGalleryPermission>::const_iterator it =
588           stored_permissions.begin(); it != stored_permissions.end(); ++it) {
589    if (!it->has_permission) {
590      result.erase(it->pref_id);
591    } else {
592      MediaGalleriesPrefInfoMap::const_iterator gallery =
593          known_galleries_.find(it->pref_id);
594      DCHECK(gallery != known_galleries_.end());
595      if (gallery->second.type != MediaGalleryPrefInfo::kBlackListed) {
596        result.insert(it->pref_id);
597      } else {
598        NOTREACHED() << gallery->second.device_id;
599      }
600    }
601  }
602  return result;
603}
604
605void MediaGalleriesPreferences::SetGalleryPermissionForExtension(
606    const extensions::Extension& extension,
607    MediaGalleryPrefId pref_id,
608    bool has_permission) {
609  // The gallery may not exist anymore if the user opened a second config
610  // surface concurrently and removed it. Drop the permission update if so.
611  MediaGalleriesPrefInfoMap::const_iterator gallery_info =
612      known_galleries_.find(pref_id);
613  if (gallery_info == known_galleries_.end())
614    return;
615
616#if defined(ENABLE_EXTENSIONS)
617  extensions::GalleryWatchStateTracker* state_tracker =
618      extensions::GalleryWatchStateTracker::GetForProfile(profile_);
619#endif
620  bool all_permission = HasAutoDetectedGalleryPermission(extension);
621  if (has_permission && all_permission) {
622    if (gallery_info->second.type == MediaGalleryPrefInfo::kAutoDetected) {
623      extensions::MediaGalleriesPrivateAPI::UnsetMediaGalleryPermission(
624          GetExtensionPrefs(), extension.id(), pref_id);
625      NotifyChangeObservers(extension.id());
626#if defined(ENABLE_EXTENSIONS)
627      if (state_tracker) {
628        state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
629                                                  true);
630      }
631#endif
632      return;
633    }
634  }
635
636  if (!has_permission && !all_permission) {
637    extensions::MediaGalleriesPrivateAPI::UnsetMediaGalleryPermission(
638        GetExtensionPrefs(), extension.id(), pref_id);
639  } else {
640    extensions::MediaGalleriesPrivateAPI::SetMediaGalleryPermission(
641        GetExtensionPrefs(), extension.id(), pref_id, has_permission);
642  }
643  NotifyChangeObservers(extension.id());
644#if defined(ENABLE_EXTENSIONS)
645  if (state_tracker) {
646    state_tracker->OnGalleryPermissionChanged(extension.id(), pref_id,
647                                              has_permission);
648  }
649#endif
650}
651
652void MediaGalleriesPreferences::Shutdown() {
653  weak_factory_.InvalidateWeakPtrs();
654  profile_ = NULL;
655}
656
657// static
658bool MediaGalleriesPreferences::APIHasBeenUsed(Profile* profile) {
659  MediaGalleryPrefId current_id =
660      profile->GetPrefs()->GetUint64(prefs::kMediaGalleriesUniqueId);
661  return current_id != kInvalidMediaGalleryPrefId + 1;
662}
663
664// static
665void MediaGalleriesPreferences::RegisterUserPrefs(
666    user_prefs::PrefRegistrySyncable* registry) {
667  registry->RegisterListPref(prefs::kMediaGalleriesRememberedGalleries,
668                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
669  registry->RegisterUint64Pref(
670      prefs::kMediaGalleriesUniqueId,
671      kInvalidMediaGalleryPrefId + 1,
672      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
673}
674
675extensions::ExtensionPrefs*
676MediaGalleriesPreferences::GetExtensionPrefs() const {
677  ExtensionService* extension_service =
678      extensions::ExtensionSystem::Get(profile_)->extension_service();
679  return extension_service->extension_prefs();
680}
681
682}  // namespace chrome
683