OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/chromeos/drive/drive_scheduler.h" | |
6 | |
7 #include <math.h> | |
8 | |
9 #include "base/message_loop.h" | |
10 #include "base/prefs/pref_service.h" | |
11 #include "base/rand_util.h" | |
12 #include "base/stl_util.h" | |
13 #include "base/strings/string_number_conversions.h" | |
14 #include "chrome/browser/chromeos/drive/drive_file_system_util.h" | |
15 #include "chrome/browser/chromeos/drive/logging.h" | |
16 #include "chrome/browser/google_apis/drive_api_parser.h" | |
17 #include "chrome/browser/google_apis/gdata_wapi_parser.h" | |
18 #include "chrome/browser/profiles/profile.h" | |
19 #include "chrome/common/pref_names.h" | |
20 #include "content/public/browser/browser_thread.h" | |
21 | |
22 using content::BrowserThread; | |
23 | |
24 namespace drive { | |
25 | |
26 namespace { | |
27 const int kMaxThrottleCount = 5; | |
28 } | |
29 | |
30 const int DriveScheduler::kMaxJobCount[] = { | |
31 5, // METADATA_QUEUE | |
32 1, // FILE_QUEUE | |
33 }; | |
34 | |
35 DriveScheduler::QueueEntry::QueueEntry() | |
36 : job_id(-1), | |
37 context(DriveClientContext(USER_INITIATED)) { | |
38 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
39 } | |
40 | |
41 DriveScheduler::QueueEntry::~QueueEntry() { | |
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
43 } | |
44 | |
45 bool DriveScheduler::QueueEntry::Compare( | |
46 const DriveScheduler::QueueEntry* left, | |
47 const DriveScheduler::QueueEntry* right) { | |
48 return (left->context.type < right->context.type); | |
49 } | |
50 | |
51 DriveScheduler::DriveScheduler( | |
52 Profile* profile, | |
53 google_apis::DriveServiceInterface* drive_service) | |
54 : throttle_count_(0), | |
55 disable_throttling_(false), | |
56 drive_service_(drive_service), | |
57 uploader_(new google_apis::DriveUploader(drive_service)), | |
58 profile_(profile), | |
59 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
61 for (int i = 0; i < NUM_QUEUES; ++i) { | |
62 jobs_running_[i] = 0; | |
63 } | |
64 | |
65 net::NetworkChangeNotifier::AddConnectionTypeObserver(this); | |
66 } | |
67 | |
68 DriveScheduler::~DriveScheduler() { | |
69 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
70 | |
71 size_t num_pending_jobs = 0; | |
72 size_t num_running_jobs = 0; | |
73 for (int i = 0; i < NUM_QUEUES; ++i) { | |
74 num_pending_jobs += queue_[i].size(); | |
75 num_running_jobs += jobs_running_[i]; | |
76 } | |
77 DCHECK_EQ(num_pending_jobs + num_running_jobs, job_map_.size()); | |
78 | |
79 for (int i = 0; i < NUM_QUEUES; ++i) { | |
80 STLDeleteElements(&queue_[i]); | |
81 } | |
82 net::NetworkChangeNotifier::RemoveConnectionTypeObserver(this); | |
83 } | |
84 | |
85 std::vector<JobInfo> DriveScheduler::GetJobInfoList() { | |
86 std::vector<JobInfo> job_info_list; | |
87 for (JobIDMap::iterator iter(&job_map_); !iter.IsAtEnd(); iter.Advance()) | |
88 job_info_list.push_back(*iter.GetCurrentValue()); | |
89 return job_info_list; | |
90 } | |
91 | |
92 void DriveScheduler::AddObserver(JobListObserver* observer) { | |
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
94 observer_list_.AddObserver(observer); | |
95 } | |
96 | |
97 void DriveScheduler::RemoveObserver(JobListObserver* observer) { | |
98 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
99 observer_list_.RemoveObserver(observer); | |
100 } | |
101 | |
102 void DriveScheduler::CancelJob(JobID job_id) { | |
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
104 | |
105 // TODO(kinaba): Move the cancellation feature from DriveService | |
106 // to DriveScheduler. In particular, implement cancel based on job_id. | |
107 // crbug.com/231029 | |
108 JobInfo* info = job_map_.Lookup(job_id); | |
109 if (info) | |
110 drive_service_->CancelForFilePath(info->file_path); | |
111 } | |
112 | |
113 void DriveScheduler::CancelAllJobs() { | |
114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
115 | |
116 // TODO(kinaba): Move the cancellation feature from DriveService | |
117 // to DriveScheduler. | |
118 drive_service_->CancelAll(); | |
119 } | |
120 | |
121 void DriveScheduler::GetAccountMetadata( | |
122 const google_apis::GetAccountMetadataCallback& callback) { | |
123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
124 DCHECK(!callback.is_null()); | |
125 | |
126 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
127 new_job->get_account_metadata_callback = callback; | |
128 | |
129 StartNewJob(new_job.Pass(), TYPE_GET_ACCOUNT_METADATA); | |
130 } | |
131 | |
132 void DriveScheduler::GetAboutResource( | |
133 const google_apis::GetAboutResourceCallback& callback) { | |
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
135 DCHECK(!callback.is_null()); | |
136 | |
137 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
138 new_job->get_about_resource_callback = callback; | |
139 | |
140 StartNewJob(new_job.Pass(), TYPE_GET_ABOUT_RESOURCE); | |
141 } | |
142 | |
143 void DriveScheduler::GetAppList( | |
144 const google_apis::GetAppListCallback& callback) { | |
145 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
146 DCHECK(!callback.is_null()); | |
147 | |
148 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
149 new_job->get_app_list_callback = callback; | |
150 | |
151 StartNewJob(new_job.Pass(), TYPE_GET_APP_LIST); | |
152 } | |
153 | |
154 void DriveScheduler::GetAllResourceList( | |
155 const google_apis::GetResourceListCallback& callback) { | |
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
157 DCHECK(!callback.is_null()); | |
158 | |
159 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
160 new_job->get_resource_list_callback = callback; | |
161 | |
162 StartNewJob(new_job.Pass(), TYPE_GET_ALL_RESOURCE_LIST); | |
163 } | |
164 | |
165 void DriveScheduler::GetResourceListInDirectory( | |
166 const std::string& directory_resource_id, | |
167 const google_apis::GetResourceListCallback& callback) { | |
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
169 DCHECK(!callback.is_null()); | |
170 | |
171 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
172 new_job->directory_resource_id = directory_resource_id; | |
173 new_job->get_resource_list_callback = callback; | |
174 | |
175 StartNewJob(new_job.Pass(), TYPE_GET_RESOURCE_LIST_IN_DIRECTORY); | |
176 } | |
177 | |
178 void DriveScheduler::Search( | |
179 const std::string& search_query, | |
180 const google_apis::GetResourceListCallback& callback) { | |
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
182 DCHECK(!callback.is_null()); | |
183 | |
184 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
185 new_job->search_query = search_query; | |
186 new_job->get_resource_list_callback = callback; | |
187 | |
188 StartNewJob(new_job.Pass(), TYPE_SEARCH); | |
189 } | |
190 | |
191 void DriveScheduler::GetChangeList( | |
192 int64 start_changestamp, | |
193 const google_apis::GetResourceListCallback& callback) { | |
194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
195 DCHECK(!callback.is_null()); | |
196 | |
197 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
198 new_job->start_changestamp = start_changestamp; | |
199 new_job->get_resource_list_callback = callback; | |
200 | |
201 StartNewJob(new_job.Pass(), TYPE_GET_CHANGE_LIST); | |
202 } | |
203 | |
204 void DriveScheduler::ContinueGetResourceList( | |
205 const GURL& feed_url, | |
206 const google_apis::GetResourceListCallback& callback) { | |
207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
208 DCHECK(!callback.is_null()); | |
209 | |
210 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
211 new_job->feed_url = feed_url; | |
212 new_job->get_resource_list_callback = callback; | |
213 | |
214 StartNewJob(new_job.Pass(), TYPE_CONTINUE_GET_RESOURCE_LIST); | |
215 } | |
216 | |
217 void DriveScheduler::GetResourceEntry( | |
218 const std::string& resource_id, | |
219 const DriveClientContext& context, | |
220 const google_apis::GetResourceEntryCallback& callback) { | |
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
222 DCHECK(!callback.is_null()); | |
223 | |
224 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
225 new_job->resource_id = resource_id; | |
226 new_job->context = context; | |
227 new_job->get_resource_entry_callback = callback; | |
228 | |
229 StartNewJob(new_job.Pass(), TYPE_GET_RESOURCE_ENTRY); | |
230 } | |
231 | |
232 void DriveScheduler::DeleteResource( | |
233 const std::string& resource_id, | |
234 const google_apis::EntryActionCallback& callback) { | |
235 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
236 DCHECK(!callback.is_null()); | |
237 | |
238 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
239 new_job->resource_id = resource_id; | |
240 new_job->entry_action_callback = callback; | |
241 | |
242 StartNewJob(new_job.Pass(), TYPE_DELETE_RESOURCE); | |
243 } | |
244 | |
245 | |
246 void DriveScheduler::CopyHostedDocument( | |
247 const std::string& resource_id, | |
248 const std::string& new_name, | |
249 const google_apis::GetResourceEntryCallback& callback) { | |
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
251 DCHECK(!callback.is_null()); | |
252 | |
253 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
254 new_job->resource_id = resource_id; | |
255 new_job->new_name = new_name; | |
256 new_job->get_resource_entry_callback = callback; | |
257 | |
258 StartNewJob(new_job.Pass(), TYPE_COPY_HOSTED_DOCUMENT); | |
259 } | |
260 | |
261 void DriveScheduler::RenameResource( | |
262 const std::string& resource_id, | |
263 const std::string& new_name, | |
264 const google_apis::EntryActionCallback& callback) { | |
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
266 DCHECK(!callback.is_null()); | |
267 | |
268 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
269 new_job->resource_id = resource_id; | |
270 new_job->new_name = new_name; | |
271 new_job->entry_action_callback = callback; | |
272 | |
273 StartNewJob(new_job.Pass(), TYPE_RENAME_RESOURCE); | |
274 } | |
275 | |
276 void DriveScheduler::AddResourceToDirectory( | |
277 const std::string& parent_resource_id, | |
278 const std::string& resource_id, | |
279 const google_apis::EntryActionCallback& callback) { | |
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
281 DCHECK(!callback.is_null()); | |
282 | |
283 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
284 new_job->parent_resource_id = parent_resource_id; | |
285 new_job->resource_id = resource_id; | |
286 new_job->entry_action_callback = callback; | |
287 | |
288 StartNewJob(new_job.Pass(), TYPE_ADD_RESOURCE_TO_DIRECTORY); | |
289 } | |
290 | |
291 void DriveScheduler::RemoveResourceFromDirectory( | |
292 const std::string& parent_resource_id, | |
293 const std::string& resource_id, | |
294 const google_apis::EntryActionCallback& callback) { | |
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
296 | |
297 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
298 new_job->parent_resource_id = parent_resource_id; | |
299 new_job->resource_id = resource_id; | |
300 new_job->entry_action_callback = callback; | |
301 | |
302 StartNewJob(new_job.Pass(), TYPE_REMOVE_RESOURCE_FROM_DIRECTORY); | |
303 } | |
304 | |
305 void DriveScheduler::AddNewDirectory( | |
306 const std::string& parent_resource_id, | |
307 const std::string& directory_name, | |
308 const google_apis::GetResourceEntryCallback& callback) { | |
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
310 | |
311 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
312 new_job->parent_resource_id = parent_resource_id; | |
313 new_job->directory_name = directory_name; | |
314 new_job->get_resource_entry_callback = callback; | |
315 | |
316 StartNewJob(new_job.Pass(), TYPE_ADD_NEW_DIRECTORY); | |
317 } | |
318 | |
319 void DriveScheduler::DownloadFile( | |
320 const base::FilePath& virtual_path, | |
321 const base::FilePath& local_cache_path, | |
322 const GURL& download_url, | |
323 const DriveClientContext& context, | |
324 const google_apis::DownloadActionCallback& download_action_callback, | |
325 const google_apis::GetContentCallback& get_content_callback) { | |
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
327 | |
328 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
329 new_job->drive_file_path = virtual_path; | |
330 new_job->local_file_path = local_cache_path; | |
331 new_job->download_url = download_url; | |
332 new_job->context = context; | |
333 new_job->download_action_callback = download_action_callback; | |
334 new_job->get_content_callback = get_content_callback; | |
335 | |
336 StartNewJob(new_job.Pass(), TYPE_DOWNLOAD_FILE); | |
337 } | |
338 | |
339 void DriveScheduler::UploadNewFile( | |
340 const std::string& parent_resource_id, | |
341 const base::FilePath& drive_file_path, | |
342 const base::FilePath& local_file_path, | |
343 const std::string& title, | |
344 const std::string& content_type, | |
345 const DriveClientContext& context, | |
346 const google_apis::UploadCompletionCallback& callback) { | |
347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
348 | |
349 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
350 new_job->resource_id = parent_resource_id; | |
351 new_job->drive_file_path = drive_file_path; | |
352 new_job->local_file_path = local_file_path; | |
353 new_job->title = title; | |
354 new_job->content_type = content_type; | |
355 new_job->upload_completion_callback = callback; | |
356 new_job->context = context; | |
357 | |
358 StartNewJob(new_job.Pass(), TYPE_UPLOAD_NEW_FILE); | |
359 } | |
360 | |
361 void DriveScheduler::UploadExistingFile( | |
362 const std::string& resource_id, | |
363 const base::FilePath& drive_file_path, | |
364 const base::FilePath& local_file_path, | |
365 const std::string& content_type, | |
366 const std::string& etag, | |
367 const DriveClientContext& context, | |
368 const google_apis::UploadCompletionCallback& upload_completion_callback) { | |
369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
370 | |
371 scoped_ptr<QueueEntry> new_job(new QueueEntry); | |
372 new_job->resource_id = resource_id; | |
373 new_job->drive_file_path = drive_file_path; | |
374 new_job->local_file_path = local_file_path; | |
375 new_job->content_type = content_type; | |
376 new_job->etag = etag; | |
377 new_job->upload_completion_callback = upload_completion_callback; | |
378 new_job->context = context; | |
379 | |
380 StartNewJob(new_job.Pass(), TYPE_UPLOAD_EXISTING_FILE); | |
381 } | |
382 | |
383 void DriveScheduler::StartNewJob(scoped_ptr<QueueEntry> job, JobType type) { | |
384 // job_info is owned by job_map_ and released when it is removed in OnJobDone. | |
385 JobInfo* job_info = new JobInfo(type); | |
386 job->job_id = job_info->job_id = job_map_.Add(job_info); | |
387 job_info->file_path = job->drive_file_path; | |
388 | |
389 QueueJob(job.Pass()); | |
390 NotifyJobAdded(*job_info); | |
391 StartJobLoop(GetJobQueueType(type)); | |
392 } | |
393 | |
394 void DriveScheduler::QueueJob(scoped_ptr<QueueEntry> job) { | |
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
396 | |
397 JobInfo* job_info = job_map_.Lookup(job->job_id); | |
398 DCHECK(job_info); | |
399 util::Log("Queue job: %s [%d]", | |
400 JobTypeToString(job_info->job_type).c_str(), | |
401 job_info->job_id); | |
402 | |
403 QueueType queue_type = GetJobQueueType(job_info->job_type); | |
404 std::list<QueueEntry*>& queue = queue_[queue_type]; | |
405 | |
406 queue.push_back(job.release()); | |
407 queue.sort(&QueueEntry::Compare); | |
408 } | |
409 | |
410 void DriveScheduler::StartJobLoop(QueueType queue_type) { | |
411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
412 | |
413 if (jobs_running_[queue_type] < kMaxJobCount[queue_type]) | |
414 DoJobLoop(queue_type); | |
415 } | |
416 | |
417 void DriveScheduler::DoJobLoop(QueueType queue_type) { | |
418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
419 | |
420 if (queue_[queue_type].empty()) { | |
421 return; | |
422 } | |
423 | |
424 // Check if we should defer based on the first item in the queue | |
425 if (ShouldStopJobLoop(queue_type, queue_[queue_type].front()->context)) { | |
426 return; | |
427 } | |
428 | |
429 // Increment the number of jobs. | |
430 ++jobs_running_[queue_type]; | |
431 | |
432 // Should copy before calling queue_.pop_front(). | |
433 scoped_ptr<QueueEntry> queue_entry(queue_[queue_type].front()); | |
434 queue_[queue_type].pop_front(); | |
435 | |
436 JobInfo* job_info = job_map_.Lookup(queue_entry->job_id); | |
437 DCHECK(job_info); | |
438 job_info->state = STATE_RUNNING; | |
439 job_info->start_time = base::Time::Now(); | |
440 NotifyJobUpdated(*job_info); | |
441 | |
442 // The some arguments are evaluated after bind, so we copy the pointer to the | |
443 // QueueEntry | |
444 QueueEntry* entry = queue_entry.get(); | |
445 util::Log("Start job: %s [%d]", | |
446 JobTypeToString(job_info->job_type).c_str(), | |
447 job_info->job_id); | |
448 | |
449 switch (job_info->job_type) { | |
450 case TYPE_GET_ABOUT_RESOURCE: { | |
451 drive_service_->GetAboutResource( | |
452 base::Bind(&DriveScheduler::OnGetAboutResourceJobDone, | |
453 weak_ptr_factory_.GetWeakPtr(), | |
454 base::Passed(&queue_entry))); | |
455 } | |
456 break; | |
457 | |
458 case TYPE_GET_ACCOUNT_METADATA: { | |
459 drive_service_->GetAccountMetadata( | |
460 base::Bind(&DriveScheduler::OnGetAccountMetadataJobDone, | |
461 weak_ptr_factory_.GetWeakPtr(), | |
462 base::Passed(&queue_entry))); | |
463 } | |
464 break; | |
465 | |
466 case TYPE_GET_APP_LIST: { | |
467 drive_service_->GetAppList( | |
468 base::Bind(&DriveScheduler::OnGetAppListJobDone, | |
469 weak_ptr_factory_.GetWeakPtr(), | |
470 base::Passed(&queue_entry))); | |
471 } | |
472 break; | |
473 | |
474 case TYPE_GET_ALL_RESOURCE_LIST: { | |
475 drive_service_->GetAllResourceList( | |
476 base::Bind(&DriveScheduler::OnGetResourceListJobDone, | |
477 weak_ptr_factory_.GetWeakPtr(), | |
478 base::Passed(&queue_entry))); | |
479 } | |
480 break; | |
481 | |
482 case TYPE_GET_RESOURCE_LIST_IN_DIRECTORY: { | |
483 drive_service_->GetResourceListInDirectory( | |
484 entry->directory_resource_id, | |
485 base::Bind(&DriveScheduler::OnGetResourceListJobDone, | |
486 weak_ptr_factory_.GetWeakPtr(), | |
487 base::Passed(&queue_entry))); | |
488 } | |
489 break; | |
490 | |
491 case TYPE_SEARCH: { | |
492 drive_service_->Search( | |
493 entry->search_query, | |
494 base::Bind(&DriveScheduler::OnGetResourceListJobDone, | |
495 weak_ptr_factory_.GetWeakPtr(), | |
496 base::Passed(&queue_entry))); | |
497 } | |
498 break; | |
499 | |
500 case TYPE_GET_CHANGE_LIST: { | |
501 drive_service_->GetChangeList( | |
502 entry->start_changestamp, | |
503 base::Bind(&DriveScheduler::OnGetResourceListJobDone, | |
504 weak_ptr_factory_.GetWeakPtr(), | |
505 base::Passed(&queue_entry))); | |
506 } | |
507 break; | |
508 | |
509 case TYPE_CONTINUE_GET_RESOURCE_LIST: { | |
510 drive_service_->ContinueGetResourceList( | |
511 entry->feed_url, | |
512 base::Bind(&DriveScheduler::OnGetResourceListJobDone, | |
513 weak_ptr_factory_.GetWeakPtr(), | |
514 base::Passed(&queue_entry))); | |
515 } | |
516 break; | |
517 | |
518 case TYPE_GET_RESOURCE_ENTRY: { | |
519 drive_service_->GetResourceEntry( | |
520 entry->resource_id, | |
521 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone, | |
522 weak_ptr_factory_.GetWeakPtr(), | |
523 base::Passed(&queue_entry))); | |
524 } | |
525 break; | |
526 | |
527 case TYPE_DELETE_RESOURCE: { | |
528 drive_service_->DeleteResource( | |
529 entry->resource_id, | |
530 "", // etag | |
531 base::Bind(&DriveScheduler::OnEntryActionJobDone, | |
532 weak_ptr_factory_.GetWeakPtr(), | |
533 base::Passed(&queue_entry))); | |
534 } | |
535 break; | |
536 | |
537 | |
538 case TYPE_COPY_HOSTED_DOCUMENT: { | |
539 drive_service_->CopyHostedDocument( | |
540 entry->resource_id, | |
541 entry->new_name, | |
542 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone, | |
543 weak_ptr_factory_.GetWeakPtr(), | |
544 base::Passed(&queue_entry))); | |
545 } | |
546 break; | |
547 | |
548 case TYPE_RENAME_RESOURCE: { | |
549 drive_service_->RenameResource( | |
550 entry->resource_id, | |
551 entry->new_name, | |
552 base::Bind(&DriveScheduler::OnEntryActionJobDone, | |
553 weak_ptr_factory_.GetWeakPtr(), | |
554 base::Passed(&queue_entry))); | |
555 } | |
556 break; | |
557 | |
558 case TYPE_ADD_RESOURCE_TO_DIRECTORY: { | |
559 drive_service_->AddResourceToDirectory( | |
560 entry->parent_resource_id, | |
561 entry->resource_id, | |
562 base::Bind(&DriveScheduler::OnEntryActionJobDone, | |
563 weak_ptr_factory_.GetWeakPtr(), | |
564 base::Passed(&queue_entry))); | |
565 } | |
566 break; | |
567 | |
568 case TYPE_REMOVE_RESOURCE_FROM_DIRECTORY: { | |
569 drive_service_->RemoveResourceFromDirectory( | |
570 entry->parent_resource_id, | |
571 entry->resource_id, | |
572 base::Bind(&DriveScheduler::OnEntryActionJobDone, | |
573 weak_ptr_factory_.GetWeakPtr(), | |
574 base::Passed(&queue_entry))); | |
575 } | |
576 break; | |
577 | |
578 case TYPE_ADD_NEW_DIRECTORY: { | |
579 drive_service_->AddNewDirectory( | |
580 entry->parent_resource_id, | |
581 entry->directory_name, | |
582 base::Bind(&DriveScheduler::OnGetResourceEntryJobDone, | |
583 weak_ptr_factory_.GetWeakPtr(), | |
584 base::Passed(&queue_entry))); | |
585 } | |
586 break; | |
587 | |
588 case TYPE_DOWNLOAD_FILE: { | |
589 drive_service_->DownloadFile( | |
590 entry->drive_file_path, | |
591 entry->local_file_path, | |
592 entry->download_url, | |
593 base::Bind(&DriveScheduler::OnDownloadActionJobDone, | |
594 weak_ptr_factory_.GetWeakPtr(), | |
595 base::Passed(&queue_entry)), | |
596 entry->get_content_callback, | |
597 base::Bind(&DriveScheduler::UpdateProgress, | |
598 weak_ptr_factory_.GetWeakPtr(), | |
599 job_info->job_id)); | |
600 } | |
601 break; | |
602 | |
603 case TYPE_UPLOAD_NEW_FILE: { | |
604 uploader_->UploadNewFile( | |
605 entry->resource_id, | |
606 entry->drive_file_path, | |
607 entry->local_file_path, | |
608 entry->title, | |
609 entry->content_type, | |
610 base::Bind(&DriveScheduler::OnUploadCompletionJobDone, | |
611 weak_ptr_factory_.GetWeakPtr(), | |
612 base::Passed(&queue_entry)), | |
613 base::Bind(&DriveScheduler::UpdateProgress, | |
614 weak_ptr_factory_.GetWeakPtr(), | |
615 job_info->job_id)); | |
616 } | |
617 break; | |
618 | |
619 case TYPE_UPLOAD_EXISTING_FILE: { | |
620 uploader_->UploadExistingFile( | |
621 entry->resource_id, | |
622 entry->drive_file_path, | |
623 entry->local_file_path, | |
624 entry->content_type, | |
625 entry->etag, | |
626 base::Bind(&DriveScheduler::OnUploadCompletionJobDone, | |
627 weak_ptr_factory_.GetWeakPtr(), | |
628 base::Passed(&queue_entry)), | |
629 base::Bind(&DriveScheduler::UpdateProgress, | |
630 weak_ptr_factory_.GetWeakPtr(), | |
631 job_info->job_id)); | |
632 } | |
633 break; | |
634 | |
635 // There is no default case so that there will be a compiler error if a type | |
636 // is added but unhandled. | |
637 } | |
638 } | |
639 | |
640 bool DriveScheduler::ShouldStopJobLoop(QueueType queue_type, | |
641 const DriveClientContext& context) { | |
642 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
643 | |
644 // Should stop if the gdata feature was disabled while running the fetch | |
645 // loop. | |
646 if (profile_->GetPrefs()->GetBoolean(prefs::kDisableDrive)) | |
647 return true; | |
648 | |
649 // Should stop if the network is not online. | |
650 if (net::NetworkChangeNotifier::IsOffline()) | |
651 return true; | |
652 | |
653 // Should stop background jobs if the current connection is on cellular | |
654 // network, and fetching is disabled over cellular. | |
655 bool should_stop_on_cellular_network = false; | |
656 switch (context.type) { | |
657 case USER_INITIATED: | |
658 should_stop_on_cellular_network = false; | |
659 break; | |
660 case BACKGROUND: | |
661 case PREFETCH: | |
662 should_stop_on_cellular_network = (queue_type == FILE_QUEUE); | |
663 break; | |
664 } | |
665 if (should_stop_on_cellular_network && | |
666 profile_->GetPrefs()->GetBoolean(prefs::kDisableDriveOverCellular) && | |
667 net::NetworkChangeNotifier::IsConnectionCellular( | |
668 net::NetworkChangeNotifier::GetConnectionType())) { | |
669 return true; | |
670 } | |
671 | |
672 return false; | |
673 } | |
674 | |
675 void DriveScheduler::ThrottleAndContinueJobLoop(QueueType queue_type) { | |
676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
677 | |
678 if (throttle_count_ < kMaxThrottleCount) | |
679 throttle_count_++; | |
680 | |
681 base::TimeDelta delay; | |
682 if (disable_throttling_) { | |
683 delay = base::TimeDelta::FromSeconds(0); | |
684 } else { | |
685 delay = | |
686 base::TimeDelta::FromSeconds(pow(2, throttle_count_ - 1)) + | |
687 base::TimeDelta::FromMilliseconds(base::RandInt(0, 1000)); | |
688 } | |
689 VLOG(1) << "Throttling for " << delay.InMillisecondsF(); | |
690 | |
691 const bool posted = base::MessageLoopProxy::current()->PostDelayedTask( | |
692 FROM_HERE, | |
693 base::Bind(&DriveScheduler::DoJobLoop, | |
694 weak_ptr_factory_.GetWeakPtr(), | |
695 queue_type), | |
696 delay); | |
697 DCHECK(posted); | |
698 } | |
699 | |
700 void DriveScheduler::ResetThrottleAndContinueJobLoop(QueueType queue_type) { | |
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
702 | |
703 // Post a task to continue the job loop. This allows us to finish handling | |
704 // the current job before starting the next one. | |
705 throttle_count_ = 0; | |
706 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
707 base::Bind(&DriveScheduler::DoJobLoop, | |
708 weak_ptr_factory_.GetWeakPtr(), | |
709 queue_type)); | |
710 } | |
711 | |
712 scoped_ptr<DriveScheduler::QueueEntry> DriveScheduler::OnJobDone( | |
713 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
714 FileError error) { | |
715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
716 | |
717 JobInfo* job_info = job_map_.Lookup(queue_entry->job_id); | |
718 DCHECK(job_info); | |
719 QueueType queue_type = GetJobQueueType(job_info->job_type); | |
720 | |
721 const base::TimeDelta elapsed = base::Time::Now() - job_info->start_time; | |
722 util::Log("Job done: %s [%d] => %s (elapsed time: %sms)", | |
723 JobTypeToString(job_info->job_type).c_str(), | |
724 job_info->job_id, | |
725 FileErrorToString(error).c_str(), | |
726 base::Int64ToString(elapsed.InMilliseconds()).c_str()); | |
727 | |
728 // Decrement the number of jobs for this queue. | |
729 --jobs_running_[queue_type]; | |
730 | |
731 // Retry, depending on the error. | |
732 if (error == FILE_ERROR_THROTTLED) { | |
733 job_info->state = STATE_RETRY; | |
734 NotifyJobUpdated(*job_info); | |
735 | |
736 // Requeue the job. | |
737 QueueJob(queue_entry.Pass()); | |
738 | |
739 ThrottleAndContinueJobLoop(queue_type); | |
740 | |
741 return scoped_ptr<DriveScheduler::QueueEntry>(); | |
742 } else { | |
743 NotifyJobDone(*job_info, error); | |
744 // The job has finished, no retry will happen in the scheduler. Now we can | |
745 // remove the job info from the map. This is the only place of the removal. | |
746 job_map_.Remove(queue_entry->job_id); | |
747 | |
748 ResetThrottleAndContinueJobLoop(queue_type); | |
749 | |
750 // Send the entry back. | |
751 return queue_entry.Pass(); | |
752 } | |
753 } | |
754 | |
755 void DriveScheduler::OnGetResourceListJobDone( | |
756 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
757 google_apis::GDataErrorCode error, | |
758 scoped_ptr<google_apis::ResourceList> resource_list) { | |
759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
760 | |
761 FileError drive_error(util::GDataToFileError(error)); | |
762 | |
763 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
764 | |
765 if (!queue_entry) | |
766 return; | |
767 | |
768 // Handle the callback. | |
769 base::MessageLoopProxy::current()->PostTask( | |
770 FROM_HERE, | |
771 base::Bind(queue_entry->get_resource_list_callback, | |
772 error, | |
773 base::Passed(&resource_list))); | |
774 } | |
775 | |
776 void DriveScheduler::OnGetResourceEntryJobDone( | |
777 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
778 google_apis::GDataErrorCode error, | |
779 scoped_ptr<google_apis::ResourceEntry> entry) { | |
780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
781 | |
782 FileError drive_error(util::GDataToFileError(error)); | |
783 | |
784 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
785 | |
786 if (!queue_entry) | |
787 return; | |
788 | |
789 // Handle the callback. | |
790 base::MessageLoopProxy::current()->PostTask( | |
791 FROM_HERE, | |
792 base::Bind(queue_entry->get_resource_entry_callback, | |
793 error, | |
794 base::Passed(&entry))); | |
795 } | |
796 | |
797 void DriveScheduler::OnGetAboutResourceJobDone( | |
798 scoped_ptr<QueueEntry> queue_entry, | |
799 google_apis::GDataErrorCode error, | |
800 scoped_ptr<google_apis::AboutResource> about_resource) { | |
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
802 | |
803 FileError drive_error(util::GDataToFileError(error)); | |
804 | |
805 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
806 | |
807 if (!queue_entry) | |
808 return; | |
809 | |
810 // Handle the callback. | |
811 queue_entry->get_about_resource_callback.Run(error, about_resource.Pass()); | |
812 } | |
813 | |
814 void DriveScheduler::OnGetAccountMetadataJobDone( | |
815 scoped_ptr<QueueEntry> queue_entry, | |
816 google_apis::GDataErrorCode error, | |
817 scoped_ptr<google_apis::AccountMetadata> account_metadata) { | |
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
819 | |
820 FileError drive_error(util::GDataToFileError(error)); | |
821 | |
822 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
823 | |
824 if (!queue_entry) | |
825 return; | |
826 | |
827 // Handle the callback. | |
828 queue_entry->get_account_metadata_callback.Run(error, | |
829 account_metadata.Pass()); | |
830 } | |
831 | |
832 void DriveScheduler::OnGetAppListJobDone( | |
833 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
834 google_apis::GDataErrorCode error, | |
835 scoped_ptr<google_apis::AppList> app_list) { | |
836 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
837 | |
838 FileError drive_error(util::GDataToFileError(error)); | |
839 | |
840 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
841 | |
842 if (!queue_entry) | |
843 return; | |
844 | |
845 // Handle the callback. | |
846 queue_entry->get_app_list_callback.Run(error, app_list.Pass()); | |
847 } | |
848 | |
849 void DriveScheduler::OnEntryActionJobDone( | |
850 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
851 google_apis::GDataErrorCode error) { | |
852 FileError drive_error(util::GDataToFileError(error)); | |
853 | |
854 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
855 | |
856 if (!queue_entry) | |
857 return; | |
858 | |
859 // Handle the callback. | |
860 DCHECK(!queue_entry->entry_action_callback.is_null()); | |
861 queue_entry->entry_action_callback.Run(error); | |
862 } | |
863 | |
864 void DriveScheduler::OnDownloadActionJobDone( | |
865 scoped_ptr<DriveScheduler::QueueEntry> queue_entry, | |
866 google_apis::GDataErrorCode error, | |
867 const base::FilePath& temp_file) { | |
868 FileError drive_error(util::GDataToFileError(error)); | |
869 | |
870 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
871 | |
872 if (!queue_entry) | |
873 return; | |
874 | |
875 // Handle the callback. | |
876 DCHECK(!queue_entry->download_action_callback.is_null()); | |
877 queue_entry->download_action_callback.Run(error, temp_file); | |
878 } | |
879 | |
880 void DriveScheduler::OnUploadCompletionJobDone( | |
881 scoped_ptr<QueueEntry> queue_entry, | |
882 google_apis::DriveUploadError error, | |
883 const base::FilePath& drive_path, | |
884 const base::FilePath& file_path, | |
885 scoped_ptr<google_apis::ResourceEntry> resource_entry) { | |
886 FileError drive_error(DriveUploadErrorToFileError(error)); | |
887 | |
888 queue_entry = OnJobDone(queue_entry.Pass(), drive_error); | |
889 | |
890 if (!queue_entry) | |
891 return; | |
892 | |
893 // Handle the callback. | |
894 DCHECK(!queue_entry->upload_completion_callback.is_null()); | |
895 queue_entry->upload_completion_callback.Run( | |
896 error, drive_path, file_path, resource_entry.Pass()); | |
897 } | |
898 | |
899 void DriveScheduler::UpdateProgress(JobID job_id, int64 progress, int64 total) { | |
900 JobInfo* job_info = job_map_.Lookup(job_id); | |
901 DCHECK(job_info); | |
902 | |
903 job_info->num_completed_bytes = progress; | |
904 job_info->num_total_bytes = total; | |
905 NotifyJobUpdated(*job_info); | |
906 } | |
907 | |
908 void DriveScheduler::OnConnectionTypeChanged( | |
909 net::NetworkChangeNotifier::ConnectionType type) { | |
910 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
911 | |
912 // Resume the job loop if the network is back online. Note that we don't | |
913 // need to check the type of the network as it will be checked in | |
914 // ShouldStopJobLoop() as soon as the loop is resumed. | |
915 if (!net::NetworkChangeNotifier::IsOffline()) { | |
916 for (int i = METADATA_QUEUE; i < NUM_QUEUES; ++i) { | |
917 StartJobLoop(static_cast<QueueType>(i)); | |
918 } | |
919 } | |
920 } | |
921 | |
922 DriveScheduler::QueueType DriveScheduler::GetJobQueueType(JobType type) { | |
923 switch (type) { | |
924 case TYPE_GET_ABOUT_RESOURCE: | |
925 case TYPE_GET_ACCOUNT_METADATA: | |
926 case TYPE_GET_APP_LIST: | |
927 case TYPE_GET_ALL_RESOURCE_LIST: | |
928 case TYPE_GET_RESOURCE_LIST_IN_DIRECTORY: | |
929 case TYPE_SEARCH: | |
930 case TYPE_GET_CHANGE_LIST: | |
931 case TYPE_CONTINUE_GET_RESOURCE_LIST: | |
932 case TYPE_GET_RESOURCE_ENTRY: | |
933 case TYPE_DELETE_RESOURCE: | |
934 case TYPE_COPY_HOSTED_DOCUMENT: | |
935 case TYPE_RENAME_RESOURCE: | |
936 case TYPE_ADD_RESOURCE_TO_DIRECTORY: | |
937 case TYPE_REMOVE_RESOURCE_FROM_DIRECTORY: | |
938 case TYPE_ADD_NEW_DIRECTORY: | |
939 return METADATA_QUEUE; | |
940 | |
941 case TYPE_DOWNLOAD_FILE: | |
942 case TYPE_UPLOAD_NEW_FILE: | |
943 case TYPE_UPLOAD_EXISTING_FILE: | |
944 return FILE_QUEUE; | |
945 } | |
946 NOTREACHED(); | |
947 return FILE_QUEUE; | |
948 } | |
949 | |
950 void DriveScheduler::NotifyJobAdded(const JobInfo& job_info) { | |
951 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
952 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobAdded(job_info)); | |
953 } | |
954 | |
955 void DriveScheduler::NotifyJobDone(const JobInfo& job_info, | |
956 FileError error) { | |
957 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
958 FOR_EACH_OBSERVER(JobListObserver, observer_list_, | |
959 OnJobDone(job_info, error)); | |
960 } | |
961 | |
962 void DriveScheduler::NotifyJobUpdated(const JobInfo& job_info) { | |
963 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
964 FOR_EACH_OBSERVER(JobListObserver, observer_list_, OnJobUpdated(job_info)); | |
965 } | |
966 | |
967 } // namespace drive | |
OLD | NEW |