OLD | NEW |
| (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/pick_request_task.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/logging.h" | |
9 #include "base/time/time.h" | |
10 #include "components/offline_pages/background/device_conditions.h" | |
11 #include "components/offline_pages/background/offliner_policy.h" | |
12 #include "components/offline_pages/background/offliner_policy_utils.h" | |
13 #include "components/offline_pages/background/request_coordinator_event_logger.h
" | |
14 #include "components/offline_pages/background/request_notifier.h" | |
15 #include "components/offline_pages/background/request_queue_store.h" | |
16 #include "components/offline_pages/background/save_page_request.h" | |
17 | |
18 namespace { | |
19 template <typename T> | |
20 int signum(T t) { | |
21 return (T(0) < t) - (t < T(0)); | |
22 } | |
23 | |
24 bool kCleanupNeeded = true; | |
25 bool kNonUserRequestsFound = true; | |
26 | |
27 #define CALL_MEMBER_FUNCTION(object, ptrToMember) ((object)->*(ptrToMember)) | |
28 } // namespace | |
29 | |
30 namespace offline_pages { | |
31 | |
32 PickRequestTask::PickRequestTask(RequestQueueStore* store, | |
33 OfflinerPolicy* policy, | |
34 RequestPickedCallback picked_callback, | |
35 RequestNotPickedCallback not_picked_callback, | |
36 RequestCountCallback request_count_callback, | |
37 DeviceConditions& device_conditions, | |
38 const std::set<int64_t>& disabled_requests) | |
39 : store_(store), | |
40 policy_(policy), | |
41 picked_callback_(picked_callback), | |
42 not_picked_callback_(not_picked_callback), | |
43 request_count_callback_(request_count_callback), | |
44 disabled_requests_(disabled_requests), | |
45 weak_ptr_factory_(this) { | |
46 device_conditions_.reset(new DeviceConditions(device_conditions)); | |
47 } | |
48 | |
49 PickRequestTask::~PickRequestTask() {} | |
50 | |
51 void PickRequestTask::Run() { | |
52 GetRequests(); | |
53 } | |
54 | |
55 void PickRequestTask::GetRequests() { | |
56 // Get all the requests from the queue, we will classify them in the callback. | |
57 store_->GetRequests( | |
58 base::Bind(&PickRequestTask::Choose, weak_ptr_factory_.GetWeakPtr())); | |
59 } | |
60 | |
61 void PickRequestTask::Choose( | |
62 bool success, | |
63 std::vector<std::unique_ptr<SavePageRequest>> requests) { | |
64 // If there is nothing to do, return right away. | |
65 if (requests.empty()) { | |
66 request_count_callback_.Run(requests.size(), 0); | |
67 not_picked_callback_.Run(!kNonUserRequestsFound, !kCleanupNeeded); | |
68 TaskComplete(); | |
69 return; | |
70 } | |
71 | |
72 // Pick the most deserving request for our conditions. | |
73 const SavePageRequest* picked_request = nullptr; | |
74 | |
75 RequestCompareFunction comparator = nullptr; | |
76 | |
77 // Choose which comparison function to use based on policy. | |
78 if (policy_->RetryCountIsMoreImportantThanRecency()) | |
79 comparator = &PickRequestTask::RetryCountFirstCompareFunction; | |
80 else | |
81 comparator = &PickRequestTask::RecencyFirstCompareFunction; | |
82 | |
83 // TODO(petewil): Consider replacing this bool with a better named enum. | |
84 bool non_user_requested_tasks_remaining = false; | |
85 bool cleanup_needed = false; | |
86 | |
87 size_t available_request_count = 0; | |
88 | |
89 // Iterate once through the requests, keeping track of best candidate. | |
90 for (unsigned i = 0; i < requests.size(); ++i) { | |
91 // If the request is expired or has exceeded the retry count, skip it. | |
92 if (OfflinerPolicyUtils::CheckRequestExpirationStatus(requests[i].get(), | |
93 policy_) != | |
94 OfflinerPolicyUtils::RequestExpirationStatus::VALID) { | |
95 cleanup_needed = true; | |
96 continue; | |
97 } | |
98 | |
99 // If the request is on the disabled list, skip it. | |
100 auto search = disabled_requests_.find(requests[i]->request_id()); | |
101 if (search != disabled_requests_.end()) | |
102 continue; | |
103 | |
104 // If there are non-user-requested tasks remaining, we need to make sure | |
105 // that they are scheduled after we run out of user requested tasks. Here we | |
106 // detect if any exist. If we don't find any user-requested tasks, we will | |
107 // inform the "not_picked_callback_" that it needs to schedule a task for | |
108 // non-user-requested items, which have different network and power needs. | |
109 if (!requests[i]->user_requested()) | |
110 non_user_requested_tasks_remaining = true; | |
111 if (requests[i]->request_state() == | |
112 SavePageRequest::RequestState::AVAILABLE) { | |
113 available_request_count++; | |
114 } | |
115 if (!RequestConditionsSatisfied(requests[i].get())) | |
116 continue; | |
117 if (IsNewRequestBetter(picked_request, requests[i].get(), comparator)) | |
118 picked_request = requests[i].get(); | |
119 } | |
120 | |
121 // Report the request queue counts. | |
122 request_count_callback_.Run(requests.size(), available_request_count); | |
123 | |
124 // If we have a best request to try next, get the request coodinator to | |
125 // start it. Otherwise return that we have no candidates. | |
126 if (picked_request != nullptr) { | |
127 picked_callback_.Run(*picked_request, cleanup_needed); | |
128 } else { | |
129 not_picked_callback_.Run(non_user_requested_tasks_remaining, | |
130 cleanup_needed); | |
131 } | |
132 | |
133 TaskComplete(); | |
134 } | |
135 | |
136 // Filter out requests that don't meet the current conditions. For instance, if | |
137 // this is a predictive request, and we are not on WiFi, it should be ignored | |
138 // this round. | |
139 bool PickRequestTask::RequestConditionsSatisfied( | |
140 const SavePageRequest* request) { | |
141 // If the user did not request the page directly, make sure we are connected | |
142 // to power and have WiFi and sufficient battery remaining before we take this | |
143 // request. | |
144 if (!device_conditions_->IsPowerConnected() && | |
145 policy_->PowerRequired(request->user_requested())) { | |
146 return false; | |
147 } | |
148 | |
149 if (device_conditions_->GetNetConnectionType() != | |
150 net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI && | |
151 policy_->UnmeteredNetworkRequired(request->user_requested())) { | |
152 return false; | |
153 } | |
154 | |
155 if (device_conditions_->GetBatteryPercentage() < | |
156 policy_->BatteryPercentageRequired(request->user_requested())) { | |
157 return false; | |
158 } | |
159 | |
160 // If the request is paused, do not consider it. | |
161 if (request->request_state() == SavePageRequest::RequestState::PAUSED) | |
162 return false; | |
163 | |
164 // If this request is not active yet, return false. | |
165 // TODO(petewil): If the only reason we return nothing to do is that we have | |
166 // inactive requests, we still want to try again later after their activation | |
167 // time elapses, we shouldn't take ourselves completely off the scheduler. | |
168 if (request->activation_time() > base::Time::Now()) | |
169 return false; | |
170 | |
171 return true; | |
172 } | |
173 | |
174 // Look at policies to decide which requests to prefer. | |
175 bool PickRequestTask::IsNewRequestBetter(const SavePageRequest* oldRequest, | |
176 const SavePageRequest* newRequest, | |
177 RequestCompareFunction comparator) { | |
178 // If there is no old request, the new one is better. | |
179 if (oldRequest == nullptr) | |
180 return true; | |
181 | |
182 // User requested pages get priority. | |
183 if (newRequest->user_requested() && !oldRequest->user_requested()) | |
184 return true; | |
185 | |
186 // Otherwise, use the comparison function for the current policy, which | |
187 // returns true if the older request is better. | |
188 return !(CALL_MEMBER_FUNCTION(this, comparator)(oldRequest, newRequest)); | |
189 } | |
190 | |
191 // Compare the results, checking request count before recency. Returns true if | |
192 // left hand side is better, false otherwise. | |
193 bool PickRequestTask::RetryCountFirstCompareFunction( | |
194 const SavePageRequest* left, | |
195 const SavePageRequest* right) { | |
196 // Check the attempt count. | |
197 int result = CompareRetryCount(left, right); | |
198 | |
199 if (result != 0) | |
200 return (result > 0); | |
201 | |
202 // If we get here, the attempt counts were the same, so check recency. | |
203 result = CompareCreationTime(left, right); | |
204 | |
205 return (result > 0); | |
206 } | |
207 | |
208 // Compare the results, checking recency before request count. Returns true if | |
209 // left hand side is better, false otherwise. | |
210 bool PickRequestTask::RecencyFirstCompareFunction( | |
211 const SavePageRequest* left, | |
212 const SavePageRequest* right) { | |
213 // Check the recency. | |
214 int result = CompareCreationTime(left, right); | |
215 | |
216 if (result != 0) | |
217 return (result > 0); | |
218 | |
219 // If we get here, the recency was the same, so check the attempt count. | |
220 result = CompareRetryCount(left, right); | |
221 | |
222 return (result > 0); | |
223 } | |
224 | |
225 // Compare left and right side, returning 1 if the left side is better | |
226 // (preferred by policy), 0 if the same, and -1 if the right side is better. | |
227 int PickRequestTask::CompareRetryCount(const SavePageRequest* left, | |
228 const SavePageRequest* right) { | |
229 // Check the attempt count. | |
230 int result = signum(left->completed_attempt_count() - | |
231 right->completed_attempt_count()); | |
232 | |
233 // Flip the direction of comparison if policy prefers fewer retries. | |
234 if (policy_->ShouldPreferUntriedRequests()) | |
235 result *= -1; | |
236 | |
237 return result; | |
238 } | |
239 | |
240 // Compare left and right side, returning 1 if the left side is better | |
241 // (preferred by policy), 0 if the same, and -1 if the right side is better. | |
242 int PickRequestTask::CompareCreationTime(const SavePageRequest* left, | |
243 const SavePageRequest* right) { | |
244 // Check the recency. | |
245 base::TimeDelta difference = left->creation_time() - right->creation_time(); | |
246 int result = signum(difference.InMilliseconds()); | |
247 | |
248 // Flip the direction of comparison if policy prefers fewer retries. | |
249 if (policy_->ShouldPreferEarlierRequests()) | |
250 result *= -1; | |
251 | |
252 return result; | |
253 } | |
254 | |
255 } // namespace offline_pages | |
OLD | NEW |