1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The SyncedNotificationAppInfoService brings down read only metadata from the
6 // sync server with information about the services sending synced notifications.
8 #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h"
10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
12 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "sync/api/sync_change.h"
15 #include "sync/api/sync_change_processor.h"
16 #include "sync/api/sync_error_factory.h"
17 #include "sync/protocol/sync.pb.h"
18 #include "sync/protocol/synced_notification_app_info_specifics.pb.h"
23 SyncedNotificationSendingServiceSettingsData::
24 SyncedNotificationSendingServiceSettingsData(
25 std::string settings_display_name_param,
26 gfx::Image settings_icon_param,
27 message_center::NotifierId notifier_id_param)
28 : settings_display_name(settings_display_name_param),
29 settings_icon(settings_icon_param),
30 notifier_id(notifier_id_param) {}
32 bool SyncedNotificationAppInfoService::avoid_bitmap_fetching_for_test_ = false;
34 SyncedNotificationAppInfoService::SyncedNotificationAppInfoService(
36 : profile_(profile), chrome_notifier_service_(NULL) {}
38 SyncedNotificationAppInfoService::~SyncedNotificationAppInfoService() {}
40 // Methods from KeyedService.
41 void SyncedNotificationAppInfoService::Shutdown() {}
43 // syncer::SyncableService implementation.
45 // This is called at startup to sync with the sync data. This code is not thread
46 // safe, it should only be called by sync on the browser thread.
47 syncer::SyncMergeResult
48 SyncedNotificationAppInfoService::MergeDataAndStartSyncing(
49 syncer::ModelType type,
50 const syncer::SyncDataList& initial_sync_data,
51 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
52 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
53 thread_checker_.CalledOnValidThread();
54 DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
55 syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATION_APP_INFO);
57 // There should only be one sync data in the list for this data type.
58 if (initial_sync_data.size() > 1) {
59 LOG(ERROR) << "Too many app infos over sync";
62 // TODO(petewil): Today we can only handle a single object, so we simply check
63 // for a non-empty list. If in the future we can ever handle more, convert
64 // this whole block to be a loop over all of |initial_sync_data|.
65 if (!initial_sync_data.empty()) {
66 const syncer::SyncData& sync_data = initial_sync_data.front();
67 DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType());
69 const sync_pb::SyncedNotificationAppInfoSpecifics& specifics =
70 sync_data.GetSpecifics().synced_notification_app_info();
72 // Store our sync data, so GetAllSyncData can give it back later.
73 sync_data_ = sync_data;
75 size_t app_info_count = specifics.synced_notification_app_info_size();
77 // The SyncedNotificationAppInfo is a repeated field, process each one.
78 for (size_t app_info_index = 0;
79 app_info_index < app_info_count;
81 const sync_pb::SyncedNotificationAppInfo app_info(
82 specifics.synced_notification_app_info(app_info_index));
84 ProcessIncomingAppInfoProtobuf(app_info);
91 void SyncedNotificationAppInfoService::StopSyncing(syncer::ModelType type) {
92 DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
93 // Implementation is not required, since this is not a user selectable sync
97 // This method is called when there is an incoming sync change from the server.
98 syncer::SyncError SyncedNotificationAppInfoService::ProcessSyncChanges(
99 const tracked_objects::Location& from_here,
100 const syncer::SyncChangeList& change_list) {
101 thread_checker_.CalledOnValidThread();
102 syncer::SyncError error;
104 for (syncer::SyncChangeList::const_iterator it = change_list.begin();
105 it != change_list.end();
107 syncer::SyncData sync_data = it->sync_data();
108 DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType());
109 syncer::SyncChange::SyncChangeType change_type = it->change_type();
110 DCHECK(change_type == syncer::SyncChange::ACTION_UPDATE ||
111 change_type == syncer::SyncChange::ACTION_ADD);
113 sync_pb::SyncedNotificationAppInfoSpecifics specifics =
114 sync_data.GetSpecifics().synced_notification_app_info();
116 // Copy over the sync data with the new one.
117 sync_data_ = sync_data;
119 size_t app_info_count = specifics.synced_notification_app_info_size();
120 if (app_info_count == 0) {
121 NOTREACHED() << "Bad notification app info change from the server.";
125 // The SyncedNotificationAppInfo is a repeated field, process each one.
126 for (size_t app_info_index = 0;
127 app_info_index < app_info_count;
129 const sync_pb::SyncedNotificationAppInfo app_info(
130 specifics.synced_notification_app_info(app_info_index));
131 ProcessIncomingAppInfoProtobuf(app_info);
138 syncer::SyncDataList SyncedNotificationAppInfoService::GetAllSyncData(
139 syncer::ModelType type) const {
140 DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type);
141 syncer::SyncDataList sync_data_list;
143 // TODO(petewil): Recreate this from internal data instead,
144 // it improves code maintainability.
146 // make a copy for the list we give back to the caller.
147 sync_data_list.push_back(sync_data_);
149 return sync_data_list;
152 // When a new protobuf comes in, we will fetch all the bitmaps before reporting
153 // the new protobufs to the ChromeNotifierService. This helps insure that new
154 // protobufs appear at the same time old ones disappear, and that no protobuf
155 // appears before its bitmaps are present.
156 // We also check for duplicate bitmap URLs when loading the bitmaps for a
157 // protobuf, so we aren't left waiting for a second copy of the bitmap to be
158 // fetched before we call the protobuf done.
159 void SyncedNotificationAppInfoService::ProcessIncomingAppInfoProtobuf(
160 const sync_pb::SyncedNotificationAppInfo& app_info) {
161 // Build a local app_info object from the sync data.
162 scoped_ptr<SyncedNotificationAppInfo> incoming(
163 CreateSyncedNotificationAppInfoFromProtobuf(app_info));
164 if (incoming.get() == NULL) {
165 LOG(ERROR) << "Invalid Synced No5tification App Info protobuf";
169 // Process each incoming app_info protobuf.
170 std::string app_info_name = incoming->settings_display_name();
171 DCHECK_GT(app_info_name.length(), 0U);
172 if (app_info_name.length() == 0) {
173 // If there is no unique id (name), there is nothing we can do.
177 SyncedNotificationAppInfo* found =
178 FindSyncedNotificationAppInfoByName(app_info_name);
180 std::vector<std::string> old_app_ids;
181 std::vector<std::string> new_app_ids;
182 std::vector<std::string> added_app_ids;
183 std::vector<std::string> removed_app_ids;
185 new_app_ids = incoming->GetAppIdList();
188 added_app_ids = new_app_ids;
190 // When we have an update, some app id types may be added or removed.
191 // Append to lists of added and removed types.
192 old_app_ids = found->GetAppIdList();
193 new_app_ids = incoming->GetAppIdList();
194 FreeSyncedNotificationAppInfoByName(app_info_name);
196 // Set up for a set difference by sorting the lists.
197 std::sort(old_app_ids.begin(), old_app_ids.end());
198 std::sort(new_app_ids.begin(), new_app_ids.end());
200 // Calculate which app ids are removed (in old, but not in new app ids).
201 std::set_difference(old_app_ids.begin(),
205 std::back_inserter(removed_app_ids));
207 // Calculate which app_ids are added (in new, but not in old app ids).
208 std::set_difference(new_app_ids.begin(),
212 std::back_inserter(added_app_ids));
215 // Put these lists into the app_info object.
216 incoming->set_added_app_ids(added_app_ids);
217 incoming->set_removed_app_ids(removed_app_ids);
219 // Start bitmap fetch.
220 if (!avoid_bitmap_fetching_for_test_) {
221 incoming->QueueBitmapFetchJobs();
222 incoming->StartBitmapFetch();
224 OnBitmapFetchesDone(incoming->added_app_ids(), incoming->removed_app_ids());
227 sending_service_infos_.push_back(incoming.release());
230 void SyncedNotificationAppInfoService::OnBitmapFetchesDone(
231 std::vector<std::string> added_app_ids,
232 std::vector<std::string> removed_app_ids) {
233 // Tell the Chrome Notifier Service so it can show any notifications that were
234 // waiting for the app id to arrive, and to remave any notifications that are
235 // no longer supported.
236 if (chrome_notifier_service_ != NULL) {
237 chrome_notifier_service_->OnAddedAppIds(added_app_ids);
238 chrome_notifier_service_->OnRemovedAppIds(removed_app_ids);
242 // Static Method. Convert from a server protobuf to our internal format.
243 scoped_ptr<SyncedNotificationAppInfo>
244 SyncedNotificationAppInfoService::CreateSyncedNotificationAppInfoFromProtobuf(
245 const sync_pb::SyncedNotificationAppInfo& server_app_info) {
247 // Check for mandatory fields in the sync_data object.
248 std::string display_name;
249 if (server_app_info.has_app_name()) {
250 display_name = server_app_info.app_name();
251 } else if (server_app_info.has_settings_display_name()) {
252 display_name = server_app_info.settings_display_name();
255 scoped_ptr<SyncedNotificationAppInfo> app_info;
256 if (display_name.length() == 0)
257 return app_info.Pass();
259 // Create a new app info object based on the supplied protobuf.
260 app_info.reset(new SyncedNotificationAppInfo(profile_, display_name, this));
262 // This URL is used whenever the user clicks on the body of the app's welcome
264 if (server_app_info.has_info_url()) {
265 app_info->SetWelcomeLinkUrl(GURL(server_app_info.info_url()));
268 // TODO(petewil): Eventually we will add the monochrome icon here, and we may
269 // need to fetch the correct url for the current DPI.
270 // Add the icon URL, if any.
271 if (server_app_info.has_icon()) {
272 std::string icon_url = server_app_info.icon().url();
273 // Set the URLs for the low and high DPI images.
274 // TODO(petewil): Since the high DPI image is not available yet, we just
275 // pass an empty URL for now. Fix this once the high DPI URL is available.
276 app_info->SetSettingsURLs(GURL(icon_url), GURL());
279 // Add all the AppIds from the protobuf.
280 size_t app_id_count = server_app_info.app_id_size();
281 for (size_t ii = 0; ii < app_id_count; ++ii) {
282 app_info->AddAppId(server_app_info.app_id(ii));
285 return app_info.Pass();
288 // This returns a pointer into a vector that we own. Caller must not free it.
289 // Returns NULL if no match is found.
290 notifier::SyncedNotificationAppInfo*
291 SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByName(
292 const std::string& name) {
293 for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
294 sending_service_infos_.begin();
295 it != sending_service_infos_.end();
297 SyncedNotificationAppInfo* app_info = *it;
298 if (name == app_info->settings_display_name())
305 // This returns a pointer into a vector that we own. Caller must not free it.
306 // Returns NULL if no match is found.
307 notifier::SyncedNotificationAppInfo*
308 SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByAppId(
309 const std::string& app_id) {
310 for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
311 sending_service_infos_.begin();
312 it != sending_service_infos_.end();
314 SyncedNotificationAppInfo* app_info = *it;
315 if (app_info->HasAppId(app_id))
322 // Lookup the sending service name for a given app id.
323 std::string SyncedNotificationAppInfoService::FindSendingServiceNameFromAppId(
324 const std::string app_id) {
325 for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it =
326 sending_service_infos_.begin();
327 it != sending_service_infos_.end();
329 SyncedNotificationAppInfo* app_info = *it;
330 if (app_info->HasAppId(app_id))
331 return app_info->settings_display_name();
334 return std::string();
337 void SyncedNotificationAppInfoService::FreeSyncedNotificationAppInfoByName(
338 const std::string& name) {
339 ScopedVector<SyncedNotificationAppInfo>::iterator it =
340 sending_service_infos_.begin();
341 for (; it != sending_service_infos_.end(); ++it) {
342 SyncedNotificationAppInfo* app_info = *it;
343 if (name == app_info->settings_display_name()) {
344 sending_service_infos_.erase(it);
350 std::vector<SyncedNotificationSendingServiceSettingsData>
351 SyncedNotificationAppInfoService::GetAllSendingServiceSettingsData() {
352 std::vector<SyncedNotificationSendingServiceSettingsData> settings_data;
353 ScopedVector<SyncedNotificationAppInfo>::iterator it =
354 sending_service_infos_.begin();
356 for (; it != sending_service_infos_.end(); ++it) {
357 SyncedNotificationSendingServiceSettingsData this_service(
358 (*it)->settings_display_name(),
360 (*it)->GetNotifierId());
361 settings_data.push_back(this_service);
364 return settings_data;
367 // Add a new app info to our data structure. This takes ownership
368 // of the passed in pointer.
369 void SyncedNotificationAppInfoService::Add(
370 scoped_ptr<SyncedNotificationAppInfo> app_info) {
371 sending_service_infos_.push_back(app_info.release());
374 } // namespace notifier