blob: 731648879459ef1c9dccdc0cae9f318c3a8c035b [file] [log] [blame]
[email protected]5051fdde2012-03-19 21:10:141// 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/chromeos/gdata/gdata_sync_client.h"
6
[email protected]92b84f332012-03-21 20:45:217#include <sys/types.h>
8#include <sys/stat.h>
9#include <unistd.h>
10
[email protected]b83e5202012-06-27 07:50:2411#include <algorithm>
[email protected]3653146a2012-05-29 13:41:4712#include <vector>
13
[email protected]92b84f332012-03-21 20:45:2114#include "base/bind.h"
15#include "base/file_util.h"
16#include "base/message_loop_proxy.h"
17#include "base/threading/sequenced_worker_pool.h"
[email protected]a5381772012-04-05 02:20:0418#include "chrome/browser/chromeos/cros/cros_library.h"
[email protected]5051fdde2012-03-19 21:10:1419#include "chrome/browser/chromeos/gdata/gdata_file_system.h"
[email protected]a5381772012-04-05 02:20:0420#include "chrome/browser/prefs/pref_change_registrar.h"
21#include "chrome/browser/prefs/pref_service.h"
22#include "chrome/browser/profiles/profile.h"
23#include "chrome/common/pref_names.h"
[email protected]92b84f332012-03-21 20:45:2124#include "content/public/browser/browser_thread.h"
25
26using content::BrowserThread;
[email protected]5051fdde2012-03-19 21:10:1427
28namespace gdata {
29
[email protected]b83e5202012-06-27 07:50:2430namespace {
31
32// The delay constant is used to delay processing a SyncTask in
33// DoSyncLoop(). We should not process SyncTasks immediately for the
34// following reasons:
35//
36// 1) For fetching, the user may accidentally click on "Make available
37// offline" checkbox on a file, and immediately cancel it in a second.
38// It's a waste to fetch the file in this case.
39//
40// 2) For uploading, file writing via HTML5 file system API is perfomred in
41// two steps: 1) truncate a file to 0 bytes, 2) write contents. We
42// shouldn't start uploading right after the step 1). Besides, the user
43// may edit the same file repeatedly in a short period of time.
44//
45// TODO(satorux): We should find a way to handle the upload case more nicely,
46// and shorten the delay. crbug.com/134774
47const int kDelaySeconds = 5;
48
49// Functor for std::find_if() search for a sync task that matches
50// |in_sync_type| and |in_resource_id|.
51struct CompareTypeAndResourceId {
52 CompareTypeAndResourceId(const GDataSyncClient::SyncType& in_sync_type,
53 const std::string& in_resource_id)
54 : sync_type(in_sync_type),
55 resource_id(in_resource_id) {}
56
57 bool operator()(const GDataSyncClient::SyncTask& sync_task) {
58 return (sync_type == sync_task.sync_type &&
59 resource_id == sync_task.resource_id);
60 }
61
62 const GDataSyncClient::SyncType sync_type;
63 const std::string resource_id;
64};
65
66} // namespace
67
[email protected]b7c5da72012-06-26 05:27:4668GDataSyncClient::SyncTask::SyncTask(SyncType in_sync_type,
[email protected]b83e5202012-06-27 07:50:2469 const std::string& in_resource_id,
70 const base::Time& in_timestamp)
[email protected]b7c5da72012-06-26 05:27:4671 : sync_type(in_sync_type),
[email protected]b83e5202012-06-27 07:50:2472 resource_id(in_resource_id),
73 timestamp(in_timestamp) {
[email protected]b7c5da72012-06-26 05:27:4674}
75
[email protected]a5381772012-04-05 02:20:0476GDataSyncClient::GDataSyncClient(Profile* profile,
[email protected]e0529fc2012-06-12 11:07:5877 GDataFileSystemInterface* file_system,
78 GDataCache* cache)
[email protected]a5381772012-04-05 02:20:0479 : profile_(profile),
80 file_system_(file_system),
[email protected]e0529fc2012-06-12 11:07:5881 cache_(cache),
[email protected]a5381772012-04-05 02:20:0482 registrar_(new PrefChangeRegistrar),
[email protected]b83e5202012-06-27 07:50:2483 delay_(base::TimeDelta::FromSeconds(kDelaySeconds)),
[email protected]b7c5da72012-06-26 05:27:4684 sync_loop_is_running_(false),
[email protected]92b84f332012-03-21 20:45:2185 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
86 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]5051fdde2012-03-19 21:10:1487}
88
89GDataSyncClient::~GDataSyncClient() {
[email protected]92b84f332012-03-21 20:45:2190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
[email protected]73f9c742012-06-15 07:37:1391 if (cache_)
92 cache_->RemoveObserver(this);
[email protected]2416a2e2012-05-07 20:58:4993
94 chromeos::NetworkLibrary* network_library =
95 chromeos::CrosLibrary::Get()->GetNetworkLibrary();
96 if (network_library)
97 network_library->RemoveNetworkManagerObserver(this);
[email protected]13cfee922012-03-20 01:04:4498}
99
[email protected]bf57c872012-03-22 23:17:15100void GDataSyncClient::Initialize() {
[email protected]92b84f332012-03-21 20:45:21101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102
[email protected]1ff88ea52012-06-20 04:54:00103 file_system_->AddObserver(this);
[email protected]73f9c742012-06-15 07:37:13104 cache_->AddObserver(this);
[email protected]a5381772012-04-05 02:20:04105
[email protected]2416a2e2012-05-07 20:58:49106 chromeos::NetworkLibrary* network_library =
107 chromeos::CrosLibrary::Get()->GetNetworkLibrary();
108 if (network_library)
109 network_library->AddNetworkManagerObserver(this);
110 else
111 LOG(ERROR) << "NetworkLibrary is not present";
112
[email protected]a5381772012-04-05 02:20:04113 registrar_->Init(profile_->GetPrefs());
114 registrar_->Add(prefs::kDisableGData, this);
115 registrar_->Add(prefs::kDisableGDataOverCellular, this);
[email protected]5051fdde2012-03-19 21:10:14116}
117
[email protected]b83e5202012-06-27 07:50:24118void GDataSyncClient::StartProcessingBacklog() {
119 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
120
121 cache_->GetResourceIdsOfBacklogOnUIThread(
122 base::Bind(&GDataSyncClient::OnGetResourceIdsOfBacklog,
[email protected]8764a392012-06-20 06:43:08123 weak_ptr_factory_.GetWeakPtr()));
[email protected]92b84f332012-03-21 20:45:21124}
125
[email protected]b7c5da72012-06-26 05:27:46126std::vector<std::string> GDataSyncClient::GetResourceIdsForTesting(
127 SyncType sync_type) const {
128 std::vector<std::string> resource_ids;
129 for (size_t i = 0; i < queue_.size(); ++i) {
130 const SyncTask& sync_task = queue_[i];
131 if (sync_task.sync_type == sync_type)
132 resource_ids.push_back(sync_task.resource_id);
133 }
134 return resource_ids;
[email protected]8a686f082012-03-27 06:03:51135}
136
[email protected]b7c5da72012-06-26 05:27:46137void GDataSyncClient::StartSyncLoop() {
138 if (!sync_loop_is_running_)
139 DoSyncLoop();
140}
141
142void GDataSyncClient::DoSyncLoop() {
[email protected]adf84402012-03-25 21:56:38143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
144
[email protected]b7c5da72012-06-26 05:27:46145 if (queue_.empty() || ShouldStopSyncLoop()) {
146 // Note that |queue_| is not cleared so the sync loop can resume.
147 sync_loop_is_running_ = false;
[email protected]adf84402012-03-25 21:56:38148 return;
[email protected]8a686f082012-03-27 06:03:51149 }
[email protected]b7c5da72012-06-26 05:27:46150 sync_loop_is_running_ = true;
[email protected]adf84402012-03-25 21:56:38151
[email protected]b83e5202012-06-27 07:50:24152 // Should copy before calling queue_.pop_front().
153 const SyncTask sync_task = queue_.front();
[email protected]adf84402012-03-25 21:56:38154
[email protected]b83e5202012-06-27 07:50:24155 // Check if we are ready to process the task.
156 const base::TimeDelta elapsed = base::Time::Now() - sync_task.timestamp;
157 if (elapsed < delay_) {
158 // Not yet ready. Revisit at a later time.
159 const bool posted = base::MessageLoopProxy::current()->PostDelayedTask(
160 FROM_HERE,
161 base::Bind(&GDataSyncClient::DoSyncLoop,
162 weak_ptr_factory_.GetWeakPtr()),
163 delay_);
164 DCHECK(posted);
165 return;
166 }
167
168 queue_.pop_front();
169 if (sync_task.sync_type == FETCH) {
170 DVLOG(1) << "Fetching " << sync_task.resource_id;
171 file_system_->GetFileByResourceId(
172 sync_task.resource_id,
173 base::Bind(&GDataSyncClient::OnFetchFileComplete,
174 weak_ptr_factory_.GetWeakPtr(),
175 sync_task.resource_id),
176 GetDownloadDataCallback());
177 } else if (sync_task.sync_type == UPLOAD) {
178 DVLOG(1) << "Uploading " << sync_task.resource_id;
179 file_system_->UpdateFileByResourceId(
180 sync_task.resource_id,
181 base::Bind(&GDataSyncClient::OnUploadFileComplete,
182 weak_ptr_factory_.GetWeakPtr(),
183 sync_task.resource_id));
184 } else {
185 NOTREACHED() << ": Unexpected sync type: " << sync_task.sync_type;
186 }
[email protected]92b84f332012-03-21 20:45:21187}
188
[email protected]b7c5da72012-06-26 05:27:46189bool GDataSyncClient::ShouldStopSyncLoop() {
[email protected]a5381772012-04-05 02:20:04190 // Should stop if the gdata feature was disabled while running the fetch
191 // loop.
192 if (profile_->GetPrefs()->GetBoolean(prefs::kDisableGData))
193 return true;
194
195 // Something must be wrong if the network library is not present.
196 chromeos::NetworkLibrary* network_library =
197 chromeos::CrosLibrary::Get()->GetNetworkLibrary();
198 if (!network_library)
199 return true;
200
201 // Something must be wrong if the active network is not present.
202 const chromeos::Network* active_network = network_library->active_network();
203 if (!active_network)
204 return true;
205
206 // Should stop if the network is not online.
207 if (!active_network->online())
208 return true;
209
210 // Should stop if the current connection is on cellular network, and
211 // fetching is disabled over cellular.
212 if (profile_->GetPrefs()->GetBoolean(prefs::kDisableGDataOverCellular) &&
[email protected]aaa70a42012-06-05 22:22:51213 (active_network->type() == chromeos::TYPE_CELLULAR ||
214 active_network->type() == chromeos::TYPE_WIMAX)) {
[email protected]a5381772012-04-05 02:20:04215 return true;
[email protected]aaa70a42012-06-05 22:22:51216 }
[email protected]a5381772012-04-05 02:20:04217
218 return false;
219}
220
[email protected]1ff88ea52012-06-20 04:54:00221void GDataSyncClient::OnInitialLoadFinished() {
[email protected]adf84402012-03-25 21:56:38222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
223
[email protected]b83e5202012-06-27 07:50:24224 StartProcessingBacklog();
[email protected]8c4b51262012-03-21 23:42:47225}
226
[email protected]73f9c742012-06-15 07:37:13227void GDataSyncClient::OnCachePinned(const std::string& resource_id,
228 const std::string& md5) {
[email protected]adf84402012-03-25 21:56:38229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
230
[email protected]b83e5202012-06-27 07:50:24231 AddTaskToQueue(SyncTask(FETCH, resource_id, base::Time::Now()));
[email protected]b7c5da72012-06-26 05:27:46232 StartSyncLoop();
[email protected]5051fdde2012-03-19 21:10:14233}
234
[email protected]73f9c742012-06-15 07:37:13235void GDataSyncClient::OnCacheUnpinned(const std::string& resource_id,
236 const std::string& md5) {
[email protected]adf84402012-03-25 21:56:38237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
238
239 // Remove the resource_id if it's in the queue. This can happen if the user
240 // cancels pinning before the file is fetched.
[email protected]b83e5202012-06-27 07:50:24241 std::deque<SyncTask>::iterator iter =
242 std::find_if(queue_.begin(), queue_.end(),
243 CompareTypeAndResourceId(FETCH, resource_id));
244 if (iter != queue_.end())
245 queue_.erase(iter);
[email protected]8c4b51262012-03-21 23:42:47246}
247
[email protected]d7664c22012-06-18 19:35:49248void GDataSyncClient::OnCacheCommitted(const std::string& resource_id) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
250
[email protected]b83e5202012-06-27 07:50:24251 AddTaskToQueue(SyncTask(UPLOAD, resource_id, base::Time::Now()));
252 StartSyncLoop();
[email protected]d7664c22012-06-18 19:35:49253}
254
[email protected]b83e5202012-06-27 07:50:24255void GDataSyncClient::AddTaskToQueue(const SyncTask& sync_task) {
[email protected]92b84f332012-03-21 20:45:21256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
257
[email protected]b83e5202012-06-27 07:50:24258 std::deque<SyncTask>::iterator iter =
259 std::find_if(queue_.begin(), queue_.end(),
260 CompareTypeAndResourceId(sync_task.sync_type,
261 sync_task.resource_id));
262 // If the same task is already queued, remove it. We'll add back the new
263 // task to the end of the queue.
264 if (iter != queue_.end())
265 queue_.erase(iter);
266
267 queue_.push_back(sync_task);
268}
269
270void GDataSyncClient::OnGetResourceIdsOfBacklog(
271 const std::vector<std::string>& to_fetch,
272 const std::vector<std::string>& to_upload) {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
274
275 // Give priority to upload tasks over fetch tasks, so that dirty files are
276 // uploaded as soon as possible.
277 for (size_t i = 0; i < to_upload.size(); ++i) {
278 const std::string& resource_id = to_upload[i];
279 DVLOG(1) << "Queuing to upload: " << resource_id;
280 AddTaskToQueue(SyncTask(UPLOAD, resource_id, base::Time::Now()));
281 }
282
283 for (size_t i = 0; i < to_fetch.size(); ++i) {
284 const std::string& resource_id = to_fetch[i];
285 DVLOG(1) << "Queuing to fetch: " << resource_id;
286 AddTaskToQueue(SyncTask(FETCH, resource_id, base::Time::Now()));
[email protected]92b84f332012-03-21 20:45:21287 }
[email protected]ffd60e432012-03-24 20:36:00288
[email protected]b7c5da72012-06-26 05:27:46289 StartSyncLoop();
[email protected]92b84f332012-03-21 20:45:21290}
291
[email protected]adf84402012-03-25 21:56:38292void GDataSyncClient::OnFetchFileComplete(const std::string& resource_id,
293 base::PlatformFileError error,
294 const FilePath& local_path,
[email protected]ff14110f2012-03-26 05:25:12295 const std::string& ununsed_mime_type,
[email protected]adf84402012-03-25 21:56:38296 GDataFileType file_type) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298
299 if (error == base::PLATFORM_FILE_OK) {
300 DVLOG(1) << "Fetched " << resource_id << ": " << local_path.value();
301 } else {
302 // TODO(satorux): We should re-queue if the error is recoverable.
[email protected]086290f2012-05-09 19:34:49303 LOG(WARNING) << "Failed to fetch " << resource_id << ": " << error;
[email protected]adf84402012-03-25 21:56:38304 }
305
[email protected]8a686f082012-03-27 06:03:51306 // Continue the loop.
[email protected]b7c5da72012-06-26 05:27:46307 DoSyncLoop();
[email protected]adf84402012-03-25 21:56:38308}
309
[email protected]b83e5202012-06-27 07:50:24310void GDataSyncClient::OnUploadFileComplete(const std::string& resource_id,
311 base::PlatformFileError error) {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313
314 if (error == base::PLATFORM_FILE_OK) {
315 DVLOG(1) << "Uploaded " << resource_id;
316 } else {
317 // TODO(satorux): We should re-queue if the error is recoverable.
318 LOG(WARNING) << "Failed to upload " << resource_id << ": " << error;
319 }
320
321 // Continue the loop.
322 DoSyncLoop();
323}
324
[email protected]2416a2e2012-05-07 20:58:49325void GDataSyncClient::OnNetworkManagerChanged(
326 chromeos::NetworkLibrary* network_library) {
[email protected]a5381772012-04-05 02:20:04327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328
[email protected]b7c5da72012-06-26 05:27:46329 // Resume the sync loop if the network is back online. Note that we don't
[email protected]a5381772012-04-05 02:20:04330 // need to check the type of the network as it will be checked in
[email protected]b7c5da72012-06-26 05:27:46331 // ShouldStopSyncLoop() as soon as the loop is resumed.
[email protected]2416a2e2012-05-07 20:58:49332 const chromeos::Network* active_network = network_library->active_network();
333 if (active_network && active_network->online())
[email protected]b7c5da72012-06-26 05:27:46334 StartSyncLoop();
[email protected]a5381772012-04-05 02:20:04335}
336
337void GDataSyncClient::Observe(int type,
338 const content::NotificationSource& source,
339 const content::NotificationDetails& details) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341
[email protected]b7c5da72012-06-26 05:27:46342 // Resume the sync loop if gdata preferences are changed. Note that we
[email protected]a5381772012-04-05 02:20:04343 // don't need to check the new values here as these will be checked in
[email protected]b7c5da72012-06-26 05:27:46344 // ShouldStopSyncLoop() as soon as the loop is resumed.
345 StartSyncLoop();
[email protected]a5381772012-04-05 02:20:04346}
347
[email protected]5051fdde2012-03-19 21:10:14348} // namespace gdata