blob: c2d26b77d5fa1249cfcf5d72c3c8fd641b00e66c [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_BACKGROUND_REQUEST_COORDINATOR_H_
#define COMPONENTS_OFFLINE_PAGES_BACKGROUND_REQUEST_COORDINATOR_H_
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/callback.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/background/device_conditions.h"
#include "components/offline_pages/background/offliner.h"
#include "components/offline_pages/background/request_coordinator_event_logger.h"
#include "components/offline_pages/background/request_notifier.h"
#include "components/offline_pages/background/request_queue.h"
#include "components/offline_pages/background/scheduler.h"
#include "net/nqe/network_quality_estimator.h"
#include "url/gurl.h"
namespace offline_pages {
struct ClientId;
class OfflinerPolicy;
class OfflinerFactory;
class Offliner;
class RequestQueue;
class SavePageRequest;
class Scheduler;
class ClientPolicyController;
// 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;
};
enum class RequestAvailability {
ENABLED_FOR_OFFLINER,
DISABLED_FOR_OFFLINER,
};
// Callback specifying which request IDs were actually removed.
typedef base::Callback<void(const MultipleItemStatuses&)>
RemoveRequestsCallback;
// Callback that receives the response for GetAllRequests.
typedef base::Callback<void(std::vector<std::unique_ptr<SavePageRequest>>)>
GetRequestsCallback;
RequestCoordinator(std::unique_ptr<OfflinerPolicy> policy,
std::unique_ptr<OfflinerFactory> factory,
std::unique_ptr<RequestQueue> queue,
std::unique_ptr<Scheduler> scheduler,
net::NetworkQualityEstimator::NetworkQualityProvider*
network_quality_estimator);
~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 GURL& url,
const ClientId& client_id,
bool user_requested,
RequestAvailability availability);
// Remove a list of requests by |request_id|. This removes requests from the
// request queue, and cancels an in-progress prerender.
void RemoveRequests(const std::vector<int64_t>& request_ids,
const 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(const GetRequestsCallback& callback);
// Starts processing of one or more queued save page later requests.
// Returns whether processing was started and that caller should expect
// a callback. If processing was already active, returns false.
bool StartProcessing(const DeviceConditions& device_conditions,
const base::Callback<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::Callback<void(bool)> callback) {
scheduler_callback_ = callback;
}
// A way to set the callback which would be called if the request will be
// scheduled immediately. Used by testing harness to determine if a request
// has been processed.
void SetImmediateScheduleCallbackForTest(
const base::Callback<void(bool)> callback) {
immediate_schedule_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;
// 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_;
}
bool is_busy() {
return is_busy_;
}
// Returns whether processing is starting (before it is decided to actually
// process a request (is_busy()) at this time or not.
bool is_starting() { return is_starting_; }
// Tracks whether the last offlining attempt got canceled. This is reset by
// the next StartProcessing() call.
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(
const 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(RequestAvailability availability,
AddRequestResult result,
const SavePageRequest& request);
void UpdateMultipleRequestsCallback(
std::unique_ptr<UpdateRequestsResult> result);
void HandleRemovedRequestsAndCallback(
const RemoveRequestsCallback& callback,
RequestNotifier::BackgroundSavePageResult status,
std::unique_ptr<UpdateRequestsResult> result);
void HandleRemovedRequests(RequestNotifier::BackgroundSavePageResult status,
std::unique_ptr<UpdateRequestsResult> result);
bool StartProcessingInternal(const ProcessingWindowState processing_state,
const DeviceConditions& device_conditions,
const base::Callback<void(bool)>& callback);
// Start processing now if connected (but with conservative assumption
// as to other device conditions).
void StartImmediatelyIfConnected();
OfflinerImmediateStartStatus TryImmediateStart();
// 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);
// 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);
// 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 pre-rendering, and updates state appropriately.
void StopPrerendering(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);
// 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);
// 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);
// Returns the appropriate offliner to use, getting a new one from the factory
// if needed.
void GetOffliner();
// Method to wrap calls to getting the connection type so it can be
// changed for tests.
net::NetworkChangeNotifier::ConnectionType GetConnectionType();
void SetNetworkConditionsForTest(
net::NetworkChangeNotifier::ConnectionType connection) {
use_test_connection_type_ = true;
test_connection_type_ = connection;
}
void SetDeviceConditionsForTest(const DeviceConditions& current_conditions) {
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_;
// The offliner can only handle one request at a time - if the offliner is
// busy, prevent other requests. This flag marks whether the offliner is in
// use.
bool is_busy_;
// There is more than one path to start processing so this flag is used
// to avoid race conditions before is_busy_ is established.
bool is_starting_;
// Identifies the type of current processing window or if processing stopped.
ProcessingWindowState processing_state_;
// True if we should use the test connection type instead of the actual type.
bool use_test_connection_type_;
// For use by tests, a fake network connection type
net::NetworkChangeNotifier::ConnectionType test_connection_type_;
// Unowned pointer to the current offliner, if any.
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_;
// OfflinerFactory. Used to create offline pages. Owned.
std::unique_ptr<OfflinerFactory> factory_;
// 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 to the Network Quality Estimator.
net::NetworkQualityEstimator::NetworkQualityProvider*
network_quality_estimator_;
// Holds copy of the active request, if any.
std::unique_ptr<SavePageRequest> active_request_;
// 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_;
// Calling this returns to the scheduler across the JNI bridge.
base::Callback<void(bool)> scheduler_callback_;
// Logger to record events.
RequestCoordinatorEventLogger event_logger_;
// Timer to watch for pre-render attempts running too long.
base::OneShotTimer watchdog_timer_;
// Callback invoked when an immediate request is done (default empty).
base::Callback<void(bool)> immediate_schedule_callback_;
// 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_BACKGROUND_REQUEST_COORDINATOR_H_