blob: bd951b519ed631817d4d3faee97b225bca515ad0 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
#define COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/containers/circular_deque.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/supports_user_data.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/offline_pages/core/background/connection_notifier.h"
#include "components/offline_pages/core/background/device_conditions.h"
#include "components/offline_pages/core/background/pending_state_updater.h"
#include "components/offline_pages/core/background/request_coordinator_event_logger.h"
#include "components/offline_pages/core/background/request_notifier.h"
#include "components/offline_pages/core/background/request_queue.h"
#include "components/offline_pages/core/background/scheduler.h"
#include "net/nqe/network_quality_estimator.h"
#include "url/gurl.h"
namespace network {
class NetworkQualityTracker;
}
namespace offline_pages {
struct ClientId;
class OfflinerPolicy;
class Offliner;
class SavePageRequest;
class ClientPolicyController;
class OfflinePagesUkmReporter;
// Coordinates queueing and processing save page later requests.
class RequestCoordinator : public KeyedService,
public RequestNotifier,
public base::SupportsUserData {
public:
// Nested observer class. To make sure that no events are missed, the client
// code should first register for notifications, then |GetAllRequests|, and
// ignore all events before the return from |GetAllRequests|, and consume
// events after the return callback from |GetAllRequests|.
class Observer {
public:
virtual ~Observer() = default;
virtual void OnAdded(const SavePageRequest& request) = 0;
virtual void OnCompleted(
const SavePageRequest& request,
RequestNotifier::BackgroundSavePageResult status) = 0;
virtual void OnChanged(const SavePageRequest& request) = 0;
virtual void OnNetworkProgress(const SavePageRequest& request,
int64_t received_bytes) = 0;
};
enum class RequestAvailability {
ENABLED_FOR_OFFLINER,
DISABLED_FOR_OFFLINER,
};
enum class RequestCoordinatorState {
IDLE,
PICKING,
OFFLINING,
};
// Describes the parameters to control how to save a page when system
// conditions allow.
struct SavePageLaterParams {
SavePageLaterParams();
SavePageLaterParams(const SavePageLaterParams& other);
~SavePageLaterParams();
// The last committed URL of the page to save.
GURL url;
// The identification used by the client.
ClientId client_id;
// Whether the user requests the save action. Defaults to true.
bool user_requested;
// Request availability. Defaults to ENABLED_FOR_OFFLINER.
RequestAvailability availability;
// The original URL of the page to save. Empty if no redirect occurs.
GURL original_url;
// The origin of the request, if any.
std::string request_origin;
};
// Callback specifying which request IDs were actually removed.
typedef base::OnceCallback<void(const MultipleItemStatuses&)>
RemoveRequestsCallback;
// Callback that receives the response for GetAllRequests.
typedef base::OnceCallback<void(
std::vector<std::unique_ptr<SavePageRequest>>)>
GetRequestsCallback;
// Callback for stopping the background offlining.
typedef base::OnceCallback<void(int64_t request_id)> CancelCallback;
// Callback for SavePageLater calls.
typedef base::OnceCallback<void(AddRequestResult)> SavePageLaterCallback;
RequestCoordinator(std::unique_ptr<OfflinerPolicy> policy,
std::unique_ptr<Offliner> offliner,
std::unique_ptr<RequestQueue> queue,
std::unique_ptr<Scheduler> scheduler,
network::NetworkQualityTracker* network_quality_tracker,
std::unique_ptr<OfflinePagesUkmReporter> ukm_reporter);
~RequestCoordinator() override;
// Queues |request| to later load and save when system conditions allow.
// Returns an id if the page could be queued successfully, 0L otherwise.
int64_t SavePageLater(const SavePageLaterParams& save_page_later_params,
SavePageLaterCallback save_page_later_callback);
// Remove a list of requests by |request_id|. This removes requests from the
// request queue, and cancels an in-progress offliner.
void RemoveRequests(const std::vector<int64_t>& request_ids,
RemoveRequestsCallback callback);
// Pause a list of requests by |request_id|. This will change the state
// in the request queue so the request cannot be started.
void PauseRequests(const std::vector<int64_t>& request_ids);
// Resume a list of previously paused requests, making them available.
void ResumeRequests(const std::vector<int64_t>& request_ids);
// Get all save page request items in the callback.
void GetAllRequests(GetRequestsCallback callback);
// Starts processing of one or more queued save page later requests
// in scheduled background mode.
// Returns whether processing was started and that caller should expect
// a callback. If processing was already active, returns false.
bool StartScheduledProcessing(
const DeviceConditions& device_conditions,
const base::RepeatingCallback<void(bool)>& callback);
// Attempts to starts processing of one or more queued save page later
// requests (if device conditions are suitable) in immediate mode
// (opposed to scheduled background mode). This method is suitable to call
// when there is some user action that suggests the user wants to do this
// operation now, if possible, vs. trying to do it in the background when
// idle.
// Returns whether processing was started and that caller should expect
// a callback. If processing was already active or some condition was
// not suitable for immediate processing (e.g., network or low-end device),
// returns false.
bool StartImmediateProcessing(
const base::RepeatingCallback<void(bool)>& callback);
// Stops the current request processing if active. This is a way for
// caller to abort processing; otherwise, processing will complete on
// its own. In either case, the callback will be called when processing
// is stopped or complete.
void StopProcessing(Offliner::RequestStatus stop_status);
// Used to denote that the foreground thread is ready for the offliner
// to start work on a previously entered, but unavailable request.
void EnableForOffliner(int64_t request_id, const ClientId& client_id);
// If a request that is unavailable to the offliner is finished elsewhere,
// (by the tab helper synchronous download), send a notificaiton that it
// succeeded through our notificaiton system.
void MarkRequestCompleted(int64_t request_id);
const Scheduler::TriggerConditions GetTriggerConditions(
const bool user_requested);
// A way for tests to set the callback in use when an operation is over.
void SetProcessingCallbackForTest(
const base::RepeatingCallback<void(bool)>& callback) {
scheduler_callback_ = callback;
}
// A way to set the callback which would be called if processing will be
// triggered immediately internally by the coordinator. Used by testing
// harness to determine if a request has been processed.
void SetInternalStartProcessingCallbackForTest(
const base::RepeatingCallback<void(bool)>& callback) {
internal_start_processing_callback_ = callback;
}
void StartImmediatelyForTest() { StartImmediatelyIfConnected(); }
// Observers implementing the RequestCoordinator::Observer interface can
// register here to get notifications of changes to request state. This
// pointer is not owned, and it is the callers responsibility to remove the
// observer before the observer is deleted.
void AddObserver(RequestCoordinator::Observer* observer);
void RemoveObserver(RequestCoordinator::Observer* observer);
// Implement RequestNotifier
void NotifyAdded(const SavePageRequest& request) override;
void NotifyCompleted(
const SavePageRequest& request,
RequestNotifier::BackgroundSavePageResult status) override;
void NotifyChanged(const SavePageRequest& request) override;
void NotifyNetworkProgress(const SavePageRequest& request,
int64_t received_bytes) override;
// Returns the request queue used for requests. Coordinator keeps ownership.
RequestQueue* queue() { return queue_.get(); }
// Return an unowned pointer to the Scheduler.
Scheduler* scheduler() { return scheduler_.get(); }
OfflinerPolicy* policy() { return policy_.get(); }
ClientPolicyController* GetPolicyController();
// Returns the status of the most recent offlining.
Offliner::RequestStatus last_offlining_status() {
return last_offlining_status_;
}
// Return the state of the request coordinator.
RequestCoordinatorState state() { return state_; }
// Tracks whether the last offlining attempt got canceled. This is reset by
// the next call to start processing.
bool is_canceled() {
return processing_state_ == ProcessingWindowState::STOPPED;
}
RequestCoordinatorEventLogger* GetLogger() { return &event_logger_; }
private:
// Immediate start attempt status code for UMA.
// These values are written to logs. New enum values can be added, but
// existing enums must never be renumbered or deleted and reused.
// For any additions, also update corresponding histogram in histograms.xml.
enum OfflinerImmediateStartStatus {
// Did start processing request.
STARTED = 0,
// Already busy processing a request.
BUSY = 1,
// The Offliner did not accept processing the request.
NOT_ACCEPTED = 2,
// No current network connection.
NO_CONNECTION = 3,
// Weak network connection (worse than 2G speed)
// according to network quality estimator.
WEAK_CONNECTION = 4,
// Did not start because this is svelte device.
NOT_STARTED_ON_SVELTE = 5,
// NOTE: insert new values above this line and update histogram enum too.
STATUS_COUNT = 6,
};
enum class ProcessingWindowState {
STOPPED,
SCHEDULED_WINDOW,
IMMEDIATE_WINDOW,
};
// Receives the results of a get from the request queue, and turns that into
// SavePageRequest objects for the caller of GetQueuedRequests.
void GetQueuedRequestsCallback(
GetRequestsCallback callback,
GetRequestsResult result,
std::vector<std::unique_ptr<SavePageRequest>> requests);
// Receives the results of a get from the request queue, and turns that into
// SavePageRequest objects for the caller of GetQueuedRequests.
void GetRequestsForSchedulingCallback(
GetRequestsResult result,
std::vector<std::unique_ptr<SavePageRequest>> requests);
// Receives the result of add requests to the request queue.
void AddRequestResultCallback(SavePageLaterCallback save_page_later_callback,
RequestAvailability availability,
AddRequestResult result,
const SavePageRequest& request);
void UpdateMultipleRequestsCallback(
std::unique_ptr<UpdateRequestsResult> result);
void ReconcileCallback(std::unique_ptr<UpdateRequestsResult> result);
void HandleRemovedRequestsAndCallback(
RemoveRequestsCallback callback,
RequestNotifier::BackgroundSavePageResult status,
std::unique_ptr<UpdateRequestsResult> result);
void HandleRemovedRequests(RequestNotifier::BackgroundSavePageResult status,
std::unique_ptr<UpdateRequestsResult> result);
// Handle updating of request status after cancel is called. Will call
// HandleCancelRecordResultCallback for UMA handling
void HandleCancelUpdateStatusCallback(
CancelCallback next_callback,
Offliner::RequestStatus stop_status,
const SavePageRequest& canceled_request);
void UpdateStatusForCancel(Offliner::RequestStatus stop_status);
void ResetActiveRequestCallback(int64_t offline_id);
void StartSchedulerCallback(int64_t offline_id);
void TryNextRequestCallback(int64_t offline_id);
bool StartProcessingInternal(
const ProcessingWindowState processing_state,
const base::RepeatingCallback<void(bool)>& callback);
// Start processing now if connected (but with conservative assumption
// as to other device conditions).
void StartImmediatelyIfConnected();
OfflinerImmediateStartStatus TryImmediateStart(
const base::RepeatingCallback<void(bool)>& callback);
// Requests a callback upon the next network connection to start processing.
void RequestConnectedEventForStarting();
// Clears the request for connected event if it was set.
void ClearConnectedEventRequest();
// Handles receiving a connection event. Will start immediate processing.
void HandleConnectedEventForStarting();
// Check the request queue, and schedule a task corresponding
// to the least restrictive type of request in the queue.
void ScheduleAsNeeded();
// Callback from the request picker when it has chosen our next request.
void RequestPicked(
const SavePageRequest& request,
std::unique_ptr<std::vector<SavePageRequest>> available_requests,
bool cleanup_needed);
// Callback from the request picker when no more requests are in the queue.
// The parameter is a signal for what (if any) conditions to schedule future
// processing for.
void RequestNotPicked(bool non_user_requested_tasks_remaining,
bool cleanup_needed);
// Callback from request picker that receives the current available queued
// request count as well as the total queued request count (which may be
// different if unavailable requests are queued such as paused requests).
// It also receives a flag as to whether this request picking is due to the
// start of a request processing window.
void RequestCounts(bool is_start_of_processing,
size_t total_requests,
size_t available_requests);
void HandleWatchdogTimeout();
// Cancels an in progress offlining, and updates state appropriately.
void StopOfflining(CancelCallback callback,
Offliner::RequestStatus stop_status);
// Marks attempt on the request and sends it to offliner in continuation.
void SendRequestToOffliner(const SavePageRequest& request);
// Continuation of |SendRequestToOffliner| after the request is marked as
// started.
void StartOffliner(int64_t request_id,
const std::string& client_namespace,
std::unique_ptr<UpdateRequestsResult> update_result);
// Called by the offliner when an offlining request is completed. (and by
// tests).
void OfflinerDoneCallback(const SavePageRequest& request,
Offliner::RequestStatus status);
// Called by the offliner periodically to report the accumulated count of
// bytes received from the network.
void OfflinerProgressCallback(const SavePageRequest& request,
int64_t received_bytes);
// Records a completed attempt for the request and update it in the queue
// (possibly removing it).
void UpdateRequestForCompletedAttempt(const SavePageRequest& request,
Offliner::RequestStatus status);
// Returns whether we should try another request based on the outcome
// of the previous one.
bool ShouldTryNextRequest(Offliner::RequestStatus previous_request_status);
// Try to find and start offlining an available request.
// |is_start_of_processing| identifies if this is the beginning of a
// processing window (vs. continuing within a current processing window).
void TryNextRequest(bool is_start_of_processing);
// Cross the JNI Bridge and get the current device conditions from Android.
void UpdateCurrentConditionsFromAndroid();
// If there is an active request in the list, cancel that request.
bool CancelActiveRequestIfItMatches(const std::vector<int64_t>& request_ids);
// Records an aborted attempt for the request and update it in the queue
// (possibly removing it).
void UpdateRequestForAbortedAttempt(const SavePageRequest& request);
// Remove the attempted request from the queue with status to pass through to
// any observers and UMA histogram.
void RemoveAttemptedRequest(const SavePageRequest& request,
BackgroundSavePageResult status);
// Marks the attempt as aborted. This makes the request available again
// for offlining.
void MarkAttemptAborted(int64_t request_id, const std::string& name_space);
// Reports change from marking request, reports an error if it fails.
void MarkAttemptDone(int64_t request_id,
const std::string& name_space,
std::unique_ptr<UpdateRequestsResult> result);
// Reports offliner status through UMA and event logger.
void RecordOfflinerResult(const SavePageRequest& request,
Offliner::RequestStatus status);
void SetDeviceConditionsForTest(const DeviceConditions& current_conditions) {
use_test_device_conditions_ = true;
current_conditions_.reset(new DeviceConditions(current_conditions));
}
// KeyedService implementation:
void Shutdown() override;
friend class RequestCoordinatorTest;
// Cached value of whether low end device. Overwritable for testing.
bool is_low_end_device_;
// Current state of the request coordinator.
RequestCoordinatorState state_;
// Identifies the type of current processing window or if processing stopped.
ProcessingWindowState processing_state_;
// True if we should use the test device conditions instead of actual
// conditions.
bool use_test_device_conditions_;
// For use by tests, a fake network connection type
net::NetworkChangeNotifier::ConnectionType test_connection_type_;
// Owned pointer to the current offliner.
std::unique_ptr<Offliner> offliner_;
base::Time operation_start_time_;
// The observers.
base::ObserverList<Observer> observers_;
// Last known conditions for network, battery
std::unique_ptr<DeviceConditions> current_conditions_;
// RequestCoordinator takes over ownership of the policy
std::unique_ptr<OfflinerPolicy> policy_;
// RequestQueue. Used to store incoming requests. Owned.
std::unique_ptr<RequestQueue> queue_;
// Scheduler. Used to request a callback when network is available. Owned.
std::unique_ptr<Scheduler> scheduler_;
// Controller of client policies. Owned.
std::unique_ptr<ClientPolicyController> policy_controller_;
// Unowned pointer. Guaranteed to be non-null during the lifetime of |this|.
// Must be accessed only on the UI thread.
network::NetworkQualityTracker* network_quality_tracker_;
// Object that can record Url Keyed Metrics (UKM).
std::unique_ptr<OfflinePagesUkmReporter> ukm_reporter_;
net::EffectiveConnectionType network_quality_at_request_start_;
// Holds an ID of the currently active request.
int64_t active_request_id_;
// Status of the most recent offlining.
Offliner::RequestStatus last_offlining_status_;
// A set of request_ids that we are holding off until the download manager is
// done with them.
std::set<int64_t> disabled_requests_;
// The processing callback to call when processing the current processing
// window stops. It is set from the Start*Processing() call that triggered
// the processing or it may be the |internal_start_processing_callback_| if
// processing was triggered internally.
// For StartScheduledProcessing() processing, calling its callback returns
// to the scheduler across the JNI bridge.
base::RepeatingCallback<void(bool)> scheduler_callback_;
// Callback invoked when internally triggered processing is done. It is
// kept as a class member so that it may be overridden for test visibility.
base::RepeatingCallback<void(bool)> internal_start_processing_callback_;
// Logger to record events.
RequestCoordinatorEventLogger event_logger_;
// Timer to watch for pre-render attempts running too long.
base::OneShotTimer watchdog_timer_;
// Used for potential immediate processing when we get network connection.
std::unique_ptr<ConnectionNotifier> connection_notifier_;
// Used to track prioritized requests.
// The requests can only be added by RC when they are resumed and there are
// two places where deletion from the |prioritized_requests_| would happen:
// 1. When request is paused RC will remove it.
// 2. When a task is not available to be picked by PickRequestTask (because
// it was completed or cancelled), the task will remove it.
// Currently it's used as LIFO.
// TODO(romax): see if LIFO is a good idea or change to FIFO. crbug.com/705106
base::circular_deque<int64_t> prioritized_requests_;
// Updates a request's PendingState.
PendingStateUpdater pending_state_updater_;
// Allows us to pass a weak pointer to callbacks.
base::WeakPtrFactory<RequestCoordinator> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RequestCoordinator);
};
} // namespace offline_pages
#endif // COMPONENTS_OFFLINE_PAGES_CORE_BACKGROUND_REQUEST_COORDINATOR_H_