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.
5 #include "chrome/browser/chromeos/drive/sync_client.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "chrome/browser/chromeos/drive/drive.pb.h"
12 #include "chrome/browser/chromeos/drive/file_cache.h"
13 #include "chrome/browser/chromeos/drive/file_system/download_operation.h"
14 #include "chrome/browser/chromeos/drive/file_system/update_operation.h"
15 #include "chrome/browser/chromeos/drive/file_system_util.h"
16 #include "chrome/browser/google_apis/task_util.h"
17 #include "content/public/browser/browser_thread.h"
19 using content::BrowserThread;
26 // The delay constant is used to delay processing a sync task. We should not
27 // process SyncTasks immediately for the following reasons:
29 // 1) For fetching, the user may accidentally click on "Make available
30 // offline" checkbox on a file, and immediately cancel it in a second.
31 // It's a waste to fetch the file in this case.
33 // 2) For uploading, file writing via HTML5 file system API is performed in
34 // two steps: 1) truncate a file to 0 bytes, 2) write contents. We
35 // shouldn't start uploading right after the step 1). Besides, the user
36 // may edit the same file repeatedly in a short period of time.
38 // TODO(satorux): We should find a way to handle the upload case more nicely,
39 // and shorten the delay. crbug.com/134774
40 const int kDelaySeconds = 5;
42 // The delay constant is used to delay retrying a sync task on server errors.
43 const int kLongDelaySeconds = 600;
45 // Iterates cache entries and appends IDs to |to_fetch| if the file is pinned
46 // but not fetched (not present locally), or to |to_upload| if the file is dirty
48 void CollectBacklog(FileCache* cache,
49 std::vector<std::string>* to_fetch,
50 std::vector<std::string>* to_upload) {
54 scoped_ptr<FileCache::Iterator> it = cache->GetIterator();
55 for (; !it->IsAtEnd(); it->Advance()) {
56 const FileCacheEntry& cache_entry = it->GetValue();
57 const std::string& local_id = it->GetID();
58 if (cache_entry.is_pinned() && !cache_entry.is_present())
59 to_fetch->push_back(local_id);
61 if (cache_entry.is_dirty())
62 to_upload->push_back(local_id);
64 DCHECK(!it->HasError());
67 // Iterates cache entries and collects IDs of ones with obsolete cache files.
68 void CheckExistingPinnedFiles(ResourceMetadata* metadata,
70 std::vector<std::string>* local_ids) {
71 scoped_ptr<FileCache::Iterator> it = cache->GetIterator();
72 for (; !it->IsAtEnd(); it->Advance()) {
73 const FileCacheEntry& cache_entry = it->GetValue();
74 const std::string& local_id = it->GetID();
75 if (!cache_entry.is_pinned() || !cache_entry.is_present())
79 FileError error = metadata->GetResourceEntryById(local_id, &entry);
80 if (error != FILE_ERROR_OK) {
81 LOG(WARNING) << "Entry not found: " << local_id;
85 // If MD5s don't match, it indicates the local cache file is stale, unless
86 // the file is dirty (the MD5 is "local"). We should never re-fetch the
87 // file when we have a locally modified version.
88 if (entry.file_specific_info().md5() == cache_entry.md5() ||
89 cache_entry.is_dirty())
92 error = cache->Remove(local_id);
93 if (error != FILE_ERROR_OK) {
94 LOG(WARNING) << "Failed to remove cache entry: " << local_id;
98 error = cache->Pin(local_id);
99 if (error != FILE_ERROR_OK) {
100 LOG(WARNING) << "Failed to pin cache entry: " << local_id;
104 local_ids->push_back(local_id);
106 DCHECK(!it->HasError());
111 SyncClient::SyncClient(base::SequencedTaskRunner* blocking_task_runner,
112 file_system::OperationObserver* observer,
113 JobScheduler* scheduler,
114 ResourceMetadata* metadata,
116 const base::FilePath& temporary_file_directory)
117 : blocking_task_runner_(blocking_task_runner),
120 download_operation_(new file_system::DownloadOperation(
121 blocking_task_runner,
126 temporary_file_directory)),
127 update_operation_(new file_system::UpdateOperation(blocking_task_runner,
132 delay_(base::TimeDelta::FromSeconds(kDelaySeconds)),
133 long_delay_(base::TimeDelta::FromSeconds(kLongDelaySeconds)),
134 weak_ptr_factory_(this) {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
138 SyncClient::~SyncClient() {
139 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
142 void SyncClient::StartProcessingBacklog() {
143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
145 std::vector<std::string>* to_fetch = new std::vector<std::string>;
146 std::vector<std::string>* to_upload = new std::vector<std::string>;
147 blocking_task_runner_->PostTaskAndReply(
149 base::Bind(&CollectBacklog, cache_, to_fetch, to_upload),
150 base::Bind(&SyncClient::OnGetLocalIdsOfBacklog,
151 weak_ptr_factory_.GetWeakPtr(),
152 base::Owned(to_fetch),
153 base::Owned(to_upload)));
156 void SyncClient::StartCheckingExistingPinnedFiles() {
157 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159 std::vector<std::string>* local_ids = new std::vector<std::string>;
160 blocking_task_runner_->PostTaskAndReply(
162 base::Bind(&CheckExistingPinnedFiles,
166 base::Bind(&SyncClient::AddFetchTasks,
167 weak_ptr_factory_.GetWeakPtr(),
168 base::Owned(local_ids)));
171 void SyncClient::AddFetchTask(const std::string& local_id) {
172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
174 AddTaskToQueue(FETCH, local_id, delay_);
177 void SyncClient::RemoveFetchTask(const std::string& local_id) {
178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
180 // TODO(kinaba): Cancel tasks in JobScheduler as well. crbug.com/248856
181 pending_fetch_list_.erase(local_id);
184 void SyncClient::AddUploadTask(const std::string& local_id) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
187 AddTaskToQueue(UPLOAD, local_id, delay_);
190 void SyncClient::AddTaskToQueue(SyncType type,
191 const std::string& local_id,
192 const base::TimeDelta& delay) {
193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
195 // If the same task is already queued, ignore this task.
198 if (fetch_list_.find(local_id) == fetch_list_.end()) {
199 fetch_list_.insert(local_id);
200 pending_fetch_list_.insert(local_id);
206 case UPLOAD_NO_CONTENT_CHECK:
207 if (upload_list_.find(local_id) == upload_list_.end()) {
208 upload_list_.insert(local_id);
215 base::MessageLoopProxy::current()->PostDelayedTask(
217 base::Bind(&SyncClient::StartTask,
218 weak_ptr_factory_.GetWeakPtr(),
224 void SyncClient::StartTask(SyncType type, const std::string& local_id) {
227 // Check if the resource has been removed from the start list.
228 if (pending_fetch_list_.find(local_id) != pending_fetch_list_.end()) {
229 DVLOG(1) << "Fetching " << local_id;
230 pending_fetch_list_.erase(local_id);
232 download_operation_->EnsureFileDownloadedByLocalId(
234 ClientContext(BACKGROUND),
235 GetFileContentInitializedCallback(),
236 google_apis::GetContentCallback(),
237 base::Bind(&SyncClient::OnFetchFileComplete,
238 weak_ptr_factory_.GetWeakPtr(),
242 fetch_list_.erase(local_id);
246 case UPLOAD_NO_CONTENT_CHECK:
247 DVLOG(1) << "Uploading " << local_id;
248 update_operation_->UpdateFileByLocalId(
250 ClientContext(BACKGROUND),
251 type == UPLOAD ? file_system::UpdateOperation::RUN_CONTENT_CHECK
252 : file_system::UpdateOperation::NO_CONTENT_CHECK,
253 base::Bind(&SyncClient::OnUploadFileComplete,
254 weak_ptr_factory_.GetWeakPtr(),
260 void SyncClient::OnGetLocalIdsOfBacklog(
261 const std::vector<std::string>* to_fetch,
262 const std::vector<std::string>* to_upload) {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265 // Give priority to upload tasks over fetch tasks, so that dirty files are
266 // uploaded as soon as possible.
267 for (size_t i = 0; i < to_upload->size(); ++i) {
268 const std::string& local_id = (*to_upload)[i];
269 DVLOG(1) << "Queuing to upload: " << local_id;
270 AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, delay_);
273 for (size_t i = 0; i < to_fetch->size(); ++i) {
274 const std::string& local_id = (*to_fetch)[i];
275 DVLOG(1) << "Queuing to fetch: " << local_id;
276 AddTaskToQueue(FETCH, local_id, delay_);
280 void SyncClient::AddFetchTasks(const std::vector<std::string>* local_ids) {
281 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283 for (size_t i = 0; i < local_ids->size(); ++i)
284 AddFetchTask((*local_ids)[i]);
287 void SyncClient::OnFetchFileComplete(const std::string& local_id,
289 const base::FilePath& local_path,
290 scoped_ptr<ResourceEntry> entry) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
293 fetch_list_.erase(local_id);
295 if (error == FILE_ERROR_OK) {
296 DVLOG(1) << "Fetched " << local_id << ": " << local_path.value();
299 case FILE_ERROR_ABORT:
300 // If user cancels download, unpin the file so that we do not sync the
302 cache_->UnpinOnUIThread(local_id,
303 base::Bind(&util::EmptyFileOperationCallback));
305 case FILE_ERROR_NO_CONNECTION:
306 // Re-queue the task so that we'll retry once the connection is back.
307 AddTaskToQueue(FETCH, local_id, delay_);
309 case FILE_ERROR_SERVICE_UNAVAILABLE:
310 // Re-queue the task so that we'll retry once the service is back.
311 AddTaskToQueue(FETCH, local_id, long_delay_);
314 LOG(WARNING) << "Failed to fetch " << local_id
315 << ": " << FileErrorToString(error);
320 void SyncClient::OnUploadFileComplete(const std::string& local_id,
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324 upload_list_.erase(local_id);
326 if (error == FILE_ERROR_OK) {
327 DVLOG(1) << "Uploaded " << local_id;
330 case FILE_ERROR_NO_CONNECTION:
331 // Re-queue the task so that we'll retry once the connection is back.
332 AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, delay_);
334 case FILE_ERROR_SERVICE_UNAVAILABLE:
335 // Re-queue the task so that we'll retry once the service is back.
336 AddTaskToQueue(UPLOAD_NO_CONTENT_CHECK, local_id, long_delay_);
339 LOG(WARNING) << "Failed to upload " << local_id << ": "
340 << FileErrorToString(error);
345 } // namespace internal