| 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 |