Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(165)

Side by Side Diff: components/offline_pages/background/request_coordinator.cc

Issue 2489443002: Move all components/offline_pages/ files into component/offline_pages/core (Closed)
Patch Set: update Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "components/offline_pages/background/request_coordinator.h"
6
7 #include <limits>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/rand_util.h"
15 #include "base/sys_info.h"
16 #include "base/time/time.h"
17 #include "components/offline_pages/background/offliner_factory.h"
18 #include "components/offline_pages/background/offliner_policy.h"
19 #include "components/offline_pages/background/save_page_request.h"
20 #include "components/offline_pages/client_policy_controller.h"
21 #include "components/offline_pages/offline_page_feature.h"
22 #include "components/offline_pages/offline_page_item.h"
23 #include "components/offline_pages/offline_page_model.h"
24
25 namespace offline_pages {
26
27 namespace {
28 const bool kUserRequest = true;
29 const int kMinDurationSeconds = 1;
30 const int kMaxDurationSeconds = 7 * 24 * 60 * 60; // 7 days
31 const int kDurationBuckets = 50;
32 const int kDisabledTaskRecheckSeconds = 5;
33
34 // TODO(dougarnett): Move to util location and share with model impl.
35 std::string AddHistogramSuffix(const ClientId& client_id,
36 const char* histogram_name) {
37 if (client_id.name_space.empty()) {
38 NOTREACHED();
39 return histogram_name;
40 }
41 std::string adjusted_histogram_name(histogram_name);
42 adjusted_histogram_name += "." + client_id.name_space;
43 return adjusted_histogram_name;
44 }
45
46 // Records the final request status UMA for an offlining request. This should
47 // only be called once per Offliner::LoadAndSave request.
48 void RecordOfflinerResultUMA(const ClientId& client_id,
49 const base::Time& request_creation_time,
50 Offliner::RequestStatus request_status) {
51 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
52 // macro adapted to allow for a dynamically suffixed histogram name.
53 // Note: The factory creates and owns the histogram.
54 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
55 AddHistogramSuffix(client_id,
56 "OfflinePages.Background.OfflinerRequestStatus"),
57 1, static_cast<int>(Offliner::RequestStatus::STATUS_COUNT),
58 static_cast<int>(Offliner::RequestStatus::STATUS_COUNT) + 1,
59 base::HistogramBase::kUmaTargetedHistogramFlag);
60 histogram->Add(static_cast<int>(request_status));
61
62 // For successful requests also record time from request to save.
63 if (request_status == Offliner::RequestStatus::SAVED) {
64 // Using regular histogram (with dynamic suffix) rather than time-oriented
65 // one to record samples in seconds rather than milliseconds.
66 base::HistogramBase* histogram = base::Histogram::FactoryGet(
67 AddHistogramSuffix(client_id, "OfflinePages.Background.TimeToSaved"),
68 kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
69 base::HistogramBase::kUmaTargetedHistogramFlag);
70 base::TimeDelta duration = base::Time::Now() - request_creation_time;
71 histogram->Add(duration.InSeconds());
72 }
73 }
74
75 void RecordStartTimeUMA(const SavePageRequest& request) {
76 std::string histogram_name("OfflinePages.Background.TimeToStart");
77 if (base::SysInfo::IsLowEndDevice()) {
78 histogram_name += ".Svelte";
79 }
80
81 // The histogram below is an expansion of the UMA_HISTOGRAM_CUSTOM_TIMES
82 // macro adapted to allow for a dynamically suffixed histogram name.
83 // Note: The factory creates and owns the histogram.
84 base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
85 AddHistogramSuffix(request.client_id(), histogram_name.c_str()),
86 base::TimeDelta::FromMilliseconds(100), base::TimeDelta::FromDays(7), 50,
87 base::HistogramBase::kUmaTargetedHistogramFlag);
88 base::TimeDelta duration = base::Time::Now() - request.creation_time();
89 histogram->AddTime(duration);
90 }
91
92 void RecordCancelTimeUMA(const SavePageRequest& canceled_request) {
93 // Using regular histogram (with dynamic suffix) rather than time-oriented
94 // one to record samples in seconds rather than milliseconds.
95 base::HistogramBase* histogram = base::Histogram::FactoryGet(
96 AddHistogramSuffix(canceled_request.client_id(),
97 "OfflinePages.Background.TimeToCanceled"),
98 kMinDurationSeconds, kMaxDurationSeconds, kDurationBuckets,
99 base::HistogramBase::kUmaTargetedHistogramFlag);
100 base::TimeDelta duration =
101 base::Time::Now() - canceled_request.creation_time();
102 histogram->Add(duration.InSeconds());
103 }
104
105 // Records the number of started attempts for completed requests (whether
106 // successful or not).
107 void RecordAttemptCount(const SavePageRequest& request,
108 RequestNotifier::BackgroundSavePageResult status) {
109 if (status == RequestNotifier::BackgroundSavePageResult::SUCCESS) {
110 // TODO(dougarnett): Also record UMA for completed attempts here.
111 UMA_HISTOGRAM_CUSTOM_COUNTS(
112 "OfflinePages.Background.RequestSuccess.StartedAttemptCount",
113 request.started_attempt_count(), 1, 10, 11);
114 } else {
115 UMA_HISTOGRAM_CUSTOM_COUNTS(
116 "OfflinePages.Background.RequestFailure.StartedAttemptCount",
117 request.started_attempt_count(), 1, 10, 11);
118 }
119 }
120
121 // Record the network quality at request creation time per namespace.
122 void RecordSavePageLaterNetworkQuality(
123 const ClientId& client_id,
124 const net::EffectiveConnectionType effective_connection) {
125 // The histogram below is an expansion of the UMA_HISTOGRAM_ENUMERATION
126 // macro adapted to allow for a dynamically suffixed histogram name.
127 // Note: The factory creates and owns the histogram.
128 base::HistogramBase* histogram = base::LinearHistogram::FactoryGet(
129 AddHistogramSuffix(
130 client_id,
131 "OfflinePages.Background.EffectiveConnectionType.SavePageLater"),
132 1, net::EFFECTIVE_CONNECTION_TYPE_LAST - 1,
133 net::EFFECTIVE_CONNECTION_TYPE_LAST,
134 base::HistogramBase::kUmaTargetedHistogramFlag);
135 histogram->Add(effective_connection);
136 }
137
138 // This should use the same algorithm as we use for OfflinePageItem, so the IDs
139 // are similar.
140 int64_t GenerateOfflineId() {
141 return base::RandGenerator(std::numeric_limits<int64_t>::max()) + 1;
142 }
143
144 // In case we start processing from SavePageLater, we need a callback, but there
145 // is nothing for it to do.
146 void EmptySchedulerCallback(bool started) {}
147
148 // Returns whether |result| is a successful result for a single request.
149 bool IsSingleSuccessResult(const UpdateRequestsResult* result) {
150 return result->store_state == StoreState::LOADED &&
151 result->item_statuses.size() == 1 &&
152 result->item_statuses.at(0).second == ItemActionStatus::SUCCESS;
153 }
154
155 } // namespace
156
157 RequestCoordinator::RequestCoordinator(
158 std::unique_ptr<OfflinerPolicy> policy,
159 std::unique_ptr<OfflinerFactory> factory,
160 std::unique_ptr<RequestQueue> queue,
161 std::unique_ptr<Scheduler> scheduler,
162 net::NetworkQualityEstimator::NetworkQualityProvider*
163 network_quality_estimator)
164 : is_low_end_device_(base::SysInfo::IsLowEndDevice()),
165 is_busy_(false),
166 is_starting_(false),
167 processing_state_(ProcessingWindowState::STOPPED),
168 use_test_connection_type_(false),
169 test_connection_type_(),
170 offliner_(nullptr),
171 policy_(std::move(policy)),
172 factory_(std::move(factory)),
173 queue_(std::move(queue)),
174 scheduler_(std::move(scheduler)),
175 policy_controller_(new ClientPolicyController()),
176 network_quality_estimator_(network_quality_estimator),
177 active_request_(nullptr),
178 last_offlining_status_(Offliner::RequestStatus::UNKNOWN),
179 scheduler_callback_(base::Bind(&EmptySchedulerCallback)),
180 immediate_schedule_callback_(base::Bind(&EmptySchedulerCallback)),
181 weak_ptr_factory_(this) {
182 DCHECK(policy_ != nullptr);
183 std::unique_ptr<PickRequestTaskFactory> picker_factory(
184 new PickRequestTaskFactory(policy_.get(), this, &event_logger_));
185 queue_->SetPickerFactory(std::move(picker_factory));
186 }
187
188 RequestCoordinator::~RequestCoordinator() {}
189
190 int64_t RequestCoordinator::SavePageLater(const GURL& url,
191 const ClientId& client_id,
192 bool user_requested,
193 RequestAvailability availability) {
194 DVLOG(2) << "URL is " << url << " " << __func__;
195
196 if (!OfflinePageModel::CanSaveURL(url)) {
197 DVLOG(1) << "Not able to save page for requested url: " << url;
198 return 0L;
199 }
200
201 int64_t id = GenerateOfflineId();
202
203 // Build a SavePageRequest.
204 offline_pages::SavePageRequest request(id, url, client_id, base::Time::Now(),
205 user_requested);
206
207 // If the download manager is not done with the request, put it on the
208 // disabled list.
209 if (availability == RequestAvailability::DISABLED_FOR_OFFLINER)
210 disabled_requests_.insert(id);
211
212 // Put the request on the request queue.
213 queue_->AddRequest(request,
214 base::Bind(&RequestCoordinator::AddRequestResultCallback,
215 weak_ptr_factory_.GetWeakPtr()));
216
217 // Record the network quality when this request is made.
218 if (network_quality_estimator_) {
219 RecordSavePageLaterNetworkQuality(
220 client_id, network_quality_estimator_->GetEffectiveConnectionType());
221 }
222
223 return id;
224 }
225 void RequestCoordinator::GetAllRequests(const GetRequestsCallback& callback) {
226 // Get all matching requests from the request queue, send them to our
227 // callback. We bind the namespace and callback to the front of the callback
228 // param set.
229 queue_->GetRequests(base::Bind(&RequestCoordinator::GetQueuedRequestsCallback,
230 weak_ptr_factory_.GetWeakPtr(), callback));
231 }
232
233 void RequestCoordinator::GetQueuedRequestsCallback(
234 const GetRequestsCallback& callback,
235 GetRequestsResult result,
236 std::vector<std::unique_ptr<SavePageRequest>> requests) {
237 callback.Run(std::move(requests));
238 }
239
240 void RequestCoordinator::StopPrerendering(Offliner::RequestStatus stop_status) {
241 if (offliner_ && is_busy_) {
242 DCHECK(active_request_.get());
243 offliner_->Cancel();
244
245 if (stop_status == Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT) {
246 // Consider watchdog timeout a completed attempt.
247 SavePageRequest request(*active_request_.get());
248 UpdateRequestForCompletedAttempt(request,
249 Offliner::REQUEST_COORDINATOR_TIMED_OUT);
250 } else {
251 // Otherwise consider this stop an aborted attempt.
252 UpdateRequestForAbortedAttempt(*active_request_.get());
253 }
254 }
255
256 // Stopping offliner means it will not call callback so set last status.
257 last_offlining_status_ = stop_status;
258
259 if (active_request_) {
260 event_logger_.RecordOfflinerResult(active_request_->client_id().name_space,
261 last_offlining_status_,
262 active_request_->request_id());
263 RecordOfflinerResultUMA(active_request_->client_id(),
264 active_request_->creation_time(),
265 last_offlining_status_);
266 is_busy_ = false;
267 active_request_.reset();
268 }
269 }
270
271 void RequestCoordinator::GetRequestsForSchedulingCallback(
272 GetRequestsResult result,
273 std::vector<std::unique_ptr<SavePageRequest>> requests) {
274 bool user_requested = false;
275
276 // Examine all requests, if we find a user requested one, we will use the less
277 // restrictive conditions for user_requested requests. Otherwise we will use
278 // the more restrictive non-user-requested conditions.
279 for (const auto& request : requests) {
280 if (request->user_requested()) {
281 user_requested = true;
282 break;
283 }
284 }
285
286 // In the get callback, determine the least restrictive, and call
287 // GetTriggerConditions based on that.
288 scheduler_->Schedule(GetTriggerConditions(user_requested));
289 }
290
291 bool RequestCoordinator::CancelActiveRequestIfItMatches(
292 const std::vector<int64_t>& request_ids) {
293 // If we have a request in progress and need to cancel it, call the
294 // pre-renderer to cancel. TODO Make sure we remove any page created by the
295 // prerenderer if it doesn't get the cancel in time.
296 if (active_request_ != nullptr) {
297 if (request_ids.end() != std::find(request_ids.begin(), request_ids.end(),
298 active_request_->request_id())) {
299 StopPrerendering(Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED);
300 active_request_.reset(nullptr);
301 return true;
302 }
303 }
304
305 return false;
306 }
307
308 void RequestCoordinator::UpdateRequestForAbortedAttempt(
309 const SavePageRequest& request) {
310 if (request.started_attempt_count() >= policy_->GetMaxStartedTries()) {
311 const RequestNotifier::BackgroundSavePageResult result(
312 RequestNotifier::BackgroundSavePageResult::START_COUNT_EXCEEDED);
313 event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
314 result, request.request_id());
315 RemoveAttemptedRequest(request, result);
316 } else {
317 queue_->MarkAttemptAborted(
318 request.request_id(),
319 base::Bind(&RequestCoordinator::MarkAttemptAbortedDone,
320 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
321 request.client_id()));
322 }
323 }
324
325 void RequestCoordinator::RemoveAttemptedRequest(
326 const SavePageRequest& request,
327 RequestNotifier::BackgroundSavePageResult result) {
328 std::vector<int64_t> remove_requests;
329 remove_requests.push_back(request.request_id());
330 queue_->RemoveRequests(remove_requests,
331 base::Bind(&RequestCoordinator::HandleRemovedRequests,
332 weak_ptr_factory_.GetWeakPtr(), result));
333 RecordAttemptCount(request, result);
334 }
335
336 void RequestCoordinator::MarkAttemptAbortedDone(
337 int64_t request_id,
338 const ClientId& client_id,
339 std::unique_ptr<UpdateRequestsResult> result) {
340 // If the request succeeded, notify observer. If it failed, we can't really
341 // do much, so just log it.
342 if (IsSingleSuccessResult(result.get())) {
343 NotifyChanged(result->updated_items.at(0));
344 } else {
345 DVLOG(1) << "Failed to mark request aborted: " << request_id;
346 UpdateRequestResult request_result =
347 result->store_state != StoreState::LOADED
348 ? UpdateRequestResult::STORE_FAILURE
349 : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
350 event_logger_.RecordUpdateRequestFailed(client_id.name_space,
351 request_result);
352 }
353 }
354
355 void RequestCoordinator::RemoveRequests(
356 const std::vector<int64_t>& request_ids,
357 const RemoveRequestsCallback& callback) {
358 bool canceled = CancelActiveRequestIfItMatches(request_ids);
359 queue_->RemoveRequests(
360 request_ids,
361 base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
362 weak_ptr_factory_.GetWeakPtr(), callback,
363 RequestNotifier::BackgroundSavePageResult::REMOVED));
364
365 // Record the network quality when this request is made.
366 if (network_quality_estimator_) {
367 UMA_HISTOGRAM_ENUMERATION(
368 "OfflinePages.Background.EffectiveConnectionType.RemoveRequests",
369 network_quality_estimator_->GetEffectiveConnectionType(),
370 net::EFFECTIVE_CONNECTION_TYPE_LAST);
371 }
372
373 if (canceled)
374 TryNextRequest();
375 }
376
377 void RequestCoordinator::PauseRequests(
378 const std::vector<int64_t>& request_ids) {
379 bool canceled = CancelActiveRequestIfItMatches(request_ids);
380 queue_->ChangeRequestsState(
381 request_ids, SavePageRequest::RequestState::PAUSED,
382 base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
383 weak_ptr_factory_.GetWeakPtr()));
384
385 // Record the network quality when this request is made.
386 if (network_quality_estimator_) {
387 UMA_HISTOGRAM_ENUMERATION(
388 "OfflinePages.Background.EffectiveConnectionType.PauseRequests",
389 network_quality_estimator_->GetEffectiveConnectionType(),
390 net::EFFECTIVE_CONNECTION_TYPE_LAST);
391 }
392
393 if (canceled)
394 TryNextRequest();
395 }
396
397 void RequestCoordinator::ResumeRequests(
398 const std::vector<int64_t>& request_ids) {
399 queue_->ChangeRequestsState(
400 request_ids, SavePageRequest::RequestState::AVAILABLE,
401 base::Bind(&RequestCoordinator::UpdateMultipleRequestsCallback,
402 weak_ptr_factory_.GetWeakPtr()));
403
404 // Record the network quality when this request is made.
405 if (network_quality_estimator_) {
406 UMA_HISTOGRAM_ENUMERATION(
407 "OfflinePages.Background.EffectiveConnectionType.ResumeRequests",
408 network_quality_estimator_->GetEffectiveConnectionType(),
409 net::EFFECTIVE_CONNECTION_TYPE_LAST);
410 }
411
412 // Schedule a task, in case there is not one scheduled.
413 ScheduleAsNeeded();
414 }
415
416 net::NetworkChangeNotifier::ConnectionType
417 RequestCoordinator::GetConnectionType() {
418 // If we have a connection type set for test, use that.
419 if (use_test_connection_type_)
420 return test_connection_type_;
421
422 return net::NetworkChangeNotifier::GetConnectionType();
423 }
424
425 void RequestCoordinator::AddRequestResultCallback(
426 AddRequestResult result,
427 const SavePageRequest& request) {
428 NotifyAdded(request);
429 // Inform the scheduler that we have an outstanding task.
430 scheduler_->Schedule(GetTriggerConditions(kUserRequest));
431
432 if (request.user_requested())
433 StartImmediatelyIfConnected();
434 }
435
436 void RequestCoordinator::MarkAttemptCompletedDoneCallback(
437 int64_t request_id,
438 const ClientId& client_id,
439 std::unique_ptr<UpdateRequestsResult> result) {
440 if (IsSingleSuccessResult(result.get())) {
441 NotifyChanged(result->updated_items.at(0));
442 } else {
443 DVLOG(1) << "Failed to mark request completed " << request_id;
444 UpdateRequestResult request_result =
445 result->store_state != StoreState::LOADED
446 ? UpdateRequestResult::STORE_FAILURE
447 : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
448 event_logger_.RecordUpdateRequestFailed(client_id.name_space,
449 request_result);
450 }
451 }
452
453 void RequestCoordinator::UpdateMultipleRequestsCallback(
454 std::unique_ptr<UpdateRequestsResult> result) {
455 for (const auto& request : result->updated_items)
456 NotifyChanged(request);
457
458 bool available_user_request = false;
459 for (const auto& request : result->updated_items) {
460 if (!available_user_request && request.user_requested() &&
461 request.request_state() == SavePageRequest::RequestState::AVAILABLE) {
462 available_user_request = true;
463 }
464 }
465
466 if (available_user_request)
467 StartImmediatelyIfConnected();
468 }
469
470 // When we successfully remove a request that completed successfully, move on to
471 // the next request.
472 void RequestCoordinator::CompletedRequestCallback(
473 const MultipleItemStatuses& status) {
474 TryNextRequest();
475 }
476
477 void RequestCoordinator::HandleRemovedRequestsAndCallback(
478 const RemoveRequestsCallback& callback,
479 RequestNotifier::BackgroundSavePageResult status,
480 std::unique_ptr<UpdateRequestsResult> result) {
481 // TODO(dougarnett): Define status code for user/api cancel and use here
482 // to determine whether to record cancel time UMA.
483 for (const auto& request : result->updated_items)
484 RecordCancelTimeUMA(request);
485 callback.Run(result->item_statuses);
486 HandleRemovedRequests(status, std::move(result));
487 }
488
489 void RequestCoordinator::HandleRemovedRequests(
490 RequestNotifier::BackgroundSavePageResult status,
491 std::unique_ptr<UpdateRequestsResult> result) {
492 for (const auto& request : result->updated_items)
493 NotifyCompleted(request, status);
494 }
495
496 void RequestCoordinator::ScheduleAsNeeded() {
497 // Get all requests from queue (there is no filtering mechanism).
498 queue_->GetRequests(
499 base::Bind(&RequestCoordinator::GetRequestsForSchedulingCallback,
500 weak_ptr_factory_.GetWeakPtr()));
501 }
502
503 void RequestCoordinator::StopProcessing(
504 Offliner::RequestStatus stop_status) {
505 processing_state_ = ProcessingWindowState::STOPPED;
506 StopPrerendering(stop_status);
507
508 // Let the scheduler know we are done processing.
509 scheduler_callback_.Run(true);
510 }
511
512 void RequestCoordinator::HandleWatchdogTimeout() {
513 Offliner::RequestStatus watchdog_status =
514 Offliner::REQUEST_COORDINATOR_TIMED_OUT;
515 StopPrerendering(watchdog_status);
516 TryNextRequest();
517 }
518
519 // Returns true if the caller should expect a callback, false otherwise. For
520 // instance, this would return false if a request is already in progress.
521 bool RequestCoordinator::StartProcessing(
522 const DeviceConditions& device_conditions,
523 const base::Callback<void(bool)>& callback) {
524 DVLOG(2) << "Scheduled " << __func__;
525 return StartProcessingInternal(ProcessingWindowState::SCHEDULED_WINDOW,
526 device_conditions, callback);
527 }
528
529 bool RequestCoordinator::StartProcessingInternal(
530 const ProcessingWindowState processing_state,
531 const DeviceConditions& device_conditions,
532 const base::Callback<void(bool)>& callback) {
533 current_conditions_.reset(new DeviceConditions(device_conditions));
534 if (is_starting_ || is_busy_)
535 return false;
536 processing_state_ = processing_state;
537 scheduler_callback_ = callback;
538
539 // Mark the time at which we started processing so we can check our time
540 // budget.
541 operation_start_time_ = base::Time::Now();
542
543 TryNextRequest();
544
545 return true;
546 }
547
548 void RequestCoordinator::StartImmediatelyIfConnected() {
549 OfflinerImmediateStartStatus immediate_start_status = TryImmediateStart();
550 UMA_HISTOGRAM_ENUMERATION(
551 "OfflinePages.Background.ImmediateStartStatus", immediate_start_status,
552 RequestCoordinator::OfflinerImmediateStartStatus::STATUS_COUNT);
553 }
554
555 RequestCoordinator::OfflinerImmediateStartStatus
556 RequestCoordinator::TryImmediateStart() {
557 DVLOG(2) << "Immediate " << __func__;
558 // Make sure not already busy processing.
559 if (is_busy_)
560 return OfflinerImmediateStartStatus::BUSY;
561
562 // Make sure we are not on svelte device to start immediately.
563 if (is_low_end_device_ &&
564 !offline_pages::IsOfflinePagesSvelteConcurrentLoadingEnabled()) {
565 DVLOG(2) << "low end device, returning";
566 // Let the scheduler know we are done processing and failed due to svelte.
567 immediate_schedule_callback_.Run(false);
568 return OfflinerImmediateStartStatus::NOT_STARTED_ON_SVELTE;
569 }
570
571 if (GetConnectionType() ==
572 net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE)
573 return OfflinerImmediateStartStatus::NO_CONNECTION;
574
575 // Start processing with manufactured conservative battery conditions
576 // (i.e., assume no battery).
577 // TODO(dougarnett): Obtain actual battery conditions (from Android/Java).
578
579 DeviceConditions device_conditions(false, 0, GetConnectionType());
580 if (StartProcessingInternal(ProcessingWindowState::IMMEDIATE_WINDOW,
581 device_conditions, immediate_schedule_callback_))
582 return OfflinerImmediateStartStatus::STARTED;
583 else
584 return OfflinerImmediateStartStatus::NOT_ACCEPTED;
585 }
586
587 void RequestCoordinator::TryNextRequest() {
588 is_starting_ = true;
589 base::TimeDelta processing_time_budget;
590 if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
591 processing_time_budget = base::TimeDelta::FromSeconds(
592 policy_->GetProcessingTimeBudgetWhenBackgroundScheduledInSeconds());
593 } else {
594 DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
595 processing_time_budget = base::TimeDelta::FromSeconds(
596 policy_->GetProcessingTimeBudgetForImmediateLoadInSeconds());
597 }
598
599 // If there is no network or no time left in the budget, return to the
600 // scheduler. We do not remove the pending scheduler task that was set
601 // up earlier in case we run out of time, so the background scheduler
602 // will return to us at the next opportunity to run background tasks.
603 if (GetConnectionType() ==
604 net::NetworkChangeNotifier::ConnectionType::CONNECTION_NONE ||
605 (base::Time::Now() - operation_start_time_) > processing_time_budget) {
606 is_starting_ = false;
607
608 // Let the scheduler know we are done processing.
609 // TODO: Make sure the scheduler callback is valid before running it.
610 scheduler_callback_.Run(true);
611 DVLOG(2) << " out of time, giving up. " << __func__;
612
613 return;
614 }
615
616 // Ask request queue to make a new PickRequestTask object, then put it on the
617 // task queue.
618 queue_->PickNextRequest(base::Bind(&RequestCoordinator::RequestPicked,
619 weak_ptr_factory_.GetWeakPtr()),
620 base::Bind(&RequestCoordinator::RequestNotPicked,
621 weak_ptr_factory_.GetWeakPtr()),
622 *current_conditions_.get(), disabled_requests_);
623 // TODO(petewil): Verify current_conditions has a good value on all calling
624 // paths. It is really more of a "last known conditions" than "current
625 // conditions". Consider having a call to Java to check the current
626 // conditions.
627 }
628
629 // Called by the request picker when a request has been picked.
630 void RequestCoordinator::RequestPicked(const SavePageRequest& request) {
631 DVLOG(2) << request.url() << " " << __func__;
632 is_starting_ = false;
633
634 // Make sure we were not stopped while picking.
635 if (processing_state_ != ProcessingWindowState::STOPPED) {
636 // Send the request on to the offliner.
637 SendRequestToOffliner(request);
638 }
639 }
640
641 void RequestCoordinator::RequestNotPicked(
642 bool non_user_requested_tasks_remaining) {
643 DVLOG(2) << __func__;
644 is_starting_ = false;
645
646 // Clear the outstanding "safety" task in the scheduler.
647 scheduler_->Unschedule();
648
649 // If disabled tasks remain, post a new safety task for 5 sec from now.
650 if (disabled_requests_.size() > 0) {
651 scheduler_->BackupSchedule(GetTriggerConditions(kUserRequest),
652 kDisabledTaskRecheckSeconds);
653 } else if (non_user_requested_tasks_remaining) {
654 // If we don't have any of those, check for non-user-requested tasks.
655 scheduler_->Schedule(GetTriggerConditions(!kUserRequest));
656 }
657
658 // Let the scheduler know we are done processing.
659 scheduler_callback_.Run(true);
660 }
661
662 void RequestCoordinator::SendRequestToOffliner(const SavePageRequest& request) {
663 // Check that offlining didn't get cancelled while performing some async
664 // steps.
665 if (processing_state_ == ProcessingWindowState::STOPPED)
666 return;
667
668 GetOffliner();
669 if (!offliner_) {
670 DVLOG(0) << "Unable to create Offliner. "
671 << "Cannot background offline page.";
672 return;
673 }
674
675 DCHECK(!is_busy_);
676 is_busy_ = true;
677
678 // Record start time if this is first attempt.
679 if (request.started_attempt_count() == 0) {
680 RecordStartTimeUMA(request);
681 }
682
683 // Mark attempt started in the database and start offliner when completed.
684 queue_->MarkAttemptStarted(
685 request.request_id(),
686 base::Bind(&RequestCoordinator::StartOffliner,
687 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
688 request.client_id().name_space));
689 }
690
691 void RequestCoordinator::StartOffliner(
692 int64_t request_id,
693 const std::string& client_namespace,
694 std::unique_ptr<UpdateRequestsResult> update_result) {
695 if (update_result->store_state != StoreState::LOADED ||
696 update_result->item_statuses.size() != 1 ||
697 update_result->item_statuses.at(0).first != request_id ||
698 update_result->item_statuses.at(0).second != ItemActionStatus::SUCCESS) {
699 is_busy_ = false;
700 // TODO(fgorski): what is the best result? Do we create a new status?
701 StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
702 DVLOG(1) << "Failed to mark attempt started: " << request_id;
703 UpdateRequestResult request_result =
704 update_result->store_state != StoreState::LOADED
705 ? UpdateRequestResult::STORE_FAILURE
706 : UpdateRequestResult::REQUEST_DOES_NOT_EXIST;
707 event_logger_.RecordUpdateRequestFailed(client_namespace, request_result);
708 return;
709 }
710
711 // TODO(fgorski): Switch to request_id only, so that this value is not written
712 // back to the store.
713 active_request_.reset(
714 new SavePageRequest(update_result->updated_items.at(0)));
715
716 // Inform observer of active request.
717 NotifyChanged(*active_request_.get());
718
719 // Start the load and save process in the offliner (Async).
720 if (offliner_->LoadAndSave(
721 update_result->updated_items.at(0),
722 base::Bind(&RequestCoordinator::OfflinerDoneCallback,
723 weak_ptr_factory_.GetWeakPtr()))) {
724 base::TimeDelta timeout;
725 if (processing_state_ == ProcessingWindowState::SCHEDULED_WINDOW) {
726 timeout = base::TimeDelta::FromSeconds(
727 policy_->GetSinglePageTimeLimitWhenBackgroundScheduledInSeconds());
728 } else {
729 DCHECK(processing_state_ == ProcessingWindowState::IMMEDIATE_WINDOW);
730 timeout = base::TimeDelta::FromSeconds(
731 policy_->GetSinglePageTimeLimitForImmediateLoadInSeconds());
732 }
733
734 // Start a watchdog timer to catch pre-renders running too long
735 watchdog_timer_.Start(FROM_HERE, timeout, this,
736 &RequestCoordinator::HandleWatchdogTimeout);
737 } else {
738 is_busy_ = false;
739 DVLOG(0) << "Unable to start LoadAndSave";
740 StopProcessing(Offliner::PRERENDERING_NOT_STARTED);
741 }
742 }
743
744 void RequestCoordinator::OfflinerDoneCallback(const SavePageRequest& request,
745 Offliner::RequestStatus status) {
746 DVLOG(2) << "offliner finished, saved: "
747 << (status == Offliner::RequestStatus::SAVED)
748 << ", status: " << static_cast<int>(status) << ", " << __func__;
749 DCHECK_NE(status, Offliner::RequestStatus::UNKNOWN);
750 DCHECK_NE(status, Offliner::RequestStatus::LOADED);
751 event_logger_.RecordOfflinerResult(request.client_id().name_space, status,
752 request.request_id());
753 last_offlining_status_ = status;
754 RecordOfflinerResultUMA(request.client_id(), request.creation_time(),
755 last_offlining_status_);
756 watchdog_timer_.Stop();
757 is_busy_ = false;
758 active_request_.reset(nullptr);
759
760 UpdateRequestForCompletedAttempt(request, status);
761 if (ShouldTryNextRequest(status))
762 TryNextRequest();
763 else
764 scheduler_callback_.Run(true);
765 }
766
767 void RequestCoordinator::UpdateRequestForCompletedAttempt(
768 const SavePageRequest& request,
769 Offliner::RequestStatus status) {
770 if (status == Offliner::RequestStatus::FOREGROUND_CANCELED ||
771 status == Offliner::RequestStatus::PRERENDERING_CANCELED) {
772 // Update the request for the canceled attempt.
773 // TODO(dougarnett): See if we can conclusively identify other attempt
774 // aborted cases to treat this way (eg, for Render Process Killed).
775 UpdateRequestForAbortedAttempt(request);
776 } else if (status == Offliner::RequestStatus::SAVED) {
777 // Remove the request from the queue if it succeeded.
778 RemoveAttemptedRequest(request,
779 RequestNotifier::BackgroundSavePageResult::SUCCESS);
780 } else if (status == Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY) {
781 RemoveAttemptedRequest(
782 request, RequestNotifier::BackgroundSavePageResult::PRERENDER_FAILURE);
783 } else if (request.completed_attempt_count() + 1 >=
784 policy_->GetMaxCompletedTries()) {
785 // Remove from the request queue if we exceeded max retries. The +1
786 // represents the request that just completed. Since we call
787 // MarkAttemptCompleted within the if branches, the completed_attempt_count
788 // has not yet been updated when we are checking the if condition.
789 const RequestNotifier::BackgroundSavePageResult result(
790 RequestNotifier::BackgroundSavePageResult::RETRY_COUNT_EXCEEDED);
791 event_logger_.RecordDroppedSavePageRequest(request.client_id().name_space,
792 result, request.request_id());
793 RemoveAttemptedRequest(request, result);
794 } else {
795 // If we failed, but are not over the limit, update the request in the
796 // queue.
797 queue_->MarkAttemptCompleted(
798 request.request_id(),
799 base::Bind(&RequestCoordinator::MarkAttemptCompletedDoneCallback,
800 weak_ptr_factory_.GetWeakPtr(), request.request_id(),
801 request.client_id()));
802 }
803 }
804
805 bool RequestCoordinator::ShouldTryNextRequest(
806 Offliner::RequestStatus previous_request_status) {
807 switch (previous_request_status) {
808 case Offliner::RequestStatus::SAVED:
809 case Offliner::RequestStatus::SAVE_FAILED:
810 case Offliner::RequestStatus::REQUEST_COORDINATOR_CANCELED:
811 case Offliner::RequestStatus::REQUEST_COORDINATOR_TIMED_OUT:
812 case Offliner::RequestStatus::PRERENDERING_FAILED_NO_RETRY:
813 return true;
814 case Offliner::RequestStatus::FOREGROUND_CANCELED:
815 case Offliner::RequestStatus::PRERENDERING_CANCELED:
816 case Offliner::RequestStatus::PRERENDERING_FAILED:
817 // No further processing in this service window.
818 return false;
819 default:
820 // Make explicit choice about new status codes that actually reach here.
821 // Their default is no further processing in this service window.
822 NOTREACHED();
823 return false;
824 }
825 }
826
827 void RequestCoordinator::EnableForOffliner(int64_t request_id) {
828 // Since the recent tab helper might call multiple times, ignore subsequent
829 // calls for a particular request_id.
830 if (disabled_requests_.find(request_id) == disabled_requests_.end())
831 return;
832 disabled_requests_.erase(request_id);
833 // If we are not busy, start processing right away.
834 StartImmediatelyIfConnected();
835 }
836
837 void RequestCoordinator::MarkRequestCompleted(int64_t request_id) {
838 // Since the recent tab helper might call multiple times, ignore subsequent
839 // calls for a particular request_id.
840 if (disabled_requests_.find(request_id) == disabled_requests_.end())
841 return;
842 disabled_requests_.erase(request_id);
843
844 // Remove the request, but send out SUCCEEDED instead of removed.
845 std::vector<int64_t> request_ids { request_id };
846 queue_->RemoveRequests(
847 request_ids,
848 base::Bind(&RequestCoordinator::HandleRemovedRequestsAndCallback,
849 weak_ptr_factory_.GetWeakPtr(),
850 base::Bind(&RequestCoordinator::CompletedRequestCallback,
851 weak_ptr_factory_.GetWeakPtr()),
852 RequestNotifier::BackgroundSavePageResult::SUCCESS));
853 }
854
855 const Scheduler::TriggerConditions RequestCoordinator::GetTriggerConditions(
856 const bool user_requested) {
857 return Scheduler::TriggerConditions(
858 policy_->PowerRequired(user_requested),
859 policy_->BatteryPercentageRequired(user_requested),
860 policy_->UnmeteredNetworkRequired(user_requested));
861 }
862
863 void RequestCoordinator::AddObserver(Observer* observer) {
864 DCHECK(observer);
865 observers_.AddObserver(observer);
866 }
867
868 void RequestCoordinator::RemoveObserver(Observer* observer) {
869 observers_.RemoveObserver(observer);
870 }
871
872 void RequestCoordinator::NotifyAdded(const SavePageRequest& request) {
873 for (Observer& observer : observers_)
874 observer.OnAdded(request);
875 }
876
877 void RequestCoordinator::NotifyCompleted(
878 const SavePageRequest& request,
879 RequestNotifier::BackgroundSavePageResult status) {
880 for (Observer& observer : observers_)
881 observer.OnCompleted(request, status);
882 }
883
884 void RequestCoordinator::NotifyChanged(const SavePageRequest& request) {
885 for (Observer& observer : observers_)
886 observer.OnChanged(request);
887 }
888
889 void RequestCoordinator::GetOffliner() {
890 if (!offliner_) {
891 offliner_ = factory_->GetOffliner(policy_.get());
892 }
893 }
894
895 ClientPolicyController* RequestCoordinator::GetPolicyController() {
896 return policy_controller_.get();
897 }
898
899 void RequestCoordinator::Shutdown() {
900 network_quality_estimator_ = nullptr;
901 }
902
903 } // namespace offline_pages
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698