blob: d9c0d6186f912efe58f5c0304bcee3a475aa11eb [file] [log] [blame]
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001// 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/drive/job_scheduler.h"
6
Ben Murdoch9ab55632013-07-18 11:57:30 +01007#include "base/message_loop/message_loop.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01008#include "base/prefs/pref_service.h"
9#include "base/rand_util.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010010#include "base/strings/string_number_conversions.h"
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +010011#include "base/strings/stringprintf.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010012#include "chrome/browser/chromeos/drive/file_system_util.h"
13#include "chrome/browser/chromeos/drive/logging.h"
14#include "chrome/browser/google_apis/drive_api_parser.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010015#include "chrome/browser/google_apis/task_util.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010016#include "chrome/common/pref_names.h"
17#include "content/public/browser/browser_thread.h"
18
19using content::BrowserThread;
20
21namespace drive {
22
23namespace {
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010024
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010025const int kMaxThrottleCount = 5;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010026
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010027const int kMaxRetryCount = kMaxThrottleCount - 1;
28
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010029// Parameter struct for RunUploadNewFile.
30struct UploadNewFileParams {
31 std::string parent_resource_id;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010032 base::FilePath local_file_path;
33 std::string title;
34 std::string content_type;
Ben Murdocheb525c52013-07-10 11:40:50 +010035 UploadCompletionCallback callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010036 google_apis::ProgressCallback progress_callback;
37};
38
39// Helper function to work around the arity limitation of base::Bind.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010040google_apis::CancelCallback RunUploadNewFile(
Ben Murdocheb525c52013-07-10 11:40:50 +010041 DriveUploaderInterface* uploader,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010042 const UploadNewFileParams& params) {
43 return uploader->UploadNewFile(params.parent_resource_id,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010044 params.local_file_path,
45 params.title,
46 params.content_type,
47 params.callback,
48 params.progress_callback);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010049}
50
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010051// Parameter struct for RunUploadExistingFile.
52struct UploadExistingFileParams {
53 std::string resource_id;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010054 base::FilePath local_file_path;
55 std::string content_type;
56 std::string etag;
Ben Murdocheb525c52013-07-10 11:40:50 +010057 UploadCompletionCallback callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010058 google_apis::ProgressCallback progress_callback;
59};
60
61// Helper function to work around the arity limitation of base::Bind.
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010062google_apis::CancelCallback RunUploadExistingFile(
Ben Murdocheb525c52013-07-10 11:40:50 +010063 DriveUploaderInterface* uploader,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010064 const UploadExistingFileParams& params) {
65 return uploader->UploadExistingFile(params.resource_id,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010066 params.local_file_path,
67 params.content_type,
68 params.etag,
69 params.callback,
70 params.progress_callback);
71}
72
73// Parameter struct for RunResumeUploadFile.
74struct ResumeUploadFileParams {
75 GURL upload_location;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010076 base::FilePath local_file_path;
77 std::string content_type;
Ben Murdocheb525c52013-07-10 11:40:50 +010078 UploadCompletionCallback callback;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010079 google_apis::ProgressCallback progress_callback;
80};
81
82// Helper function to adjust the return type.
83google_apis::CancelCallback RunResumeUploadFile(
Ben Murdocheb525c52013-07-10 11:40:50 +010084 DriveUploaderInterface* uploader,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010085 const ResumeUploadFileParams& params) {
86 return uploader->ResumeUploadFile(params.upload_location,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +010087 params.local_file_path,
88 params.content_type,
89 params.callback,
90 params.progress_callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +010091}
92
93} // namespace
94
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010095const int JobScheduler::kMaxJobCount[] = {
96 5, // METADATA_QUEUE
97 1, // FILE_QUEUE
98};
99
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100100JobScheduler::JobEntry::JobEntry(JobType type)
101 : job_info(type),
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100102 context(ClientContext(USER_INITIATED)),
103 retry_count(0) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
105}
106
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100107JobScheduler::JobEntry::~JobEntry() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
109}
110
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100111struct JobScheduler::ResumeUploadParams {
112 base::FilePath drive_file_path;
113 base::FilePath local_file_path;
114 std::string content_type;
115};
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100116
117JobScheduler::JobScheduler(
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100118 PrefService* pref_service,
Ben Murdocheb525c52013-07-10 11:40:50 +0100119 DriveServiceInterface* drive_service,
120 base::SequencedTaskRunner* blocking_task_runner)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100121 : throttle_count_(0),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100122 wait_until_(base::Time::Now()),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100123 disable_throttling_(false),
124 drive_service_(drive_service),
Ben Murdocheb525c52013-07-10 11:40:50 +0100125 uploader_(new DriveUploader(drive_service, blocking_task_runner)),
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100126 pref_service_(pref_service),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100127 weak_ptr_factory_(this) {
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100129
130 for (int i = 0; i < NUM_QUEUES; ++i)
131 queue_[i].reset(new JobQueue(kMaxJobCount[i], NUM_CONTEXT_TYPES));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100132
133 net::NetworkChangeNotifier::AddConnectionTypeObserver(this);
134}
135
136JobScheduler::~JobScheduler() {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
138
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100139 size_t num_queued_jobs = 0;
140 for (int i = 0; i < NUM_QUEUES; ++i)
141 num_queued_jobs += queue_[i]->GetNumberOfJobs();
142 DCHECK_EQ(num_queued_jobs, job_map_.size());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100143
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100144 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this);
145}
146
147std::vector<JobInfo> JobScheduler::GetJobInfoList() {
148 std::vector<JobInfo> job_info_list;
149 for (JobIDMap::iterator iter(&job_map_); !iter.IsAtEnd(); iter.Advance())
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100150 job_info_list.push_back(iter.GetCurrentValue()->job_info);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100151 return job_info_list;
152}
153
154void JobScheduler::AddObserver(JobListObserver* observer) {
155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
156 observer_list_.AddObserver(observer);
157}
158
159void JobScheduler::RemoveObserver(JobListObserver* observer) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
161 observer_list_.RemoveObserver(observer);
162}
163
164void JobScheduler::CancelJob(JobID job_id) {
165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
166
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100167 JobEntry* job = job_map_.Lookup(job_id);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100168 if (job) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100169 if (job->job_info.state == STATE_RUNNING) {
170 // If the job is running an HTTP request, cancel it via |cancel_callback|
171 // returned from the request, and wait for termination in the normal
172 // callback handler, OnJobDone.
173 if (!job->cancel_callback.is_null())
174 job->cancel_callback.Run();
175 } else {
176 AbortNotRunningJob(job, google_apis::GDATA_CANCELLED);
177 }
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100178 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100179}
180
181void JobScheduler::CancelAllJobs() {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100184 // CancelJob may remove the entry from |job_map_|. That's OK. IDMap supports
185 // removable during iteration.
186 for (JobIDMap::iterator iter(&job_map_); !iter.IsAtEnd(); iter.Advance())
187 CancelJob(iter.GetCurrentKey());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100188}
189
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100190void JobScheduler::GetAboutResource(
191 const google_apis::GetAboutResourceCallback& callback) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
193 DCHECK(!callback.is_null());
194
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100195 JobEntry* new_job = CreateNewJob(TYPE_GET_ABOUT_RESOURCE);
196 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100197 &DriveServiceInterface::GetAboutResource,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100198 base::Unretained(drive_service_),
199 base::Bind(&JobScheduler::OnGetAboutResourceJobDone,
200 weak_ptr_factory_.GetWeakPtr(),
201 new_job->job_info.job_id,
202 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100203 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100204 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100205}
206
207void JobScheduler::GetAppList(
208 const google_apis::GetAppListCallback& callback) {
209 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
210 DCHECK(!callback.is_null());
211
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100212 JobEntry* new_job = CreateNewJob(TYPE_GET_APP_LIST);
213 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100214 &DriveServiceInterface::GetAppList,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100215 base::Unretained(drive_service_),
216 base::Bind(&JobScheduler::OnGetAppListJobDone,
217 weak_ptr_factory_.GetWeakPtr(),
218 new_job->job_info.job_id,
219 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100220 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100221 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100222}
223
224void JobScheduler::GetAllResourceList(
225 const google_apis::GetResourceListCallback& callback) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 DCHECK(!callback.is_null());
228
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100229 JobEntry* new_job = CreateNewJob(TYPE_GET_ALL_RESOURCE_LIST);
230 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100231 &DriveServiceInterface::GetAllResourceList,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100232 base::Unretained(drive_service_),
233 base::Bind(&JobScheduler::OnGetResourceListJobDone,
234 weak_ptr_factory_.GetWeakPtr(),
235 new_job->job_info.job_id,
236 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100237 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100238 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100239}
240
241void JobScheduler::GetResourceListInDirectory(
242 const std::string& directory_resource_id,
243 const google_apis::GetResourceListCallback& callback) {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245 DCHECK(!callback.is_null());
246
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100247 JobEntry* new_job = CreateNewJob(
248 TYPE_GET_RESOURCE_LIST_IN_DIRECTORY);
249 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100250 &DriveServiceInterface::GetResourceListInDirectory,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100251 base::Unretained(drive_service_),
252 directory_resource_id,
253 base::Bind(&JobScheduler::OnGetResourceListJobDone,
254 weak_ptr_factory_.GetWeakPtr(),
255 new_job->job_info.job_id,
256 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100257 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100258 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100259}
260
261void JobScheduler::Search(
262 const std::string& search_query,
263 const google_apis::GetResourceListCallback& callback) {
264 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
265 DCHECK(!callback.is_null());
266
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100267 JobEntry* new_job = CreateNewJob(TYPE_SEARCH);
268 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100269 &DriveServiceInterface::Search,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100270 base::Unretained(drive_service_),
271 search_query,
272 base::Bind(&JobScheduler::OnGetResourceListJobDone,
273 weak_ptr_factory_.GetWeakPtr(),
274 new_job->job_info.job_id,
275 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100276 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100277 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100278}
279
280void JobScheduler::GetChangeList(
281 int64 start_changestamp,
282 const google_apis::GetResourceListCallback& callback) {
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
284 DCHECK(!callback.is_null());
285
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100286 JobEntry* new_job = CreateNewJob(TYPE_GET_CHANGE_LIST);
287 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100288 &DriveServiceInterface::GetChangeList,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100289 base::Unretained(drive_service_),
290 start_changestamp,
291 base::Bind(&JobScheduler::OnGetResourceListJobDone,
292 weak_ptr_factory_.GetWeakPtr(),
293 new_job->job_info.job_id,
294 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100295 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100296 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100297}
298
299void JobScheduler::ContinueGetResourceList(
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100300 const GURL& next_url,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100301 const google_apis::GetResourceListCallback& callback) {
302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
303 DCHECK(!callback.is_null());
304
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100305 JobEntry* new_job = CreateNewJob(TYPE_CONTINUE_GET_RESOURCE_LIST);
306 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100307 &DriveServiceInterface::ContinueGetResourceList,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100308 base::Unretained(drive_service_),
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +0100309 next_url,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100310 base::Bind(&JobScheduler::OnGetResourceListJobDone,
311 weak_ptr_factory_.GetWeakPtr(),
312 new_job->job_info.job_id,
313 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100314 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100315 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100316}
317
318void JobScheduler::GetResourceEntry(
319 const std::string& resource_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100320 const ClientContext& context,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100321 const google_apis::GetResourceEntryCallback& callback) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 DCHECK(!callback.is_null());
324
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100325 JobEntry* new_job = CreateNewJob(TYPE_GET_RESOURCE_ENTRY);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100326 new_job->context = context;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100327 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100328 &DriveServiceInterface::GetResourceEntry,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100329 base::Unretained(drive_service_),
330 resource_id,
331 base::Bind(&JobScheduler::OnGetResourceEntryJobDone,
332 weak_ptr_factory_.GetWeakPtr(),
333 new_job->job_info.job_id,
334 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100335 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100336 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100337}
338
Ben Murdochbbcdd452013-07-25 10:06:34 +0100339void JobScheduler::GetShareUrl(
340 const std::string& resource_id,
341 const GURL& embed_origin,
342 const ClientContext& context,
343 const google_apis::GetShareUrlCallback& callback) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345 DCHECK(!callback.is_null());
346
347 JobEntry* new_job = CreateNewJob(TYPE_GET_SHARE_URL);
348 new_job->context = context;
349 new_job->task = base::Bind(
350 &DriveServiceInterface::GetShareUrl,
351 base::Unretained(drive_service_),
352 resource_id,
353 embed_origin,
354 base::Bind(&JobScheduler::OnGetShareUrlJobDone,
355 weak_ptr_factory_.GetWeakPtr(),
356 new_job->job_info.job_id,
357 callback));
358 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
359 StartJob(new_job);
360}
361
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100362void JobScheduler::DeleteResource(
363 const std::string& resource_id,
364 const google_apis::EntryActionCallback& callback) {
365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
366 DCHECK(!callback.is_null());
367
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100368 JobEntry* new_job = CreateNewJob(TYPE_DELETE_RESOURCE);
369 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100370 &DriveServiceInterface::DeleteResource,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100371 base::Unretained(drive_service_),
372 resource_id,
373 "", // etag
374 base::Bind(&JobScheduler::OnEntryActionJobDone,
375 weak_ptr_factory_.GetWeakPtr(),
376 new_job->job_info.job_id,
377 callback));
Ben Murdocheb525c52013-07-10 11:40:50 +0100378 new_job->abort_callback = callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100379 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100380}
381
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100382void JobScheduler::CopyResource(
383 const std::string& resource_id,
384 const std::string& parent_resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100385 const std::string& new_title,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100386 const google_apis::GetResourceEntryCallback& callback) {
387 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
388 DCHECK(!callback.is_null());
389
390 JobEntry* new_job = CreateNewJob(TYPE_COPY_RESOURCE);
391 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100392 &DriveServiceInterface::CopyResource,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100393 base::Unretained(drive_service_),
394 resource_id,
395 parent_resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100396 new_title,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100397 base::Bind(&JobScheduler::OnGetResourceEntryJobDone,
398 weak_ptr_factory_.GetWeakPtr(),
399 new_job->job_info.job_id,
400 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100401 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100402 StartJob(new_job);
403}
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100404
405void JobScheduler::CopyHostedDocument(
406 const std::string& resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100407 const std::string& new_title,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100408 const google_apis::GetResourceEntryCallback& callback) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 DCHECK(!callback.is_null());
411
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100412 JobEntry* new_job = CreateNewJob(TYPE_COPY_HOSTED_DOCUMENT);
413 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100414 &DriveServiceInterface::CopyHostedDocument,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100415 base::Unretained(drive_service_),
416 resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100417 new_title,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100418 base::Bind(&JobScheduler::OnGetResourceEntryJobDone,
419 weak_ptr_factory_.GetWeakPtr(),
420 new_job->job_info.job_id,
421 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100422 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100423 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100424}
425
426void JobScheduler::RenameResource(
427 const std::string& resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100428 const std::string& new_title,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100429 const google_apis::EntryActionCallback& callback) {
430 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431 DCHECK(!callback.is_null());
432
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100433 JobEntry* new_job = CreateNewJob(TYPE_RENAME_RESOURCE);
434 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100435 &DriveServiceInterface::RenameResource,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100436 base::Unretained(drive_service_),
437 resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100438 new_title,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100439 base::Bind(&JobScheduler::OnEntryActionJobDone,
440 weak_ptr_factory_.GetWeakPtr(),
441 new_job->job_info.job_id,
442 callback));
Ben Murdocheb525c52013-07-10 11:40:50 +0100443 new_job->abort_callback = callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100444 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100445}
446
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100447void JobScheduler::TouchResource(
448 const std::string& resource_id,
449 const base::Time& modified_date,
450 const base::Time& last_viewed_by_me_date,
451 const google_apis::GetResourceEntryCallback& callback) {
452 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453 DCHECK(!callback.is_null());
454
455 JobEntry* new_job = CreateNewJob(TYPE_TOUCH_RESOURCE);
456 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100457 &DriveServiceInterface::TouchResource,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100458 base::Unretained(drive_service_),
459 resource_id,
460 modified_date,
461 last_viewed_by_me_date,
462 base::Bind(&JobScheduler::OnGetResourceEntryJobDone,
463 weak_ptr_factory_.GetWeakPtr(),
464 new_job->job_info.job_id,
465 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100466 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100467 StartJob(new_job);
468}
469
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100470void JobScheduler::AddResourceToDirectory(
471 const std::string& parent_resource_id,
472 const std::string& resource_id,
473 const google_apis::EntryActionCallback& callback) {
474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
475 DCHECK(!callback.is_null());
476
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100477 JobEntry* new_job = CreateNewJob(TYPE_ADD_RESOURCE_TO_DIRECTORY);
478 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100479 &DriveServiceInterface::AddResourceToDirectory,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100480 base::Unretained(drive_service_),
481 parent_resource_id,
482 resource_id,
483 base::Bind(&JobScheduler::OnEntryActionJobDone,
484 weak_ptr_factory_.GetWeakPtr(),
485 new_job->job_info.job_id,
486 callback));
Ben Murdocheb525c52013-07-10 11:40:50 +0100487 new_job->abort_callback = callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100488 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100489}
490
491void JobScheduler::RemoveResourceFromDirectory(
492 const std::string& parent_resource_id,
493 const std::string& resource_id,
494 const google_apis::EntryActionCallback& callback) {
495 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
496
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100497 JobEntry* new_job = CreateNewJob(TYPE_REMOVE_RESOURCE_FROM_DIRECTORY);
498 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100499 &DriveServiceInterface::RemoveResourceFromDirectory,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100500 base::Unretained(drive_service_),
501 parent_resource_id,
502 resource_id,
503 base::Bind(&JobScheduler::OnEntryActionJobDone,
504 weak_ptr_factory_.GetWeakPtr(),
505 new_job->job_info.job_id,
506 callback));
Ben Murdocheb525c52013-07-10 11:40:50 +0100507 new_job->abort_callback = callback;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100508 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100509}
510
511void JobScheduler::AddNewDirectory(
512 const std::string& parent_resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100513 const std::string& directory_title,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100514 const google_apis::GetResourceEntryCallback& callback) {
515 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
516
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100517 JobEntry* new_job = CreateNewJob(TYPE_ADD_NEW_DIRECTORY);
518 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100519 &DriveServiceInterface::AddNewDirectory,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100520 base::Unretained(drive_service_),
521 parent_resource_id,
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100522 directory_title,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100523 base::Bind(&JobScheduler::OnGetResourceEntryJobDone,
524 weak_ptr_factory_.GetWeakPtr(),
525 new_job->job_info.job_id,
526 callback));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100527 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100528 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100529}
530
531JobID JobScheduler::DownloadFile(
532 const base::FilePath& virtual_path,
533 const base::FilePath& local_cache_path,
Ben Murdocheb525c52013-07-10 11:40:50 +0100534 const std::string& resource_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100535 const ClientContext& context,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100536 const google_apis::DownloadActionCallback& download_action_callback,
537 const google_apis::GetContentCallback& get_content_callback) {
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100540 JobEntry* new_job = CreateNewJob(TYPE_DOWNLOAD_FILE);
541 new_job->job_info.file_path = virtual_path;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100542 new_job->context = context;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100543 new_job->task = base::Bind(
Ben Murdocheb525c52013-07-10 11:40:50 +0100544 &DriveServiceInterface::DownloadFile,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100545 base::Unretained(drive_service_),
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100546 local_cache_path,
Ben Murdocheb525c52013-07-10 11:40:50 +0100547 resource_id,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100548 base::Bind(&JobScheduler::OnDownloadActionJobDone,
549 weak_ptr_factory_.GetWeakPtr(),
550 new_job->job_info.job_id,
551 download_action_callback),
552 get_content_callback,
553 base::Bind(&JobScheduler::UpdateProgress,
554 weak_ptr_factory_.GetWeakPtr(),
555 new_job->job_info.job_id));
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100556 new_job->abort_callback =
557 google_apis::CreateErrorRunCallback(download_action_callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100558 StartJob(new_job);
559 return new_job->job_info.job_id;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100560}
561
562void JobScheduler::UploadNewFile(
563 const std::string& parent_resource_id,
564 const base::FilePath& drive_file_path,
565 const base::FilePath& local_file_path,
566 const std::string& title,
567 const std::string& content_type,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100568 const ClientContext& context,
569 const google_apis::GetResourceEntryCallback& callback) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
571
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100572 JobEntry* new_job = CreateNewJob(TYPE_UPLOAD_NEW_FILE);
573 new_job->job_info.file_path = drive_file_path;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100574 new_job->context = context;
575
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100576 UploadNewFileParams params;
577 params.parent_resource_id = parent_resource_id;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100578 params.local_file_path = local_file_path;
579 params.title = title;
580 params.content_type = content_type;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100581
582 ResumeUploadParams resume_params;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100583 resume_params.local_file_path = params.local_file_path;
584 resume_params.content_type = params.content_type;
585
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100586 params.callback = base::Bind(&JobScheduler::OnUploadCompletionJobDone,
587 weak_ptr_factory_.GetWeakPtr(),
588 new_job->job_info.job_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100589 resume_params,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100590 callback);
591 params.progress_callback = base::Bind(&JobScheduler::UpdateProgress,
592 weak_ptr_factory_.GetWeakPtr(),
593 new_job->job_info.job_id);
594 new_job->task = base::Bind(&RunUploadNewFile, uploader_.get(), params);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100595 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100596 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100597}
598
599void JobScheduler::UploadExistingFile(
600 const std::string& resource_id,
601 const base::FilePath& drive_file_path,
602 const base::FilePath& local_file_path,
603 const std::string& content_type,
604 const std::string& etag,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100605 const ClientContext& context,
606 const google_apis::GetResourceEntryCallback& callback) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100607 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
608
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100609 JobEntry* new_job = CreateNewJob(TYPE_UPLOAD_EXISTING_FILE);
610 new_job->job_info.file_path = drive_file_path;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100611 new_job->context = context;
612
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100613 UploadExistingFileParams params;
614 params.resource_id = resource_id;
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100615 params.local_file_path = local_file_path;
616 params.content_type = content_type;
617 params.etag = etag;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100618
619 ResumeUploadParams resume_params;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100620 resume_params.local_file_path = params.local_file_path;
621 resume_params.content_type = params.content_type;
622
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100623 params.callback = base::Bind(&JobScheduler::OnUploadCompletionJobDone,
624 weak_ptr_factory_.GetWeakPtr(),
625 new_job->job_info.job_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100626 resume_params,
627 callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100628 params.progress_callback = base::Bind(&JobScheduler::UpdateProgress,
629 weak_ptr_factory_.GetWeakPtr(),
630 new_job->job_info.job_id);
631 new_job->task = base::Bind(&RunUploadExistingFile, uploader_.get(), params);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100632 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100633 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100634}
635
636void JobScheduler::CreateFile(
637 const std::string& parent_resource_id,
638 const base::FilePath& drive_file_path,
639 const std::string& title,
640 const std::string& content_type,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100641 const ClientContext& context,
642 const google_apis::GetResourceEntryCallback& callback) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100643 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
644
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100645 const base::FilePath kDevNull(FILE_PATH_LITERAL("/dev/null"));
646
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100647 JobEntry* new_job = CreateNewJob(TYPE_CREATE_FILE);
648 new_job->job_info.file_path = drive_file_path;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100649 new_job->context = context;
650
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100651 UploadNewFileParams params;
652 params.parent_resource_id = parent_resource_id;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100653 params.local_file_path = kDevNull; // Upload an empty file.
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100654 params.title = title;
655 params.content_type = content_type;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100656
657 ResumeUploadParams resume_params;
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100658 resume_params.local_file_path = params.local_file_path;
659 resume_params.content_type = params.content_type;
660
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100661 params.callback = base::Bind(&JobScheduler::OnUploadCompletionJobDone,
662 weak_ptr_factory_.GetWeakPtr(),
663 new_job->job_info.job_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100664 resume_params,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100665 callback);
666 params.progress_callback = google_apis::ProgressCallback();
667
668 new_job->task = base::Bind(&RunUploadNewFile, uploader_.get(), params);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100669 new_job->abort_callback = google_apis::CreateErrorRunCallback(callback);
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100670 StartJob(new_job);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100671}
672
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100673JobScheduler::JobEntry* JobScheduler::CreateNewJob(JobType type) {
674 JobEntry* job = new JobEntry(type);
675 job->job_info.job_id = job_map_.Add(job); // Takes the ownership of |job|.
676 return job;
677}
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100678
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100679void JobScheduler::StartJob(JobEntry* job) {
680 DCHECK(!job->task.is_null());
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100681
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100682 QueueJob(job->job_info.job_id);
683 NotifyJobAdded(job->job_info);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100684 DoJobLoop(GetJobQueueType(job->job_info.job_type));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100685}
686
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100687void JobScheduler::QueueJob(JobID job_id) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100688 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100690 JobEntry* job_entry = job_map_.Lookup(job_id);
691 DCHECK(job_entry);
692 const JobInfo& job_info = job_entry->job_info;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100693
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100694 QueueType queue_type = GetJobQueueType(job_info.job_type);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100695 queue_[queue_type]->Push(job_id, job_entry->context.type);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100696
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100697 const std::string retry_prefix = job_entry->retry_count > 0 ?
698 base::StringPrintf(" (retry %d)", job_entry->retry_count) : "";
699 util::Log(logging::LOG_INFO,
700 "Job queued%s: %s - %s",
701 retry_prefix.c_str(),
702 job_info.ToString().c_str(),
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100703 GetQueueInfo(queue_type).c_str());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100704}
705
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100706void JobScheduler::DoJobLoop(QueueType queue_type) {
707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708
Ben Murdocheb525c52013-07-10 11:40:50 +0100709 const int accepted_priority = GetCurrentAcceptedPriority(queue_type);
710
711 // Abort all USER_INITAITED jobs when not accepted.
712 if (accepted_priority < USER_INITIATED) {
713 std::vector<JobID> jobs;
714 queue_[queue_type]->GetQueuedJobs(USER_INITIATED, &jobs);
715 for (size_t i = 0; i < jobs.size(); ++i) {
716 JobEntry* job = job_map_.Lookup(jobs[i]);
717 DCHECK(job);
718 AbortNotRunningJob(job, google_apis::GDATA_NO_CONNECTION);
719 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100720 }
721
Ben Murdochbb1529c2013-08-08 10:24:53 +0100722 // Wait when throttled.
723 const base::Time now = base::Time::Now();
724 if (now < wait_until_) {
725 base::MessageLoopProxy::current()->PostDelayedTask(
726 FROM_HERE,
727 base::Bind(&JobScheduler::DoJobLoop,
728 weak_ptr_factory_.GetWeakPtr(),
729 queue_type),
730 wait_until_ - now);
731 return;
732 }
733
Ben Murdocheb525c52013-07-10 11:40:50 +0100734 // Run the job with the highest priority in the queue.
735 JobID job_id = -1;
736 if (!queue_[queue_type]->PopForRun(accepted_priority, &job_id))
737 return;
738
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100739 JobEntry* entry = job_map_.Lookup(job_id);
740 DCHECK(entry);
741
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100742 JobInfo* job_info = &entry->job_info;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100743 job_info->state = STATE_RUNNING;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100744 job_info->start_time = now;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100745 NotifyJobUpdated(*job_info);
746
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100747 entry->cancel_callback = entry->task.Run();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100748
Ben Murdochbb1529c2013-08-08 10:24:53 +0100749 UpdateWait();
750
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100751 util::Log(logging::LOG_INFO,
752 "Job started: %s - %s",
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100753 job_info->ToString().c_str(),
754 GetQueueInfo(queue_type).c_str());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100755}
756
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100757int JobScheduler::GetCurrentAcceptedPriority(QueueType queue_type) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100758 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
759
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100760 const int kNoJobShouldRun = -1;
761
Ben Murdocheb525c52013-07-10 11:40:50 +0100762 // Should stop if Drive was disabled while running the fetch loop.
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100763 if (pref_service_->GetBoolean(prefs::kDisableDrive))
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100764 return kNoJobShouldRun;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100765
766 // Should stop if the network is not online.
767 if (net::NetworkChangeNotifier::IsOffline())
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100768 return kNoJobShouldRun;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100769
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100770 // For the file queue, if it is on cellular network, only user initiated
771 // operations are allowed to start.
772 if (queue_type == FILE_QUEUE &&
Ben Murdoch7dbb3d52013-07-17 14:55:54 +0100773 pref_service_->GetBoolean(prefs::kDisableDriveOverCellular) &&
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100774 net::NetworkChangeNotifier::IsConnectionCellular(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100775 net::NetworkChangeNotifier::GetConnectionType()))
776 return USER_INITIATED;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100777
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100778 // Otherwise, every operations including background tasks are allowed.
779 return BACKGROUND;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100780}
781
Ben Murdochbb1529c2013-08-08 10:24:53 +0100782void JobScheduler::UpdateWait() {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100783 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
784
Ben Murdochbb1529c2013-08-08 10:24:53 +0100785 if (disable_throttling_ || throttle_count_ == 0)
786 return;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100787
Ben Murdochbb1529c2013-08-08 10:24:53 +0100788 // Exponential backoff: https://developers.google.com/drive/handle-errors.
789 base::TimeDelta delay =
Ben Murdocheb525c52013-07-10 11:40:50 +0100790 base::TimeDelta::FromSeconds(1 << (throttle_count_ - 1)) +
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100791 base::TimeDelta::FromMilliseconds(base::RandInt(0, 1000));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100792 VLOG(1) << "Throttling for " << delay.InMillisecondsF();
793
Ben Murdochbb1529c2013-08-08 10:24:53 +0100794 wait_until_ = std::max(wait_until_, base::Time::Now() + delay);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100795}
796
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100797bool JobScheduler::OnJobDone(JobID job_id, google_apis::GDataErrorCode error) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
799
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100800 JobEntry* job_entry = job_map_.Lookup(job_id);
801 DCHECK(job_entry);
802 JobInfo* job_info = &job_entry->job_info;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100803 QueueType queue_type = GetJobQueueType(job_info->job_type);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100804 queue_[queue_type]->MarkFinished(job_id);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100805
806 const base::TimeDelta elapsed = base::Time::Now() - job_info->start_time;
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +0100807 bool success = (GDataToFileError(error) == FILE_ERROR_OK);
808 util::Log(success ? logging::LOG_INFO : logging::LOG_WARNING,
809 "Job done: %s => %s (elapsed time: %sms) - %s",
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100810 job_info->ToString().c_str(),
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100811 GDataErrorCodeToString(error).c_str(),
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100812 base::Int64ToString(elapsed.InMilliseconds()).c_str(),
813 GetQueueInfo(queue_type).c_str());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100814
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100815 // Retry, depending on the error.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100816 const bool is_server_error =
817 error == google_apis::HTTP_SERVICE_UNAVAILABLE ||
818 error == google_apis::HTTP_INTERNAL_SERVER_ERROR;
819 if (is_server_error) {
820 if (throttle_count_ < kMaxThrottleCount)
821 ++throttle_count_;
822 UpdateWait();
823 } else {
824 throttle_count_ = 0;
825 }
826
827 const bool should_retry =
828 is_server_error && job_entry->retry_count < kMaxRetryCount;
829 if (should_retry) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100830 job_entry->cancel_callback.Reset();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100831 job_info->state = STATE_RETRY;
832 NotifyJobUpdated(*job_info);
833
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100834 ++job_entry->retry_count;
835
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100836 // Requeue the job.
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100837 QueueJob(job_id);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100838 } else {
839 NotifyJobDone(*job_info, error);
840 // The job has finished, no retry will happen in the scheduler. Now we can
Ben Murdocheb525c52013-07-10 11:40:50 +0100841 // remove the job info from the map.
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100842 job_map_.Remove(job_id);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100843 }
Ben Murdochbb1529c2013-08-08 10:24:53 +0100844
845 // Post a task to continue the job loop. This allows us to finish handling
846 // the current job before starting the next one.
847 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
848 base::Bind(&JobScheduler::DoJobLoop,
849 weak_ptr_factory_.GetWeakPtr(),
850 queue_type));
851 return !should_retry;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100852}
853
854void JobScheduler::OnGetResourceListJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100855 JobID job_id,
856 const google_apis::GetResourceListCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100857 google_apis::GDataErrorCode error,
858 scoped_ptr<google_apis::ResourceList> resource_list) {
859 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100860 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100861
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100862 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100863 callback.Run(error, resource_list.Pass());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100864}
865
866void JobScheduler::OnGetResourceEntryJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100867 JobID job_id,
868 const google_apis::GetResourceEntryCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100869 google_apis::GDataErrorCode error,
870 scoped_ptr<google_apis::ResourceEntry> entry) {
871 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100872 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100873
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100874 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100875 callback.Run(error, entry.Pass());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100876}
877
878void JobScheduler::OnGetAboutResourceJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100879 JobID job_id,
880 const google_apis::GetAboutResourceCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100881 google_apis::GDataErrorCode error,
882 scoped_ptr<google_apis::AboutResource> about_resource) {
883 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100884 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100885
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100886 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100887 callback.Run(error, about_resource.Pass());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100888}
889
Ben Murdochbbcdd452013-07-25 10:06:34 +0100890void JobScheduler::OnGetShareUrlJobDone(
891 JobID job_id,
892 const google_apis::GetShareUrlCallback& callback,
893 google_apis::GDataErrorCode error,
894 const GURL& share_url) {
895 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
896 DCHECK(!callback.is_null());
897
898 if (OnJobDone(job_id, error))
899 callback.Run(error, share_url);
900}
901
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100902void JobScheduler::OnGetAppListJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100903 JobID job_id,
904 const google_apis::GetAppListCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100905 google_apis::GDataErrorCode error,
906 scoped_ptr<google_apis::AppList> app_list) {
907 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100908 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100909
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100910 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100911 callback.Run(error, app_list.Pass());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100912}
913
914void JobScheduler::OnEntryActionJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100915 JobID job_id,
916 const google_apis::EntryActionCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100917 google_apis::GDataErrorCode error) {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100918 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
919 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100920
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100921 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100922 callback.Run(error);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100923}
924
925void JobScheduler::OnDownloadActionJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100926 JobID job_id,
927 const google_apis::DownloadActionCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100928 google_apis::GDataErrorCode error,
929 const base::FilePath& temp_file) {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100930 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
931 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100932
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100933 if (OnJobDone(job_id, error))
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100934 callback.Run(error, temp_file);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100935}
936
937void JobScheduler::OnUploadCompletionJobDone(
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100938 JobID job_id,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100939 const ResumeUploadParams& resume_params,
940 const google_apis::GetResourceEntryCallback& callback,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100941 google_apis::GDataErrorCode error,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100942 const GURL& upload_location,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100943 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100944 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
945 DCHECK(!callback.is_null());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100946
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100947 if (!upload_location.is_empty()) {
948 // If upload_location is available, update the task to resume the
949 // upload process from the terminated point.
950 // When we need to retry, the error code should be HTTP_SERVICE_UNAVAILABLE
951 // so OnJobDone called below will be in charge to re-queue the job.
952 JobEntry* job_entry = job_map_.Lookup(job_id);
953 DCHECK(job_entry);
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100954
955 ResumeUploadFileParams params;
956 params.upload_location = upload_location;
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100957 params.local_file_path = resume_params.local_file_path;
958 params.content_type = resume_params.content_type;
Ben Murdoch32409262013-08-07 11:04:47 +0100959 params.callback = base::Bind(&JobScheduler::OnResumeUploadFileDone,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100960 weak_ptr_factory_.GetWeakPtr(),
961 job_id,
Ben Murdoch32409262013-08-07 11:04:47 +0100962 job_entry->task,
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100963 callback);
964 params.progress_callback = base::Bind(&JobScheduler::UpdateProgress,
965 weak_ptr_factory_.GetWeakPtr(),
966 job_id);
967 job_entry->task = base::Bind(&RunResumeUploadFile, uploader_.get(), params);
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100968 }
969
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100970 if (OnJobDone(job_id, error))
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100971 callback.Run(error, resource_entry.Pass());
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100972}
973
Ben Murdoch32409262013-08-07 11:04:47 +0100974void JobScheduler::OnResumeUploadFileDone(
975 JobID job_id,
976 const base::Callback<google_apis::CancelCallback()>& original_task,
977 const google_apis::GetResourceEntryCallback& callback,
978 google_apis::GDataErrorCode error,
979 const GURL& upload_location,
980 scoped_ptr<google_apis::ResourceEntry> resource_entry) {
981 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
982 DCHECK(!original_task.is_null());
983 DCHECK(!callback.is_null());
984
985 if (upload_location.is_empty()) {
986 // If upload_location is not available, we should discard it and stop trying
987 // to resume. Restore the original task.
988 JobEntry* job_entry = job_map_.Lookup(job_id);
989 DCHECK(job_entry);
990 job_entry->task = original_task;
991 }
992
993 if (OnJobDone(job_id, error))
994 callback.Run(error, resource_entry.Pass());
995}
996
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100997void JobScheduler::UpdateProgress(JobID job_id, int64 progress, int64 total) {
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100998 JobEntry* job_entry = job_map_.Lookup(job_id);
999 DCHECK(job_entry);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001000
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +01001001 job_entry->job_info.num_completed_bytes = progress;
1002 job_entry->job_info.num_total_bytes = total;
1003 NotifyJobUpdated(job_entry->job_info);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001004}
1005
1006void JobScheduler::OnConnectionTypeChanged(
1007 net::NetworkChangeNotifier::ConnectionType type) {
1008 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1009
Ben Murdocheb525c52013-07-10 11:40:50 +01001010 // Resume the job loop.
1011 // Note that we don't need to check the network connection status as it will
1012 // be checked in GetCurrentAcceptedPriority().
1013 for (int i = METADATA_QUEUE; i < NUM_QUEUES; ++i)
1014 DoJobLoop(static_cast<QueueType>(i));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001015}
1016
1017JobScheduler::QueueType JobScheduler::GetJobQueueType(JobType type) {
1018 switch (type) {
1019 case TYPE_GET_ABOUT_RESOURCE:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001020 case TYPE_GET_APP_LIST:
1021 case TYPE_GET_ALL_RESOURCE_LIST:
1022 case TYPE_GET_RESOURCE_LIST_IN_DIRECTORY:
1023 case TYPE_SEARCH:
1024 case TYPE_GET_CHANGE_LIST:
1025 case TYPE_CONTINUE_GET_RESOURCE_LIST:
1026 case TYPE_GET_RESOURCE_ENTRY:
Ben Murdochbbcdd452013-07-25 10:06:34 +01001027 case TYPE_GET_SHARE_URL:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001028 case TYPE_DELETE_RESOURCE:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001029 case TYPE_COPY_RESOURCE:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001030 case TYPE_COPY_HOSTED_DOCUMENT:
1031 case TYPE_RENAME_RESOURCE:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001032 case TYPE_TOUCH_RESOURCE:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001033 case TYPE_ADD_RESOURCE_TO_DIRECTORY:
1034 case TYPE_REMOVE_RESOURCE_FROM_DIRECTORY:
1035 case TYPE_ADD_NEW_DIRECTORY:
1036 case TYPE_CREATE_FILE:
1037 return METADATA_QUEUE;
1038
1039 case TYPE_DOWNLOAD_FILE:
1040 case TYPE_UPLOAD_NEW_FILE:
1041 case TYPE_UPLOAD_EXISTING_FILE:
1042 return FILE_QUEUE;
1043 }
1044 NOTREACHED();
1045 return FILE_QUEUE;
1046}
1047
Ben Murdocheb525c52013-07-10 11:40:50 +01001048void JobScheduler::AbortNotRunningJob(JobEntry* job,
1049 google_apis::GDataErrorCode error) {
1050 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1051
1052 const base::TimeDelta elapsed = base::Time::Now() - job->job_info.start_time;
1053 const QueueType queue_type = GetJobQueueType(job->job_info.job_type);
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01001054 util::Log(logging::LOG_INFO,
1055 "Job aborted: %s => %s (elapsed time: %sms) - %s",
Ben Murdocheb525c52013-07-10 11:40:50 +01001056 job->job_info.ToString().c_str(),
1057 GDataErrorCodeToString(error).c_str(),
1058 base::Int64ToString(elapsed.InMilliseconds()).c_str(),
1059 GetQueueInfo(queue_type).c_str());
1060
1061 base::Callback<void(google_apis::GDataErrorCode)> callback =
1062 job->abort_callback;
1063 queue_[GetJobQueueType(job->job_info.job_type)]->Remove(job->job_info.job_id);
Ben Murdoch7dbb3d52013-07-17 14:55:54 +01001064 NotifyJobDone(job->job_info, error);
Ben Murdocheb525c52013-07-10 11:40:50 +01001065 job_map_.Remove(job->job_info.job_id);
1066 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
1067 base::Bind(callback, error));
1068}
1069
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001070void JobScheduler::NotifyJobAdded(const JobInfo& job_info) {
1071 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1072 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobAdded(job_info));
1073}
1074
1075void JobScheduler::NotifyJobDone(const JobInfo& job_info,
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +01001076 google_apis::GDataErrorCode error) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001077 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1078 FOR_EACH_OBSERVER(JobListObserver, observer_list_,
Torne (Richard Coles)a36e5922013-08-05 13:57:33 +01001079 OnJobDone(job_info, GDataToFileError(error)));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001080}
1081
1082void JobScheduler::NotifyJobUpdated(const JobInfo& job_info) {
1083 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1084 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobUpdated(job_info));
1085}
1086
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +01001087std::string JobScheduler::GetQueueInfo(QueueType type) const {
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001088 return QueueTypeToString(type) + " " + queue_[type]->ToString();
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +01001089}
1090
1091// static
1092std::string JobScheduler::QueueTypeToString(QueueType type) {
1093 switch (type) {
1094 case METADATA_QUEUE:
1095 return "METADATA_QUEUE";
1096 case FILE_QUEUE:
1097 return "FILE_QUEUE";
1098 case NUM_QUEUES:
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +01001099 break; // This value is just a sentinel. Should never be used.
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +01001100 }
1101 NOTREACHED();
1102 return "";
1103}
1104
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01001105} // namespace drive