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

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

Powered by Google App Engine
This is Rietveld 408576698